Tool catalog

Zuke ships 54 typed packages to JSR under the @zuke/* scope. Most are tool wrappers: each turns a real CLI — Deno, Docker, kubectl, ESLint, Playwright — into a fluent, strongly-typed task you configure in a lambda, so build steps are refactor-safe and editor-completable. The rest are the engine and plugins the wrappers are built on.

The wrapper model

A wrapper is a settings-lambda: you configure a fluent settings object in a callback, and the task function assembles the argv and runs it. Arguments stay a discrete array end-to-end — never a shell string — so command construction is injection-free. Awaiting a task resolves to the same CommandOutput the shell $ produces.

import { DenoTasks } from "jsr:@zuke/deno";
import { NpmTasks } from "jsr:@zuke/npm";
import { CmdTasks } from "jsr:@zuke/cmd";

// Configure a fluent settings object in a lambda; the task builds the argv.
await DenoTasks.test((s) => s.allowAll().coverage("cov_profile"));
await NpmTasks.run((s) => s.script("build").workspace("app"));

// Fallback for any tool without a dedicated wrapper:
await CmdTasks.exec("git", (s) => s.args("rev-parse", "HEAD"));

Every settings object also supports the shared chainers .env(), .cwd(), .noThrow(), .quiet(), .toolPath() (binary override) and .args() (an escape hatch for flags without a typed option). Need the binary itself? A build can fetch a pinned, checksum-verified CLI and hand its path to .toolPath(...) — see Installing tools.

Every card below links to the package's page on JSR, where the full task list and typed settings for that wrapper are documented. To read a wrapper's API in your terminal without @types/node noise, run zuke doc <package> (e.g. zuke doc deno).

The catalog

Grouped by capability. A package can appear under more than one heading when it covers more than one job.

Runtimes & package managers

Install, run, and publish across every major JS toolchain.

Bundlers & monorepo

Bundle apps and orchestrate monorepos from a typed pipeline.

TypeScript runners & compilers

Execute, type-check, and compile TypeScript with or without a build step.

Frameworks & code generation

Scaffold app frameworks and generate typed clients from a schema.

AI coding, review & self-healing

Fold the major AI coding CLIs into a build, gate it on a model-assessed review, or let a fixer heal failures.

Lint, format & quality

Keep the tree clean with linters, formatters, and dead-code checks.

Test, coverage & browsers

Run unit and end-to-end suites, then upload coverage to your dashboard.

Containers & orchestration

Build images and ship to clusters from a typed pipeline.

Cloud & infrastructure

Provision and deploy with infra-as-code, typed end to end.

Version control, registry & CI

Script Git, GitHub, and publishing straight from your build.

Supply-chain security

Scan workflows, secrets, and dependencies as part of the gate.

Resolving the binary

By default a wrapper spawns the bare tool name and lets the OS find it on PATH. In a Node monorepo the tools are usually installed locally and hoisted to the repo root, so Zuke can resolve them npx-style — walking up from the working directory for node_modules/.bin/<tool> and falling back to PATH on a miss. Three ways to turn it on, most specific first:

  • Per call: .fromNodeModules() (or .fromPath() to force PATH) on any settings object.
  • Per wrapper: the JS-ecosystem wrappers default to node_modules-first, so in a workspace package whose binaries are hoisted you write no .toolPath() and no .fromNodeModules() at all.
  • Repo-wide: ZUKE_TOOL_RESOLUTION=node_modules (or path) flips every wrapper without touching call sites. A per-call setting still wins over the ambient value.
// Per call: resolve npx-style from node_modules/.bin (or force PATH).
await OxlintTasks.lint((s) => s.fromNodeModules());
await EslintTasks.lint((s) => s.fromPath());

// Repo-wide, no code change — a per-call setting still wins over this.
// ZUKE_TOOL_RESOLUTION=node_modules   (or "path")

An explicit .toolPath(...) always wins over resolution — pins from toolchain() stay hermetic. If a binary is missing, Zuke retries through cmd /c on Windows (npm ships as a .cmd shim there) and otherwise raises a ToolNotFoundError that names the tool and the fix (run npm ci, or provision it via toolchain()). resolvedArgv() reports the argv a run will actually spawn — the resolved shim or the bare fallback — for diagnostics.

Define your own tool

For a CLI without a dedicated package, defineTool (from @zuke/core/tooling) gives you a typed, fluent task in the same style — no class needed. Build the argv with arg / flag / option (in call order), and the shared chainers all apply. An optional subcommand is prepended to every invocation.

import { defineTool } from "jsr:@zuke/core/tooling";

const terraform = defineTool("terraform");
await terraform((s) => s.arg("plan").option("out", "plan.tfplan"));
// → terraform plan --out plan.tfplan

const helmUpgrade = defineTool("helm", { subcommand: "upgrade" });
await helmUpgrade((s) => s.arg("api", "./chart").flag("install").cwd("infra"));
// → helm upgrade api ./chart --install   (run in ./infra)

flag/option add a -- prefix unless the name already starts with a dash (so flag("-v") stays -v). Argv is a discrete array end-to-end, so a defineTool command is just as injection-free as the built-in wrappers. For the full extension surface — custom settings classes and plugins — see Extending Zuke.

Engine & plugin packages

Everything that isn't a CLI wrapper — the build engine, the CLI, and the layers the wrappers sit on: