magus v0.4.3 is out. See what's new
¶ View generated markdown
10 min read

magus-buzz-write

Short form: 7345 bytes, 17% smaller than the full form's 8872.

Write and run Buzz, the language magusfiles, spells, and magus buzz scripts are written in. Use when writing or debugging a magusfile target, a spell, or a .buzz file, and when a one-off script is needed in a magus workspace - Buzz is already installed with the whole magus host surface (fs, http, json, yaml, template, vcs, ...), so it needs no dependency install. Also use when Buzz syntax surprises you: namespace access is a backslash, object literals use =, and magus buzz runs upstream-strict (no top-level control flow, every argument after the first must be labeled).

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 4943b90e40ec
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-buzz-write/SKILL.md
# Writing Buzz

Buzz is the language magusfiles and spells are written in, and `magus buzz` runs
it as a general-purpose scripting language with the whole magus host surface
attached. Reach for it for a
one-off script: already installed, no dependency install, same language as the
workspace's build logic.

## The smallest thing that runs

Everything is imported by bare name, work goes in a function, and you call it.

```buzz
import "std";

fun main() > void {
    std\print("hello");
}
main();
```

```sh
magus buzz hello.buzz
```

## Run it

| form | use |
| --- | --- |
| `magus buzz <file>` | run a script file |
| `magus buzz -e '<code>'` | run a snippet inline |
| `echo '<code>' \| magus buzz -` | run from stdin (a pipe or heredoc also works with no `-`) |
| `magus buzz -t <file>` | run the file's `test "..." {}` blocks and report pass/fail |
| `magus buzz --check <file>...` | parse and type-check, without running |
| `magus buzz` (a terminal, no args) | REPL, with the magusfile at cwd loaded |

## Never guess an API: ask

The stdlib is discoverable, and guessing at it is the single biggest source of
wasted turns. Look it up:

```sh
magus describe modules -o name        # every module available to a script
magus describe module json            # its methods, docs, and SIGNATURES with return types
```

Escalate deliberately:

| question | where |
| --- | --- |
| what modules exist, what a method takes and RETURNS | `magus describe module <name>` - the authority, generated from the bindings |
| how a feature works, concepts, guides, worked examples | the magus-docs-lookup skill - the documentation is written and searchable |
| what THIS workspace declares (targets, spells, projects) | the magus-query skill |

Error sets, fibers, generics, the full stdlib and sandbox behavior are all
documented; search rather than guess.

WRONG: assume `strings\toLower(s)` or `json\encode(v)` exist.
CORRECT: `magus describe module strings`, then write what it lists.

Text operations that are not case conversion are usually METHODS on the value,
not module functions: `"a.buzz".endsWith(".buzz")`, `s.len()`, `list.join(" ")`.

## Imports

Every module, including the Buzz stdlib, must be imported by BARE name.

```buzz
import "std";                 // print, assert
import "fs"; import "json";   // host modules
```

Available in `magus buzz`: the Buzz stdlib plus `archive`, `charm`, `crypto`,
`encoding`, `env`, `fmt`, `fs`, `http`, `json`, `markdown`, `magus`, `os`,
`path`, `platform`, `semver`, `strings`, `template`, `time`, `toml`, `uuid`,
`vcs`, `xml`, `yaml`.

### Calling magus from a script

`magus` is a host module like `fs` or `vcs`, so `import "magus"` is what makes the
namespace exist: without the import line, `magus\...` is `undefined: magus`. That
holds in a magusfile too. With it, ask magus about the workspace through the
namespace rather than shelling out to the binary:

```buzz
import "std"; import "magus";

fun main() > void {
    // opts.quiet captures the output instead of echoing it
    final res = magus\describe(["file", "MAGUS.md", "-o", "json"], opts: {"quiet": true});
    std\print(res.stdout);
}
main();
```

WRONG: `proc\exec("magus", args: [...], dir: ".", opts: {})` - magus warns on it.
CORRECT: `magus\cmd`, or the typed `magus\run` / `describe` / `insight` / `doctor`.

