magus v0.3.0 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
2 min read

bash

The bash spell lints shell scripts. Its single op finds every .sh/.bash file and runs shellcheck over the set.

Runtime name: bash (source spells/bash/)

Version probe: none

Passing arguments to ops

Every op is invoked as bash["<op>"](ctx, opts?). The first argument is the target's context, which is what carries the execution environment; the optional options map shapes the command itself:

Key Type Description Source
args [str] Extra arguments appended to the resolved command. Omit it and a bare bash["<op>"]() forwards magus run <target> -- <extra> to the tool automatically; pass it to set the arguments explicitly, which replaces that passthrough. source
stdin str Data written to the command's standard input. source

Working directory and environment are NOT options: they ride the context, as bash["<op>"](ctx.withCwd("sub")) and bash["<op>"](ctx.withEnv({"CGO_ENABLED": "0"})). Only the context reaches the cache key, so an option-table cwd or env would change what the tool did while the key said otherwise - passing either as an option is an error.

Charms (the :charm suffix, e.g. magus run test:rw) are orthogonal: they patch the base argv, while these options add to it. See Charms.

shellcheck

One shellcheck invocation over every shell source: find feeds xargs with NUL separators, and -r skips running shellcheck on an empty set. node_modules and .claude/worktrees are pruned inside the find too, not just declared above: a declared ignore dir shapes what magus treats as sources, but this op's find is its own walk. Without the prunes, third-party shell files or stale agent worktrees make the current project fail lint for code it does not own. TODO: this prune is a stopgap and duplicates knowledge the engine already has. An op handler is called ONCE with a null Target and reduced to static {cmd, args} (see recordOp in internal/spellruntime/resolve.go), so it cannot expand a file list itself - the sh -c find is what defers the walk to execution time in the project dir. The fix is engine-side: let an op declare a sources placeholder that the runner expands per project from expandSources(..., IgnoreDirs), the same walk that builds cache keys. Then every spell inherits the declared ignore dirs instead of hardcoding names here.

Command: sh -c find . \( -name node_modules -o -path './.claude/worktrees' \) -prune -o \( -name '*.sh' -o -name '*.bash' \) -print0 | xargs -0 -r shellcheck

Example

// shellcheck lints every .sh/.bash script found under the project.
import "magus";
import "magus/spell/bash";

magus\project({ "spells": [bash] });

export fun lint(ctx: magus\Context, args: [str]) > void {
    bash["shellcheck"](ctx);
}
auto-generatedbashspellshellshellchecklinttools
Last updated (a103255f)
Earlier changes on this page (6)

Full history ↗ · Blame source ↗

Glossary

Project

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

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.

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.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. See wards.

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.

Engine

The interpreter a magusfile runs on; magus embeds the Buzz engine. See engines.

Cache

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

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

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.