Getting started
Zuke is a code-first, strongly-typed build automation system for Deno & TypeScript. Builds are plain TypeScript classes — targets are class fields, dependencies are real references, and Zuke runs everything in topological order. This guide gets you from zero to your first green build.
Install
Zuke runs on Deno and ships on JSR. Install the CLI globally, then let it scaffold a project:
# Install the CLI globally with Deno
deno install -A -g -n zuke jsr:@zuke/cli
# Scaffold zuke.ts, launchers, and config in your project
zuke setup
# Run it — the ./zuke launcher bootstraps Deno if needed
./zuke
No Deno yet? The generated ./zuke launcher will install a
pinned, known-good version on first run — so contributors don't need to set
anything up by hand.
Scaffold a build
zuke setup creates a zuke.ts at your project root
alongside the zuke / zuke.ps1 launchers and a small
zuke.json. The zuke.ts file is your build —
edit it like any other TypeScript module.
Already have package.json scripts or a Makefile?
zuke import reads them and generates a zuke.ts with
one target() per task — a working starting point to refine into
typed wrappers, rather than a blank page. It auto-detects the source
(package.json first, then a Makefile); pass
--from makefile to pin it. A command maps to
CmdTasks.exec(...), an && chain becomes
sequential steps, a script delegation or Makefile prerequisite becomes a
.dependsOn(...), and anything too shell-specific to translate is
preserved behind a // TODO so the file still compiles. Like
zuke setup, it also writes the launchers and
deno.json.
Your first target
A target is a class field built with target(). Give it a
description, declare what it dependsOn using
this.* references, and provide the work in
executes:
import { Build, run, target } from "jsr:@zuke/core";
import { DenoTasks } from "jsr:@zuke/deno";
class MyBuild extends Build {
clean = target()
.description("Remove build output")
.executes(async () => {
await $`rm -rf dist`;
});
compile = target()
.description("Type-check the project")
.dependsOn(this.clean)
.executes(async () => {
await DenoTasks.check((s) => s.paths("mod.ts"));
});
}
await run(MyBuild);
Because compile depends on this.clean, Zuke runs
clean first — automatically and exactly once. Rename
clean and the reference moves with it; there are no strings to
keep in sync.
The closing await run(MyBuild) makes the file runnable.
run() is entry-aware — it executes only when the file is run
directly, and is a no-op when imported (say, from a test) — so no
import.meta.main guard is needed.
Running targets
Invoke any target through the launcher:
./zuke compile # run a target (and its dependencies)
./zuke compile --parallel # run independent targets concurrently
./zuke compile --skip clean # run it, but skip a named dependency (repeatable)
./zuke ci --affected=origin/main # only targets affected by changes since a base
./zuke --list # list every available target
./zuke --list --json # …the build surface (targets, flags, params) as JSON
./zuke graph # print the dependency graph
./zuke doc jsr:@zuke/deno # prints the typed API docs for a @zuke package
./zuke generate-ci # write declared CI/CD pipeline files
./zuke completions install zsh # install shell tab-completion
./zuke mcp # run an MCP server over the build for AI agents
./zuke # run the default target
Each target is framed with a ruled banner and closes with a pass/fail line
and its duration. The run ends with an aligned Build Summary
— status (Succeeded / Failed / Skipped
/ Cached) and timing per target, a total, and a one-line verdict
with a timestamp. A target named default runs when you pass no
arguments.
A few flags narrow what runs. --skip <dep> runs the target
but drops a named dependency (repeatable).
--affected[=<base>] keeps only the targets a git diff can
reach — a changed file inside a target's .inputs(), or anything
downstream of one — skipping the rest; the base defaults to
HEAD, and --affected=origin/main is the usual CI
form. And --list --json emits the whole build surface — targets,
flags, and parameters — as JSON for tools and agents.
zuke doc <spec> prints the typed API docs for a package.
The spec is passed to deno doc as-is: a
jsr:/npm:/https: specifier or an
absolute path passes through unchanged (say
zuke doc jsr:@zuke/deno), while a relative or bare name is
resolved as a file path against the working directory. It runs
deno doc from an isolated directory, so a surrounding Node
project's type resolution can't drown the output.
$ ./zuke build
Build Summary
────────────────────────────
Target Status Duration
────────────────────────────
clean Succeeded 0.1s
install Succeeded 12.4s
check Succeeded 3.1s
build Succeeded 2.2s
────────────────────────────
Total 17.8s
✔ Build succeeded — 4/4 targets in 17.8s · 2026-06-23 07:19
Under GitHub Actions the same run renders as collapsible
::group:: blocks with ::error:: annotations and
secret masking, plus a matching Markdown summary table on the job page.
Visualizing the graph
zuke graph prints the dependency graph as a terminal listing.
Add --output=html to render it instead as a self-contained,
interactive Cytoscape page written to
<repo root>/.zuke/graph.html — which Zuke then opens in your
browser:
./zuke graph # print the dependency graph as text
./zuke graph --output=html # render an interactive HTML page and open it
./zuke graph --output=html --no-open # write .zuke/graph.html without opening
Nodes are colour-coded by dependency depth (roots through leaves) on a dark
canvas. Pan and zoom freely, click a target to highlight
everything it connects to — its transitive dependencies and dependents — and
switch the layout direction or export a PNG from the header. Targets in a
group() are drawn inside a
labelled box. Pass --no-open to write the file without launching
a browser (handy in CI). See
Zuke's own build graph for a rendered example.
Cytoscape loads from a pinned CDN, so the first view needs internet access; the page itself is otherwise self-contained.
Shell completions
Zuke ships tab-completion for bash, zsh, and
fish. The install sub-command writes the script
and wires it into your shell in one step; print emits it to
stdout if you'd rather source it yourself:
# Install completions for your shell — writes the script and wires it in
./zuke completions install bash # → ~/.config/zuke/completions/zuke.bash (+ ~/.bashrc)
./zuke completions install zsh # → ~/.config/zuke/completions/zuke.zsh (+ ~/.zshrc)
./zuke completions install fish # → ~/.config/fish/completions/zuke.fish (auto-loaded)
# Or print the script to stdout and source it yourself
source <(./zuke completions print bash)
source <(./zuke completions print zsh)
./zuke completions print fish | source
Completions cover your target names, the reserved commands
(graph, generate-ci, completions), the
built-in flags, and any declared parameters
as --flag candidates. The sub-action is required —
zuke completions bash on its own prints
Usage: zuke completions <install|print> <bash|zsh|fish> and exits 1.
install honours $XDG_CONFIG_HOME and is idempotent —
re-running won't duplicate the line in your rc file. The script is a static
snapshot of the build, so re-run it after adding or renaming targets.
Next steps
- Core concepts — targets, dependencies, groups, caching, conditional execution, the
$shell, parameters, code-first CI/CD, and extending Zuke. - Service targets — declare a long-lived process (dev server, database, mock API) with
service(); Zuke starts it, waits until it's ready, and tears it down. - Caching — the incremental build cache (
.inputs()/.outputs()/.cacheKey()), the remote cache, and the AI response cache (aiCache). - Secrets — source a secret from a manager with
.from(execSecret/fileSecret)and have Zuke redact it from every log, summary, and error. - Core library — the built-in helpers: file tasks, globbing, HTTP, compression, tool installs, assertions, and paths.
- Installing tools — fetch the CLIs a build drives, pinned by SHA-256, verified, and cached (
ToolTasks.install(),toolchain()). - Console output — rich terminal output with
@zuke/console: markup, rules, boxes, tables, and a levelled logger. - AI code review — gate a build on a model-assessed security review with
@zuke/ai. - Self-healing builds — let an AI fixer diagnose, suggest, and auto-fix a failed target, then re-run the real command to verify.
- MCP server — expose the build to AI agents as typed tools over the Model Context Protocol with
zuke mcp. - Examples — real-world build files for Node, Deno libraries, Docker, and generated CI pipelines.
- JSR packages — the full set of typed tool wrappers.