Members that need a magusfile raise [MGS1022](https://eli.gladman.cc/magus/reference/codes/magusfile/MGS1022/) naming the constraint: the ones
that declare into a workspace being loaded (`magus\project`, the provider
selections) have no script equivalent, and the ones that read a loaded workspace
(`magus\projects`, `targets`, `affected`, `graph`, `where`) are reachable through the
nested commands above.

## Two rules that cause most first-try failures

`magus buzz` runs upstream-strict by default.

1. **Control flow is not allowed at the top level.** Put them in a function and call
   it.
   (`--embedded` relaxes this.)
2. **Every argument after the first must be labeled.**

```buzz
// WRONG (strict mode): argument 2 must be labeled
std\assert(x == 1, "checked x");
template\render(tpl, {"name": "world"});

// CORRECT
std\assert(x == 1, message: "checked x");
template\render(tpl, data: {"name": "world"});
```

## Syntax that differs from what you expect

| thing | Buzz |
| --- | --- |
| namespace access | `fs\list(".")` - a BACKSLASH, not a dot |
| member access | `obj.field`, `"s".len()` - a dot |
| object literal | `Point{ x = 1 }` - `=`, not `:` |
| map literal | `{"key": value}` - `:`, like JSON |
| typed binding | `final n: int = 1;` - type AFTER the name |
| immutable / mutable | `final` / `var`; collections need `mut [1, 2]` to be mutated |
| optional | `int?`, unwrap with `??`, `?.`, or `!` |
| conditional value | `if`/`else` only; there is NO `a ? b : c` |
| string offsets | BYTES, not characters: `s.sub(0, len: 8)` cuts a multi-byte rune in half |
| a path from `fs\glob` | a `Path`, not a `str`; pass `p.value` to anything taking a path |
| errors | `fun f() > int !> str` declares what it throws; `try`/`catch`, or `expr catch fallback` inline |

Reserved words that cannot be used as binding names (var/fun/param/field/...):
`out`, `from`, `match`, `pat`, `fib`, `rg`, `obj`, `ud`, `zdef`, `typeof`, `type`,
`protocol`, `static`, `extern`, `double`, `any`, `Function`, `int`, `str`, `bool`,
`void`. `test` is NOT
reserved. Prefix or rename only the words above.

A separate hazard: naming a local after a module or a builtin (`map`, `len`, a
module name) SHADOWS it - watch for a confusing `null is not callable`. Rename
the local.

A raw string is backticks, and it does NOT interpolate `{...}`:

```buzz
template\render(`Hello {{name}}!`, data: {"name": "world"});
```

## A worked script

```buzz
import "std"; import "fs"; import "json"; import "strings";

fun main() > void {
    var count = 0;
    foreach (f in fs\list(".")) {
        if (f.endsWith(".buzz")) { count = count + 1; }
    }
    std\print(json\stringify({"buzz_files": count, "slug": strings\kebabCase("Hello World")}));
}
main();
```

## Test what you write

Buzz has test blocks, and `magus buzz -t` is the runner. Use them for any script
worth keeping, and for spell files.

```buzz
import "std"; import "strings";

fun slug(s: str) > str { return strings\kebabCase(s); }

test "slug hyphenates" {
    std\assert(slug("Hello World") == "hello-world", message: "slug");
}
```

```sh
magus buzz -t script.buzz     # ok/fail per block, then a summary line
```

Do not test `magusfile.buzz` itself. Wanting a test
for a magusfile is the signal to move that logic into a spell or a sibling
module and test it there instead.

A module a magusfile imports is tested with the same runner, plus one flag:
`magus buzz -t --embedded render.buzz` (a magusfile's
imports parse embedded, not strict).

## Where Buzz code belongs

- **A one-off** - a standalone `.buzz` file run with `magus buzz`.
- **Work the workspace repeats** - a target in `magusfile.buzz`. Targets take
  `(ctx: magus\Context, args: [str])` and receive `magus run <target> -- <args>`
  as that `args` list.
- **A tool adapter** - a spell, so every project of that type gets the ops.

Prefer a target over a script for anything that will be run more than once.

Reviewing existing Buzz code rather than writing new code: use magus-buzz-review.
magus agent install --tar | tar -xO -f - magus-buzz-write-full/SKILL.md
# Writing Buzz

Buzz is the language magusfiles and spells are written in, and `magus buzz` runs
it as a general-purpose scripting language with the whole magus host surface
attached. In a magus workspace it is the right reach for a one-off script -
scanning files, reshaping JSON/YAML/TOML, templating, hitting HTTP - because it
is already installed, it needs no dependency install or virtualenv, and it is
the same language the workspace's own build logic is written in.

## The smallest thing that runs

Everything is imported by bare name, work goes in a function, and you call it.
That skeleton plus `magus describe module` covers most scripts:

```buzz
import "std";

fun main() > void {
    std\print("hello");
}
main();
```

```sh
magus buzz hello.buzz
```

## Run it

| form | use |
| --- | --- |
| `magus buzz <file>` | run a script file |
| `magus buzz -e '<code>'` | run a snippet inline |
| `echo '<code>' \| magus buzz -` | run from stdin (a pipe or heredoc also works with no `-`) |
| `magus buzz -t <file>` | run the file's `test "..." {}` blocks and report pass/fail |
| `magus buzz --check <file>...` | parse and type-check, without running: every diagnostic rather than the first, several paths at once. Running a script whose job is a side effect is not a check of it |
| `magus buzz` (a terminal, no args) | REPL, with the magusfile at cwd loaded |

## Never guess an API: ask

The stdlib is discoverable, and guessing at it is the single biggest source of
wasted turns. `strings` is case-conversion helpers, NOT Go's strings; JSON is
`json\stringify` / `json\parse`, not `encode` / `decode`. Look it up:

```sh
magus describe modules -o name        # every module available to a script
magus describe module json            # its methods, docs, and SIGNATURES with return types
```

This file teaches the fundamentals and nothing more. Escalate deliberately:

| question | where |
| --- | --- |
| what modules exist, what a method takes and RETURNS | `magus describe module <name>` - the authority, generated from the bindings |
| how a feature works, concepts, guides, worked examples | the magus-docs-lookup skill - the documentation is written and searchable |
| what THIS workspace declares (targets, spells, projects) | the magus-query skill |

Anything of substance - error sets, fibers, generics, the full stdlib, sandbox
behavior - is documented; search it rather than guessing from this page.

WRONG: assume `strings\toLower(s)` or `json\encode(v)` exist.
CORRECT: `magus describe module strings`, then write what it lists.

Text operations that are not case conversion are usually METHODS on the value,
not module functions: `"a.buzz".endsWith(".buzz")`, `s.len()`, `list.join(" ")`.

## Imports

Every module, including the Buzz stdlib, must be imported by BARE name. There is
no `magus:` or `buzz:` prefix on the host modules.

```buzz
import "std";                 // print, assert
import "fs"; import "json";   // host modules
```

Available in `magus buzz`: the Buzz stdlib plus `archive`, `charm`, `crypto`,
`encoding`, `env`, `fmt`, `fs`, `http`, `json`, `markdown`, `magus`, `os`,
`path`, `platform`, `semver`, `strings`, `template`, `time`, `toml`, `uuid`,
`vcs`, `xml`, `yaml`.

### Calling magus from a script

`magus` is a host module like `fs` or `vcs`, so `import "magus"` is what makes the
namespace exist: without the import line, `magus\...` is `undefined: magus`. That
holds in a magusfile too. With it, ask magus about the workspace through the
namespace rather than shelling out to the binary - it is in-process,
version-pinned, and has no arg-quoting to get wrong:

```buzz
import "std"; import "magus";

fun main() > void {
    // opts.quiet captures the output instead of echoing it
    final res = magus\describe(["file", "MAGUS.md", "-o", "json"], opts: {"quiet": true});
    std\print(res.stdout);
}
main();
```

WRONG: `proc\exec("magus", args: [...], dir: ".", opts: {})` - magus warns on it.
CORRECT: `magus\cmd`, or the typed `magus\run` / `describe` / `insight` / `doctor`.

Members that need a magusfile raise [MGS1022](https://eli.gladman.cc/magus/reference/codes/magusfile/MGS1022/) naming the constraint: the ones
that declare into a workspace being loaded (`magus\project`, the provider
selections) have no script equivalent, and the ones that read a loaded workspace
(`magus\projects`, `targets`, `affected`, `graph`, `where`) are reachable through the
nested commands above.

## Two rules that cause most first-try failures

`magus buzz` runs upstream-strict by default.

1. **Control flow is not allowed at the top level.** Declarations and expression
   statements are; `if`/`while`/`for` are not. Put them in a function and call
   it. `magus buzz --embedded` relaxes this if you want a throwaway snippet.
2. **Every argument after the first must be labeled.**

```buzz
// WRONG (strict mode): argument 2 must be labeled
std\assert(x == 1, "checked x");
template\render(tpl, {"name": "world"});

// CORRECT
std\assert(x == 1, message: "checked x");
template\render(tpl, data: {"name": "world"});
```

## Syntax that differs from what you expect

| thing | Buzz |
| --- | --- |
| namespace access | `fs\list(".")` - a BACKSLASH, not a dot |
| member access | `obj.field`, `"s".len()` - a dot |
| object literal | `Point{ x = 1 }` - `=`, not `:` |
| map literal | `{"key": value}` - `:`, like JSON |
| typed binding | `final n: int = 1;` - type AFTER the name |
| immutable / mutable | `final` / `var`; collections need `mut [1, 2]` to be mutated |
| optional | `int?`, unwrap with `??`, `?.`, or `!` |
| conditional value | `if`/`else` only; there is NO `a ? b : c` |
| string offsets | BYTES, not characters: `s.sub(0, len: 8)` cuts a multi-byte rune in half |
| a path from `fs\glob` | a `Path`, not a `str`; pass `p.value` to anything taking a path |
| errors | `fun f() > int !> str` declares what it throws; `try`/`catch`, or `expr catch fallback` inline |

Reserved words that cannot be used as binding names (var/fun/param/field/...):
`out`, `from`, `match`, `pat`, `fib`, `rg`, `obj`, `ud`, `zdef`, `typeof`, `type`,
`protocol`, `static`, `extern`, `double`, `any`, `Function`, `int`, `str`, `bool`,
`void` - upstream Buzz's list, kept for parity. `test` is NOT
reserved - every magus target set defines `export fun test(...)`,
the canonical test target, so reserving it would break the CLI's own
model. Prefix or rename only the words above.

A separate hazard: naming a local after a module or a builtin (`map`, `len`, a
module name) SHADOWS it rather than failing to parse, so a later
call through that name hits a non-callable value and dies with a
confusing `null is not callable`. Rename
the local.

A raw string is backticks, and it does NOT interpolate `{...}` - use it for
Mustache templates, regexes, and JSON blobs:

```buzz
template\render(`Hello {{name}}!`, data: {"name": "world"});
```

## A worked script

```buzz
import "std"; import "fs"; import "json"; import "strings";

fun main() > void {
    var count = 0;
    foreach (f in fs\list(".")) {
        if (f.endsWith(".buzz")) { count = count + 1; }
    }
    std\print(json\stringify({"buzz_files": count, "slug": strings\kebabCase("Hello World")}));
}
main();
```

## Test what you write

Buzz has test blocks, and `magus buzz -t` is the runner. Use them for any script
worth keeping, and for spell files (which are Buzz too).

```buzz
import "std"; import "strings";

fun slug(s: str) > str { return strings\kebabCase(s); }

test "slug hyphenates" {
    std\assert(slug("Hello World") == "hello-world", message: "slug");
}
```

```sh
magus buzz -t script.buzz     # ok/fail per block, then a summary line
```

Do not test `magusfile.buzz` itself. It is declarative configuration,
so a test of it tests your configuration, not your logic. Wanting a test
for a magusfile is the signal to move that logic into a spell or a sibling
module and test it there instead.

A module a magusfile imports is tested with the same runner, plus one flag:
`magus buzz -t --embedded render.buzz` - a magusfile's own imports
always parse embedded, not strict, so testing under the strict default would
judge the module by a mode it never actually runs in.

## Where Buzz code belongs

- **A one-off** - a standalone `.buzz` file run with `magus buzz`. Nothing is
  registered; it is a script.
- **Work the workspace repeats** - a target in `magusfile.buzz`, so it gets
  caching, sandboxing, and affected tracking. Targets take
  `(ctx: magus\Context, args: [str])` and receive `magus run <target> -- <args>`
  as that `args` list.
- **A tool adapter** - a spell, so every project of that type gets the ops.

Prefer a target over a script for anything that will be run more than once: a
script re-runs from scratch every time, a target replays from cache.

Reviewing existing Buzz code rather than writing new code: use magus-buzz-review.
generatedinternal/agent/skills/magus-buzz-write/SKILL.mdagentsskillsmagus-buzz-write
Last updated (c5971189)
Earlier changes on this page (7)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Charm

An execution modifier attached with : (lint:rw) that changes how a target runs, not which one; the built-in rw flips a check-only target to mutate in place, and ci always strips it. See charms.

Module

A magus stdlib namespace a magusfile imports for host capabilities: filesystem, exec, vcs, and more. See the module reference.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Affected

The set of projects touched by a change; magus affected <target> runs a target only over them. See affected.

Sandbox

The restricted filesystem and environment a target runs in, so builds stay reproducible and side-effect-free. See sandbox.

One-off

A single magus invocation that runs a target and exits, using a per-process pool; the opposite of the long-lived server or a service. See server.

MAGUS.md

The committed routing index at a workspace root, regenerated from the knowledge graph: it lists every node and points at the exact query for a given question, so it is the entry point an agent reads first. See knowledge.

Insight

The reports magus derives over the graph and history (hotspots, affinity, ownership, trend, volatility, unreferenced). See insight.

Job

The unit of delegated work, and one row of the job store: what an orchestrating agent handed out, with its goal, the checkpoint it was cut against, the paths it may write or must not touch, and the one check it runs. A job's holder is either a session, for work an orchestrator handed out, or the server, for its own maintenance. The store records; the agent guard is what reads those facts back when grading a write. See doctrine.

A job is not a run. magus run build web is a run, and no job exists for it. A job causes runs: its check executes as one, and a server job records the invocation of its last one. Jobs are listed with magus ls jobs and in the console's Jobs view; runs are listed in the Runs view.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate.

Pane

A split within a tab. Splitting divides the focused pane along its longer side, so the same action tiles side-by-side on a desktop and stacks on a phone; a tab with no split is a single pane. Drag the divider to re-weight the split. See reference/console.

Conventions

Placeholders

Angle brackets mark a value you replace with your own - never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.