Claude Skill

zuke-setup

Set up Zuke — a code-first, strongly-typed build automation system for Deno/TypeScript — in a project. Use when the user wants to add Zuke to a repo, scaffold a zuke.ts build file, install the Zuke CLI, or bootstrap the ./zuke launcher. After scaffolding, switch to the zuke-write

LLM Mart · 0 points · 13 views 41 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download zuke-build-zuke-plugins_zuke_skills_zuke-setup-9fd4537.zip · 5 KB
Part of zuke-build/zuke — 4 skills

Install

skills CLI npx skills add https://github.com/zuke-build/zuke/tree/master/plugins/zuke/skills/zuke-setup
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install zuke-build-zuke@llmmart
Git git clone https://github.com/zuke-build/zuke.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole zuke-build/zuke collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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), --bootstrap-deno / --no-bootstrap-deno (which launchers to write — see below; the wizard asks when interactive, and --yes takes the default, bootstrap), --mcp (also write .mcp.json registering the build's MCP server, so this agent — and any other stdio MCP client — can list and run the targets through typed calls; --allow-run registers it with execution enabled and implies --mcp), --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). With --mcp, an MCP client lists and runs the targets through typed calls; without it, the build is still discoverable through ./zuke --list --json.

Running as an agent, always pass --yes: it skips every interactive question, including the closing "star the Zuke repository?" prompt — that question is for a human at a terminal, and an agent must never answer it (or star anything) on the user's behalf.

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, --bootstrap-deno / --no-bootstrap-deno, --mcp and --allow-run 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 a default.
  • ./zuke + ./zuke.ps1 — launchers that locate the project and run zuke.ts. By default (--bootstrap-deno) they use the Deno on PATH and, when there is none, download the pinned release Zuke itself runs on, verify it against a per-platform SHA-256, and install it under ~/.deno — never an install script, never an unverified binary — so a clone needs nothing installed first. With --no-bootstrap-deno they require Deno on PATH and exit with the install docs URL when it is missing, for a project that must never download a tool from its build entry point. Both pass --frozen once a deno.lock exists, so the first run writes the lockfile and every run after verifies it.
  • deno.json — merged to add an imports entry for @zuke/core (zuke import adds @zuke/cmd too when it generates one), a zuke task, and fmt/lint/test if absent. The scaffolded build imports by bare specifier (from "@zuke/core"), not an inline jsr:@zuke/core@^1 — Deno's default lint set rejects an inline specifier under no-import-prefix, so an inlined one would fail the project's own deno task lint on its only source file. The caret major is pinned in the import map instead. Merging is additive per key: a task or import the file already declares is kept exactly as it is (so a deliberate version pin survives), only what is missing is added. A file that has the zuke task but not the imports is still completed — skipping it would leave the zuke.ts written in the same run with nothing to resolve. Two shapes cannot be completed automatically, and setup reports them as steps for you and exits 1 rather than printing Next: ./zuke over a build that cannot start: a deno.json that is JSONC (Deno accepts // comments and trailing commas that a JSON parser does not, and rewriting the file would discard them), and one that delegates via importMap to a separate file (Deno ignores that field the moment imports appears beside it, so the entry belongs in the other file — setup reads that file and stays quiet once it declares the entry, so a correctly configured project still exits 0). Both are left byte-for-byte untouched. A mapping that already points somewhere other than the JSR package is kept — that is how a pin or a local checkout survives — with a note saying so. --mcp that cannot register into an unparseable .mcp.json is reported the same way, as a note.
  • 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.
  • .mcp.json (with --mcp) — mcpServers.zuke launching deno run -A zuke.ts mcp (plus --allow-run when asked). Merged around any other servers already in the file; an existing zuke entry is kept unless --force is set.

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
./zuke <target> --no-banner # no opening banner (also ZUKE_NO_BANNER)

Every run opens with a banner: the Zuke wordmark, then zuke <version> · deno <version> · <platform> and run <id> · <cwd>. On CI the wordmark is dropped — the identifying lines are what a runner log wants, six lines of ASCII are not — and the detected host is appended to the first line. --no-banner or ZUKE_NO_BANNER turns it off entirely.

With the global CLI installed, the bare zuke works from anywhere inside the project: zuke <target>, zuke --list, zuke graph, zuke mcp, bare zuke for the default target — every command that is not the CLI's own (setup, import, doc) is forwarded to the nearest zuke.ts. --help and --version show both surfaces, the CLI's and then the build's, each labelled; zuke -- --help is the build's own usage alone, and zuke -- <target> reaches a target that shares one of those names. It walks up to the zuke.json that marks the repository root and runs deno run -A zuke.ts <args> from there, with --frozen once a deno.lock exists — the launcher's exact behaviour, minus the Deno bootstrap. Outside a project, zuke <target> reports the unknown command and the missing zuke.json, and a bare zuke prints the usage. The forwarding refuses a project whose root directory is owned by another user or is world-writable, and one where an ancestor deno.json, deno.jsonc or package.json Deno would read is owned by another user (the safe.directory rule git applies, since discovery runs code the caller never named); the error names what was refused and the fix — run that project's own ./zuke there, or fix the ownership. On Windows the gate is inert.

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)

