Set up Zuke in a project
Zuke defines builds as a TypeScript class run on Deno. Each target is a
class field; targets reference each other by this.<field> (never strings).
Packages are imported from JSR (jsr:@zuke/...), not npm.
The fast path: zuke setup
The @zuke/cli tool scaffolds everything. Install it once, then run setup in
the target project:
deno install -A -g -n zuke jsr:@zuke/cli # once, globally
zuke setup # in the project root
./zuke # run the build
No global install? The same wizard runs directly:
deno run -A jsr:@zuke/cli setup
setup flags: --dir <path>, --name <ClassName>, --force (overwrite
existing files), --yes (non-interactive), --launcher-name <name> (write the
launcher under a different name when a zuke/ directory already occupies it โ a
directory collision now fails with an actionable error instead of silently
skipping the launcher).
To read a @zuke/* package's API without a Node repo's @types/node noise, run
zuke doc <package> (e.g. zuke doc core) โ it runs deno doc in an isolated
directory.
Migrating an existing project: zuke import
If the project already has package.json scripts or a Makefile, prefer
zuke import over setup โ it reads them and generates a zuke.ts with a
target per task, a working starting point instead of a blank build:
zuke import # auto-detects package.json, then a Makefile
zuke import --from makefile # or pin the source (package.json | makefile)
Each script/target becomes a target(); a command maps to CmdTasks.exec(...)
โ a placeholder, not the destination: before accepting it, check the package
catalogue (llms.txt's ## Packages list, or the table in
zuke-write-build's cheatsheet)
for a @zuke/<tool> wrapper matching that command and replace the placeholder
with it โ leaving CmdTasks.exec in place for a tool that has a typed wrapper
is a bug, not a shortcut. An && chain becomes sequential steps, a
run/prerequisite delegation becomes .dependsOn(...), and anything too
shell-specific to translate (pipes, redirects, env assignments) is preserved
behind a // TODO so the file still compiles. It scaffolds the launchers and
deno.json exactly like setup, and takes the same --dir, --name,
--force, --yes flags. Afterwards, use the zuke-write-build skill to
finish replacing any remaining generated CmdTasks.exec calls with typed
*Tasks wrappers.
What zuke setup writes
zuke.tsโ a starter build class with a sample target and adefault../zuke+./zuke.ps1โ launchers that locate the project and runzuke.tswith the Deno onPATH. If Deno is missing they point at the official install docs and exit rather than piping an install script into a shell, which would download and execute code unverified. They pass--frozenonce adeno.lockexists, so the first run writes the lockfile and every run after verifies it.deno.jsonโ merged to add azuketask, plusfmt/lint/testif absent. The merge is all-or-nothing: if azuketask is already declared the file is left alone entirely, and an unparseable one is skipped with a notice.zuke.jsonโ{ "name": "..." }, which marks the repo root..gitignoreโ created or appended so.zuke/is ignored (the cache and durable run state live there); untouched if it already covers it.
Running the build
./zuke # run the default target (Windows: .\zuke.ps1)
./zuke <target> # run a specific target
./zuke --list # list every target
./zuke --list --json # the whole build surface (commands, flags, targets) as JSON
./zuke <target> --dry-run # print the plan without executing
The CLI is self-describing: ./zuke --help prints the usage grammar plus the
build's live targets and parameters, so an agent discovers the real command
surface instead of guessing. For an AI client to operate the build through typed
calls, zuke mcp runs a Model Context Protocol server over it (register with
claude mcp add zuke -- deno run -A zuke.ts mcp; add --allow-run to let the
agent execute targets, not just inspect them).
If Deno is already installed you can also use deno task zuke <target> or
deno run -A zuke.ts <target>. The -A flag grants permissions, since targets
typically run processes and touch files. These are not quite equivalent to the
launcher: the scaffolded zuke task deliberately omits --frozen, so it may
heal a stale lockfile where ./zuke would fail on it.
Manual setup (no CLI)
Create zuke.ts in the project root, extend Build, declare targets with
target(), and call await run(MyBuild) at the bottom:
import { Build, run, target } from "jsr:@zuke/core";
import { DenoTasks } from "jsr:@zuke/deno";
class CI extends Build {
lint = target().executes(() => DenoTasks.lint());
test = target().dependsOn(this.lint)
.executes(() => DenoTasks.test((s) => s.allowAll()));
default = target().dependsOn(this.test).executes(() => {});
}
await run(CI);
Run with deno run -A zuke.ts test. (For the ./zuke launcher experience,
prefer zuke setup, which drops the launcher scripts in for you.)
Finding the exact API โ never guess
Every external tool has a typed *Tasks wrapper; do not fall back to
Deno.Command or hand-rolled shell. First confirm a wrapper exists at all โ
llms.txt's ## Packages catalogue or the table in
zuke-write-build's cheatsheet
is the only way to answer that; a per-package deno doc needs a name to target,
so it cannot reveal that one exists. Once you know the package name, get its
exact signatures:
- A single package on the command line:
deno doc jsr:@zuke/<package>. Prefer this in a consumer repo โ it resolves the version the project actually has installed, so it cannot describe an API that version lacks. - The whole typed surface of every package is in
llms-full.txt(indexed byllms.txt) โ at the repo root in the Zuke repo itself, or from a consumer repo https://raw.githubusercontent.com/zuke-build/zuke/master/llms-full.txt (index: https://raw.githubusercontent.com/zuke-build/zuke/master/llms.txt). Both trackmaster, so they may document symbols that are merged but not yet released; use them to find what exists, then confirm the signature withdeno doc.
Once the project is scaffolded, use the zuke-write-build skill to add and edit targets.