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

typescript

The typescript spell wires a TypeScript project's tooling into a magusfile, forking each tool through the project package manager (pnpm exec). Each check is its own op; compose them into your own targets, such as lint over tsc and eslint.

Runtime name: typescript (source spells/typescript/)

Version probe (node): node --version

Version probe (pnpm): pnpm --version

Version probe (tsc): pnpm exec tsc --version

Opaque: yes (its outputs are not enumerable, so magus treats the whole workspace as the cache input).

Passing arguments to ops

Every op is invoked as typescript["<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, replacing any trailing defaults the op declares (go-test's ./...), so passing args also states the scope. Omit it and a bare typescript["<op>"]() keeps the defaults and forwards magus run <target> -- <extra> to the tool automatically; pass it to set the arguments explicitly, which replaces that passthrough. To keep the passthrough too, append the target's own args parameter: {"args": ["-race", "./..."] + args}. source
stdin str Data written to the command's standard input. source

Working directory and environment are NOT options: they ride the context, as typescript["<op>"](ctx.withCwd("sub")) and typescript["<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.

biome-check

biome-check is biome's lint/analyze pass (eslint's role, if your project chose Biome over eslint+prettier - the magusfile decides which composes into lint, not this spell). --write and --reporter=github verified against the current Biome CLI docs (biomejs.dev/reference/cli).

Command: pnpm exec biome check .

gha

Inserts --reporter=github.

JSON Patch
[
  {
    "op": "add",
    "path": "/3",
    "value": "--reporter=github"
  }
]

rw

Inserts --write.

JSON Patch
[
  {
    "op": "add",
    "path": "/3",
    "value": "--write"
  }
]

Example

// biome-check lints the project through Biome (pnpm exec biome check), the
// spell's alternative to eslint - the magusfile picks one, not both.
import "magus";
import "magus/spell/typescript";

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

export fun lint(ctx: magus\Context, args: [str]) > void {
    typescript["biome-check"](ctx);
}

biome-format

biome-format is biome's formatter (prettier's role). Unlike prettier/ruffFormat, biome format has no --check flag to drop: it is read-only by default (reports differences, writes nothing) and --write applies them, so rw ADDS a flag instead of removing one.

Command: pnpm exec biome format .

rw

Inserts --write.

JSON Patch
[
  {
    "op": "add",
    "path": "/3",
    "value": "--write"
  }
]

Example

// biome-format checks formatting through Biome (pnpm exec biome format); the
// rw charm (magus run format:rw) applies --write. The spell's alternative to
// prettier - the magusfile picks one, not both.
import "magus";
import "magus/spell/typescript";

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

export fun format(ctx: magus\Context, args: [str]) > void {
    typescript["biome-format"](ctx);
}

dev-server

dev-server runs the project's package.json "dev" script via the package manager - framework-neutral (Vite, Next, webpack-dev-server, ...). No readiness probe is declared: the port and startup signal vary by framework, so guessing one would be wrong more often than right (readiness is optional - see services.md). A magusfile that needs to block on readiness for its specific dev server can declare its own service op instead.

Command: pnpm run dev

Example

// dev-server runs the project's package.json dev script (pnpm run dev) as a
// supervised background process when reached via ctx.needs, or foreground
// when run directly.
import "magus";
import "magus/spell/typescript";

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

export fun serve(ctx: magus\Context, args: [str]) > void {
    typescript["dev-server"](ctx);
}

eslint