Declare the packages the build imports in deno.json — bare specifier to jsr: dependency, caret major — so the build can import them by bare specifier. An inline jsr:@zuke/core@^1 in the import statement fails deno lint under its default no-import-prefix rule:

{
  "imports": {
    "@zuke/core": "jsr:@zuke/core@^1",
    "@zuke/deno": "jsr:@zuke/deno@^1"
  }
}

Then 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 "@zuke/core";
import { DenoTasks } from "@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:

Once the project is scaffolded, use the zuke-write-build skill to add and edit targets.

Files (zuke)
  • SKILL.md 11.9 KB
    ---
    name: zuke-setup
    description: Set up Zuke — a code-first, strongly-typed build automation system for Deno/TypeScript — in a project. Use when the user wants to add Zuke to a repo, scaffold a zuke.ts build file, install the Zuke CLI, or bootstrap the ./zuke launcher. After scaffolding, switch to the zuke-write-build skill to author targets.
    ---
    
    # 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:
    
    ```sh
    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:
    
    ```sh
    deno run -A jsr:@zuke/cli setup
    ```
    
    `setup` flags: `--dir <path>`, `--name <ClassName>`, `--force` (overwrite
    existing files), `--yes` (non-interactive), `--bootstrap-deno` /
    `--no-bootstrap-deno` (which launchers to write — see below; the wizard asks
    when interactive, and `--yes` takes the default, bootstrap), `--mcp` (also write
    `.mcp.json` registering the build's MCP server, so this agent — and any other
    stdio MCP client — can list and run the targets through typed calls;
    `--allow-run` registers it with execution enabled and implies `--mcp`),
    `--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). With `--mcp`, an
    MCP client lists and runs the targets through typed calls; without it, the build
    is still discoverable through `./zuke --list --json`.
    
    Running as an agent, always pass `--yes`: it skips every interactive question,
    including the closing "star the Zuke repository?" prompt — that question is for
    a human at a terminal, and an agent must never answer it (or star anything) on
    the user's behalf.
    
    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:
    
    ```sh
    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](../zuke-write-build/references/cheatsheet.md))
    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`, `--bootstrap-deno` / `--no-bootstrap-deno`, `--mcp` and
    `--allow-run` 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 a `default`.
    - **`./zuke`** + **`./zuke.ps1`** — launchers that locate the project and run
      `zuke.ts`. By default (`--bootstrap-deno`) they use the Deno on `PATH` and,
      when there is none, download the pinned release Zuke itself runs on, verify it
      against a per-platform SHA-256, and install it under `~/.deno` — never an
      install script, never an unverified binary — so a clone needs nothing
      installed first. With `--no-bootstrap-deno` they require Deno on `PATH` and
      exit with the install docs URL when it is missing, for a project that must
      never download a tool from its build entry point. Both pass `--frozen` once a
      `deno.lock` exists, so the first run writes the lockfile and every run after
      verifies it.
    - **`deno.json`** — merged to add an `imports` entry for `@zuke/core`
      (`zuke import` adds `@zuke/cmd` too when it generates one), a `zuke` task, and
      `fmt`/`lint`/`test` if absent. The scaffolded build imports by **bare
      specifier** (`from "@zuke/core"`), not an inline `jsr:@zuke/core@^1` — Deno's
      default lint set rejects an inline specifier under `no-import-prefix`, so an
      inlined one would fail the project's own `deno task lint` on its only source
      file. The caret major is pinned in the import map instead. Merging is additive
      per key: a task or import the file already declares is kept exactly as it is
      (so a deliberate version pin survives), only what is missing is added. A file
      that has the `zuke` task but not the imports is still completed — skipping it
      would leave the `zuke.ts` written in the same run with nothing to resolve. Two
      shapes cannot be completed automatically, and setup reports them as steps for
      you and **exits 1** rather than printing `Next: ./zuke` over a build that
      cannot start: a `deno.json` that is JSONC (Deno accepts `//` comments and
      trailing commas that a JSON parser does not, and rewriting the file would
      discard them), and one that delegates via `importMap` to a separate file (Deno
      ignores that field the moment `imports` appears beside it, so the entry
      belongs in the other file — setup reads that file and stays quiet once it
      declares the entry, so a correctly configured project still exits 0). Both are
      left byte-for-byte untouched. A mapping that already points somewhere other
      than the JSR package is kept — that is how a pin or a local checkout survives
      — with a note saying so. `--mcp` that cannot register into an unparseable
      `.mcp.json` is reported the same way, as a note.
    - **`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.
    - **`.mcp.json`** (with `--mcp`) — `mcpServers.zuke` launching
      `deno run -A zuke.ts mcp` (plus `--allow-run` when asked). Merged around any
      other servers already in the file; an existing `zuke` entry is kept unless
      `--force` is set.
    
    ## Running the build
    
    ```sh
    ./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
    ./zuke <target> --no-banner # no opening banner (also ZUKE_NO_BANNER)
    ```
    
    Every run opens with a banner: the Zuke wordmark, then
    `zuke <version> · deno <version> · <platform>` and `run <id> · <cwd>`. On CI the
    wordmark is dropped — the identifying lines are what a runner log wants, six
    lines of ASCII are not — and the detected host is appended to the first line.
    `--no-banner` or `ZUKE_NO_BANNER` turns it off entirely.
    
    With the global CLI installed, the bare `zuke` works from anywhere inside the
    project: `zuke <target>`, `zuke --list`, `zuke graph`, `zuke mcp`, bare `zuke`
    for the default target — every command that is not the CLI's own (`setup`,
    `import`, `doc`) is forwarded to the nearest `zuke.ts`. `--help` and `--version`
    show both surfaces, the CLI's and then the build's, each labelled;
    `zuke -- --help` is the build's own usage alone, and `zuke -- <target>` reaches
    a target that shares one of those names. It walks up to the `zuke.json` that
    marks the repository root and runs `deno run -A zuke.ts <args>` from there, with
    `--frozen` once a `deno.lock` exists — the launcher's exact behaviour, minus the
    Deno bootstrap. Outside a project, `zuke <target>` reports the unknown command
    and the missing `zuke.json`, and a bare `zuke` prints the usage. The forwarding
    refuses a project whose root directory is owned by another user or is
    world-writable, and one where an ancestor `deno.json`, `deno.jsonc` or
    `package.json` Deno would read is owned by another user (the `safe.directory`
    rule git applies, since discovery runs code the caller never named); the error
    names what was refused and the fix — run that project's own `./zuke` there, or
    fix the ownership. On Windows the gate is inert.
    
    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)
    
    Declare the packages the build imports in `deno.json` — bare specifier to `jsr:`
    dependency, caret major — so the build can import them by bare specifier. An
    inline `jsr:@zuke/core@^1` in the import statement fails `deno lint` under its
    default `no-import-prefix` rule:
    
    ```json
    {
      "imports": {
        "@zuke/core": "jsr:@zuke/core@^1",
        "@zuke/deno": "jsr:@zuke/deno@^1"
      }
    }
    ```
    
    Then create `zuke.ts` in the project root, extend `Build`, declare targets with
    `target()`, and call `await run(MyBuild)` at the bottom:
    
    <!-- check -->
    
    ```ts
    import { Build, run, target } from "@zuke/core";
    import { DenoTasks } from "@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](../zuke-write-build/references/cheatsheet.md)
    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 by
      `llms.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 track `master`, so they may document symbols that are merged but not yet
      released; use them to find what exists, then confirm the signature with
      `deno doc`.
    
    Once the project is scaffolded, use the **zuke-write-build** skill to add and
    edit targets.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related