magus-run
Short form: 8517 bytes, 33% smaller than the full form's 12722.
Run builds, tests, lints, and codegen through magus targets. Use BEFORE typing go test, go build, npm test, npx, eslint, prettier, pytest, tsc, cargo, or any other raw language tool in a repo with magusfile.buzz at the root - a target covers the work, and the raw tool bypasses the cache, the sandbox, and affected tracking. Also use when a magus target fails and you need its captured output, and for the final pre-commit gate (magus affected ci).
Install it, rather than copying from this page:
magus agent install .claude/skills # writes both forms below
An installed copy carries a provenance stamp, so magus doctor can tell you when a magus upgrade has made it stale. Text copied from this page carries none.
What an installed copy carries
magus agent install writes this frontmatter above the body. magus doctor reads it to report whether your installed skills are current.
| field | value |
|---|---|
license |
GPL-3.0-or-later |
compatibility |
any-agent |
source |
magus |
agent-skill-version |
88 |
knowledge-schema-version |
15 |
skill-content |
03d4e6f94f87 |
skill-variant |
full |
The skill-content digest covers this skill alone, and both forms below report it: they go stale together, never one silently, and a change to another skill does not move it.
The two forms
Both are hand-authored from one source body. The short form is the always-loaded primary - the enumeration dropped, the judgment kept, for the most capable readers rather than the least. The full form is its <name>-full twin, loaded by name when a reader wants the rationale. The bar above shows how much shorter the primary is; switch between them here to see exactly what it gave up. See Skills for how to choose.
magus agent install --tar | tar -xO -f - magus-run/SKILL.md
# Running work through magus
magus is the task orchestrator: its unit of work is the target, and targets
declare their inputs, outputs, and sandbox; magus caches results and computes
what a change affects. Invoking a
raw language tool directly bypasses all of that, so the cache goes
stale and `magus affected` can no longer vouch for your change.
## Which project a command hits
magus is CWD-relative; never assume the root. Scope explicitly: name the
project (`magus run test web`), or let `magus affected` compute it from the diff.
`magus where <name>` resolves a name. MCP tools ignore the CWD.
`--root <path>`, or `-C` after make's idiom, sets where that walk STARTS. Its argument
is a plain path and nothing more: any directory, one file in it or none, nested or not.
It is not a workspace and not a checkout, and calling it either is how a reader
concludes it must be one.
What the walk FINDS is separate: the nearest `magus.yaml` wins, and absent any, the
outermost CONTIGUOUS run of `magusfiles/`, `magusfile.buzz` or `go.mod`. It can resolve
somewhere other than the path you passed, and it can resolve nothing at all.
Running a binary by absolute path does NOT set its working directory: it
still walks up from your cwd. Pass `--root` when you mean elsewhere.
## Rules
1. Prefer the MCP tools.
At session start, or after an MCP call fails, check `magus status --probe=mcp`.
If it is unavailable, continue with the CLI fallback below. Hosts manage
their own MCP connection; do not manually start a server for an agent.
- `magus_run_target` {target, projects} - run named projects.
- `magus_run_affected` {target, base} - run ONLY the projects a VCS change
touched.
If the MCP tool errors or no server is connected, run the CLI equivalent
(`magus run <target>` / `magus affected <target>`). Do not stop, and do not
drop to a raw language tool. When you shell out, silence it (`-s`).
2. Verification is `ci`'s job, not a sequence you compose. `ci` is the one
target name magus enforces.
Run `magus run ci <project>` for the project you are working in, and
`magus affected ci` as the final gate once the change is done. Hand-running lint,
format, and test one at a time re-derives an order the magusfile already
owns, and the step you forget fails silently by omission. A gate you ADDED
this session is not proven by its green: make it FAIL once - break the input
it checks, watch it go red, restore - before you count its pass as
verification. A check wired to the wrong path passes exactly the same way.
Verify in place;
never `git stash`/`reset` first (it destroys a concurrent agent's untracked
work, and the tree is already what you want to verify).
3. Reach for an individual target only to iterate on a failure `ci` named:
rerunning one failing target is cheaper than the pipeline while you fix it,
and `ci` afterwards proves the change. `magus describe targets` lists every
target (`-o name` for bare names). Ask the
workspace rather than reading `MAGUS.md`.
4. Do not run raw language tools (`go test`, `eslint`, `pytest`, `tsc`, ...)
for work a target covers. If no target covers it, say so rather than silently
going around magus.
5. Rewriting DEPENDENCY state (`go get`, `go mod tidy`, `pnpm add`, `cargo
update`, `uv lock`, `pip-compile`) needs the `update` charm: `magus run
<target>:update <project>`. It is reserved and deliberately not part of `rw` -
`rw` covers output reproducible from a clean checkout, `update` covers state that
depends on what a registry serves today. Applying a lockfile (`npm ci`,
`pnpm install --frozen-lockfile`) re-resolves nothing and needs no charm.
## Command patterns
```sh
magus run ci web # verify one project: the composed pipeline, in order
magus affected ci # the final gate: everything the diff reaches
magus run test web # iterate on the one failing target ci named
magus affected test # only projects affected by the VCS diff
```
WRONG: `go test ./...` after editing Go in a magus workspace; also wrong is
hand-sequencing `magus run lint`, `format`, `test` to check your own work.
CORRECT: `magus run ci <project>` while working, `magus affected ci` once the
change is done, and a single narrower target only to iterate on a failure.
## Output control: silence runs, read structure
- `-s` / `--silent`: the default for every CLI run. A pass prints a
result line plus an output ref; a failure adds a bounded tail.
DROP it when the question is what RAN versus what replayed: the per-target
timings and the `(cached, 320ms)` / `(ran, 5m28s)` verdict only print without
it.
- `-q` / `--quiet`: looser - drops progress, keeps errors and the failing
project's full output.
- `-o <fmt>`: `text|json|yaml|jsonl|name|template=<go-template>`.
### Never pipe OR redirect a magus command
**Do NOT pipe magus output through `grep`, `head`, `tail`, `awk`, `sed`, `cut`,
or `wc`, and do NOT redirect it with `> file`, `>> file`, or `2>&1`.** Both are
denied by the guard. A pipe also REPLACES the exit status with the last stage's,
so `magus affected ci | tail` reports tail's success and a failing gate reads as
exit 0. You never need to capture the output: every run persists its full log,
and a failure prints that path along with the output ref. Use the flag
instead: `-o template='{{.Field}}'` for a field, `-o name` for bare identifiers,
`-o json` to parse, `-s` to quieten. `jq` over `-o json` is fine - that is a
contract, not scraped text.
**Piping magus INTO magus is supported and encouraged.** The composition seam is
`--stdin`. (`--tee <file>` exists but is not a composition seam: it mirrors
STRUCTURED output only - `-o json|yaml|jsonl|template` - never console text.)
```sh
magus watch | magus affected --stdin # changed paths -> affected set
magus affected ci --plan | magus run --stdin # plan -> run its shards
```
Rule of thumb: a pipe whose right-hand side is magus, or `jq` over `-o json`, is
composition. A pipe whose right-hand side is a text filter is a missing `-o`.
A backgrounded run's capture is magus output too, so the same rule reaches it:
grepping the task file your host wrote is denied, and it drops the `output:` and
`inspect:` lines that sit under the `cause:` you matched. Background it as `-o
jsonl --tee <file>` and the capture is a contract you can `jq`; otherwise read
the file whole.
## When you need finer granularity
Every top-level target composes spell ops (tool-native operations). address one directly
with the spell-qualified form:
```sh
magus run go::go-test # one op from the go spell
magus run buf::buf-lint
```
List the ops behind a target with `magus describe target <name>`.
Re-run the top-level target before you call the work done.
## When a target fails
Each target's result line mints an output reference id (`out1a2b3c`).
1. Fetch the exact captured output: `magus_output` {ref} over MCP, or
`magus query output out1a2b3c` on the CLI. Never re-run just to see the error again.
2. With no ref in hand, the ref is in the run that minted it: every `magus run`
prints one per target, and `magus session` lists recent invocations with the
targets they ran.
3. `magus doctor` validates the workspace itself (config, cache, tool
availability, cycles).
## When another magus process holds the project
A run whose project lock or machine budget another magus invocation holds exits
75 at once, naming the holder. Do not write `sleep`/`ps`/`pgrep` polling loops.
`magus status --watch=15s` reads that same lock state continuously: holder PID,
command, directory, and age. Re-run once the lock releases. A long-running
holder is not evidence of a hang by itself.
The guard denies `pgrep`, `pidof`, and `ps` for this reason.
```sh
magus status --watch=15s
```
If the lock has crossed its stale warning threshold, report the exact holder
and inspect its captured target output. Do not send signals based on a guessed
PID or a fixed elapsed delay; process termination needs an explicit, verified
owner policy.
The same status view lists shared services, including each service's lifecycle
state and current dependent count. Use it to distinguish an idle retained
service from active shared work before deciding how to proceed.
## Fetching current behavior
Trust `magus describe targets`, `magus describe target <name>` and
`magus <verb> -h` over anything remembered, and over `MAGUS.md`.
magus agent install --tar | tar -xO -f - magus-run-full/SKILL.md
# Running work through magus
magus is the task orchestrator: its unit of work is the target, and targets
declare their inputs, outputs, and sandbox; magus caches results and computes
what a change affects. Invoking a
raw language tool directly bypasses all of that, so the cache goes stale, declared
outputs drift, and `magus affected` can no longer vouch for your change.
## Which project a command hits
magus is CWD-relative: a bare `magus run`/`ls`/`describe` acts on the project holding
your current directory, or the whole workspace from the root. Do not assume the root.
Scope explicitly so a command means the same anywhere: name the project (`magus run
test web`), or let `magus affected` compute the set from the diff. `magus where <name>`
resolves a name to its path; over MCP, `magus_where`/`magus_describe` ignore the CWD.
`--root <path>`, or `-C` after make's idiom, sets where that walk STARTS. Its argument
is a plain path and nothing more: any directory, one file in it or none, nested or not.
It is not a workspace and not a checkout, and calling it either is how a reader
concludes it must be one.
What the walk FINDS is separate: the nearest `magus.yaml` wins, and absent any, the
outermost CONTIGUOUS run of `magusfiles/`, `magusfile.buzz` or `go.mod`. It can resolve
somewhere other than the path you passed, and it can resolve nothing at all.
Two consequences worth knowing before you debug one. Running a binary by
absolute path does NOT set its working directory, so `/elsewhere/magus run build .`
still walks up from YOUR cwd and can act on a tree you never named. And nearest-wins
means a directory holding its own `magus.yaml` inside a larger checkout resolves to
itself, so where a command lands is a question about markers on disk, never about the
VCS.
## Rules
1. Prefer the MCP tools; they return structured content with nothing to silence.
At session start, or after an MCP call fails, check `magus status --probe=mcp`.
If it is unavailable, continue with the CLI fallback below. Hosts manage
their own MCP connection; do not manually start a server for an agent.
Do not make the connection a prerequisite for completing the work.
- `magus_run_target` {target, projects} - run named projects (or the cwd
project). Use when you know which projects to run.
- `magus_run_affected` {target, base} - run ONLY the projects a VCS change
touched; magus computes the set. Use for a pre-commit/CI gate.
If the MCP tool errors or no server is connected, run the CLI equivalent
(`magus run <target>` / `magus affected <target>`). Do not stop, and do not
drop to a raw language tool. When you shell out, silence it (`-s`) so a
passing run costs a few lines, not a scroll of progress.
2. Verification is `ci`'s job, not a sequence you compose. `ci` is the one
target name magus enforces: the command that composes the pipeline
(typically generate, lint, build, test) in the order the magusfile declares.
Run `magus run ci <project>` for the project you are working in, and
`magus affected ci` as the final gate once the change is done - it runs the
full pipeline over every project your change reaches, which is how you learn
about ramifications in projects you never touched. Hand-running lint,
format, and test one at a time re-derives an order the magusfile already
owns, and the step you forget fails silently by omission. A gate you ADDED
this session is not proven by its green: make it FAIL once - break the input
it checks, watch it go red, restore - before you count its pass as
verification. A check wired to the wrong path passes exactly the same way.
Verify in place;
never `git stash`/`reset` first (data-loss-prone and pointless - the tree is
already what you want to verify).
3. Reach for an individual target only to iterate on a failure `ci` named:
rerunning one failing target is cheaper than the pipeline while you fix it,
and `ci` afterwards proves the change. `magus describe targets` lists every
target (`-o name` for bare names) and classifies each as canonical, spell,
or custom; `magus_describe` (kind=targets) is the MCP equivalent. Ask the
workspace rather than reading `MAGUS.md`: that file is a generated index
for humans, true only as of its last regeneration.
4. Do not run raw language tools (`go test`, `eslint`, `pytest`, `tsc`, ...)
for work a target covers. If no target covers it, say so rather than silently
going around magus.
5. Rewriting DEPENDENCY state (`go get`, `go mod tidy`, `pnpm add`, `cargo
update`, `uv lock`, `pip-compile`) needs the `update` charm: `magus run
<target>:update <project>`, so the rewrite happens inside magus, cached and
visible to affected tracking. It is reserved and deliberately not part of `rw` -
`rw` covers output reproducible from a clean checkout, `update` covers state that
depends on what a registry serves today. `ci` strips both, so a gate verifies
the committed lockfile rather than refreshing it. Applying a lockfile (`npm ci`,
`pnpm install --frozen-lockfile`) re-resolves nothing and needs no charm.
## Command patterns
```sh
magus run ci web # verify one project: the composed pipeline, in order
magus affected ci # the final gate: everything the diff reaches
magus run test web # iterate on the one failing target ci named
magus affected test # only projects affected by the VCS diff
```
MCP equivalents: `magus_run_target` {target, projects, dry_run} and
`magus_run_affected` {target, base, dry_run}. Use `magus_where` to resolve a
fuzzy project name first.
WRONG: `go test ./...` after editing Go in a magus workspace; also wrong is
hand-sequencing `magus run lint`, `format`, `test` to check your own work.
CORRECT: `magus run ci <project>` while working, `magus affected ci` once the
change is done, and a single narrower target only to iterate on a failure.
## Output control: silence runs, read structure
You are a machine reader; no news is good news. Shape the output instead of
truncating it after the fact:
- `-s` / `--silent`: the default for every CLI run. Progress is dropped; a pass
is a few lines (result line + output ref), a failure keeps a bounded tail of
the failing project plus the ref to fetch the rest.
DROP it when the question is what RAN versus what replayed: the per-target
timings and the `(cached, 320ms)` / `(ran, 5m28s)` verdict only print without
it. Reaching for shell `time` around a silent run measures the wall clock magus
already reported and hides which targets were cache hits.
- `-q` / `--quiet`: looser - drops progress, keeps errors and the failing
project's full output.
- `-o <fmt>`: `text|json|yaml|jsonl|name|template=<go-template>`. Ask for the
shape you want. `json`/`yaml` to parse, `jsonl` to stream records, `name` for
bare identifiers one per line, `template=` to project exactly the fields you
need and nothing else.
### Never pipe OR redirect a magus command
**Do NOT pipe magus output through `grep`, `head`, `tail`, `awk`, `sed`, `cut`,
or `wc`, and do NOT redirect it with `> file`, `>> file`, or `2>&1`.** Both are
denied by the guard. A pipe also REPLACES the exit status with the last stage's,
so `magus affected ci | tail` reports tail's success and a failing gate reads as
exit 0. You never need to capture the output: every run persists its full log,
and a failure prints that path along with the output ref. Every magus command already has an output contract, so filtering its
text after the fact is always the wrong tool. It is not a style preference; it
actively breaks things:
- It is lossy in the direction that matters. A truncating filter drops the
failing tail and the output ref, which is the only part you needed.
- It hides progress on a long run, so a working command looks hung and gets
killed.
- It scrapes a human-facing layout that is free to change, instead of reading a
stable contract that is not.
- `grep` cannot see structure. A field you matched textually may belong to a
different record entirely.
Replace the filter with the flag that already does it:
| Instead of | Use |
|---|---|
| `\| grep <field>` | `-o template='{{.Field}}'` |
| `\| grep -c .` (counting) | `-o json` and read the count, or the verb's own summary |
| `\| head` / `\| tail` (quieting) | `-s` / `--silent` |
| `\| grep -i error` | `-s` (a pass prints almost nothing; failures already surface) |
| `\| awk '{print $1}'` | `-o name` |
| `\| jq` after `-o text` | `-o json` first, then `jq` |
`jq` on `-o json` is fine: that is consuming a contract, not scraping text. The
prohibition is on text filters standing in for an output format.
**Piping magus INTO magus is supported and encouraged.** The composition seam is
`--stdin`. (`--tee <file>` exists but is not a composition seam: it mirrors
STRUCTURED output only - `-o json|yaml|jsonl|template` - never console text.) These are contracts on both
ends, so they are the opposite of the antipattern above:
```sh
magus watch | magus affected --stdin # changed paths -> affected set
magus affected ci --plan | magus run --stdin # plan -> run its shards
```
Rule of thumb: a pipe whose right-hand side is magus, or `jq` over `-o json`, is
composition. A pipe whose right-hand side is a text filter is a missing `-o`.
A backgrounded run's capture is magus output too, so the same rule reaches it:
grepping the task file your host wrote is denied, and it drops the `output:` and
`inspect:` lines that sit under the `cause:` you matched. Background it as `-o
jsonl --tee <file>` and the capture is a contract you can `jq`; otherwise read
the file whole.
WRONG: `magus run test | head -50` (drops the failing tail that matters).
WRONG: `magus query "kind=target" -o name | grep -c .` (use the JSON count).
CORRECT: `magus run test -s`, then fetch the printed ref for full detail.
The silent run plus ref-fetch IS the low-token failure loop: never re-run a
target just to see its error again.
## When you need finer granularity
Every top-level target composes spell ops (tool-native operations). When you
genuinely need one op - a single formatter, one linter - address one directly
with the spell-qualified form:
```sh
magus run go::go-test # one op from the go spell
magus run buf::buf-lint
```
List the ops behind a target with `magus describe target <name>`: it prints the
fully-evaluated dispatch plan per project (sources, outputs, spells, policy).
Re-run the top-level target before you call the work done: ci runs the full
composition, so the full composition is what has to pass.
## When a target fails
Each target's result line mints an output reference id (`out1a2b3c`).
1. Fetch the exact captured output: `magus_output` {ref} over MCP, or
`magus query output out1a2b3c` on the CLI. Do this instead of re-running the
target to see the error again.
2. With no ref in hand, the ref is in the run that minted it: every `magus run`
prints one per target, and `magus session` lists recent invocations with the
targets they ran. There is no tool that fetches "the latest log for a
project", because a second door onto the same bytes only makes an agent holding a
ref pick between two.
3. `magus doctor` validates the workspace itself (config, cache, tool
availability, cycles) when failures look environmental rather than caused by
your change.
## When another magus process holds the project
A run whose project lock or machine budget another magus invocation holds exits
75 at once, naming the holder. Do not write `sleep`/`ps`/`pgrep` polling loops.
`magus status --watch=15s` reads that same lock state continuously: holder PID,
command, directory, and age. Re-run once the lock releases. A long-running
holder is not evidence of a hang by itself.
The guard denies `pgrep`, `pidof`, and `ps` for this reason.
```sh
magus status --watch=15s
```
If the lock has crossed its stale warning threshold, report the exact holder
and inspect its captured target output. Do not send signals based on a guessed
PID or a fixed elapsed delay; process termination needs an explicit, verified
owner policy.
The same status view lists shared services, including each service's lifecycle
state and current dependent count. Use it to distinguish an idle retained
service from active shared work before deciding how to proceed.
## Fetching current behavior
Flags and target sets differ per workspace and magus version. Trust
`magus describe targets`, `magus describe target <name>`, and `magus <verb> -h`
over anything remembered - and over `MAGUS.md`, which is generated output that
lags the tree between regenerations.