eslint has no built-in "github" formatter (unlike ruff's --output-format=github); "unix" is the built-in, no-extra-devDependency formatter closest to a CI-friendly, one-line-per-problem shape for annotation/regex parsing.

Command: pnpm exec eslint .

gha

Inserts --format=unix.

JSON Patch
[
  {
    "op": "add",
    "path": "/2",
    "value": "--format=unix"
  }
]

rw

Inserts --fix.

JSON Patch
[
  {
    "op": "add",
    "path": "/2",
    "value": "--fix"
  }
]

Example

// eslint lints the project through the package manager (pnpm exec eslint).
import "magus";
import "magus/spell/typescript";

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

export fun lint(ctx: magus\Context, args: [str]) > void {
    typescript["eslint"](ctx);
}

npm-ci

Command: npm ci --prefer-offline

update

Replaces ci with update, drops --prefer-offline.

JSON Patch
[
  {
    "op": "replace",
    "path": "/0",
    "value": "update"
  },
  {
    "op": "remove",
    "path": "/1"
  }
]

pnpm-install

Command: pnpm install --frozen-lockfile --prefer-offline

update

Replaces install with update, drops --prefer-offline, drops --frozen-lockfile.

JSON Patch
[
  {
    "op": "replace",
    "path": "/0",
    "value": "update"
  },
  {
    "op": "remove",
    "path": "/2"
  },
  {
    "op": "remove",
    "path": "/1"
  }
]

prettier

Command: pnpm exec prettier --check .

rw

Replaces --check with --write.

JSON Patch
[
  {
    "op": "replace",
    "path": "/2",
    "value": "--write"
  }
]

Example

// prettier checks formatting; the rw charm (magus run format:rw) rewrites in place.
import "magus";
import "magus/spell/typescript";

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

export fun format(ctx: magus\Context, args: [str]) > void {
    typescript["prettier"](ctx);
}

scip

The indexer is a PATH binary (install it with mise, not as a project dep), so it is forked directly. magus injects MAGUS_SYMBOL_INDEX with the cache destination, so the index never lands in the tree; scip-typescript writes there via --output. The runner resolves the bare $MAGUS_SYMBOL_INDEX token against that destination, so no shell is needed to expand it.

Command: scip-typescript index --output $MAGUS_SYMBOL_INDEX

tsc

Command: pnpm exec tsc

Example

// tsc is static analysis, so it composes into the canonical `lint` target
// (alongside eslint) rather than a bespoke `typecheck` target. `magus run lint`
// forks `pnpm exec tsc`.
import "magus";
import "magus/spell/typescript";

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

export fun lint(ctx: magus\Context, args: [str]) > void {
    typescript["tsc"](ctx);
}

tsc-build

tsc-build uses TypeScript's project-references incremental build mode (works even without declared references, via its own .tsbuildinfo cache), emitting per tsconfig outDir - see mgs_listProvidedGlobs.

Command: pnpm exec tsc --build

Example

// tsc-build compiles the project via TypeScript's project-references
// incremental build mode (pnpm exec tsc --build).
import "magus";
import "magus/spell/typescript";

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

export fun build(ctx: magus\Context, args: [str]) > void {
    typescript["tsc-build"](ctx);
}

tsc-clean

tsc-clean mirrors tsc-build's project-references mode: --clean removes the declared outputs and the incremental .tsbuildinfo cache.

Command: pnpm exec tsc --build --clean

Example

// tsc-clean removes tsc-build's declared outputs and its incremental
// .tsbuildinfo cache (pnpm exec tsc --build --clean).
import "magus";
import "magus/spell/typescript";

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

export fun clean(ctx: magus\Context, args: [str]) > void {
    typescript["tsc-clean"](ctx);
}

vitest

Command: pnpm exec vitest run

gha

Appends --reporter=github-actions.

JSON Patch
[
  {
    "op": "add",
    "path": "/-",
    "value": "--reporter=github-actions"
  }
]

Example

// vitest runs the test suite; the gha charm annotates failures in GitHub Actions.
import "magus";
import "magus/spell/typescript";

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

export fun test(ctx: magus\Context, args: [str]) > void {
    typescript["vitest"](ctx);
}
generatedspells/typescript/spell.buzztypescriptspellnodeeslintvitesttools
Last updated (63e73856)
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.

Ward

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

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.

Service

A long-running or shared process magus manages across runs, distinct from a one-shot target. See services.

Server

The background process a person starts with magus server start. It serves MCP, the console, background jobs and the warm knowledge graph, and adopts nested magus calls into one pool. See server.

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.

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.

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.