zudo-doc
GitHub repository

Type to search...

to open search from anywhere

create-zudo-doc CLI

Created Mar 15, 2026Updated Sep 7, 2026Takeshi Takatsudo

Complete CLI reference for the create-zudo-doc project scaffolder.

Usage

create-zudo-doc [destination] [options]

When run without flags, the CLI launches an interactive wizard. All options can be specified via flags for non-interactive (CI/agent) usage.

You can also use the Setup Preset Generator to interactively build a configuration and copy it as a JSON preset or CLI command.

Destination and project name

The first positional argument is the destination — the directory to scaffold into. It may be a path, and its last segment becomes the project name written to the generated package.json:

# Creates ./sub/ref-doc, package name "ref-doc"
pnpm create zudo-doc sub/ref-doc --yes

Only that last segment goes through the project-name grammar (starts with a lowercase letter or digit; lowercase letters, digits, dots, underscores, and hyphens only; 214 characters max). The directories leading up to it are just a path, so Scratch-Dir/ref-doc is accepted.

DestinationDirectory createdProject name
my-docs./my-docsmy-docs
sub/ref-doc./sub/ref-docref-doc
./sub/ref-doc/./sub/ref-docref-doc
../ref-doc../ref-docref-doc
/tmp/scratch/ref-doc/tmp/scratch/ref-docref-doc

Upward traversal (../ref-doc) and absolute destinations are both accepted. What is rejected is ., .., and a filesystem root (/) — they name no last segment to derive the project name from:

Destination "." has no final path segment to name the project. Pass a destination whose last segment names the project, e.g. "sub/my-docs"

When the last segment is a legal directory name but not a legal package name, the error points at that segment:

Invalid project name "My-Docs" — the last segment of destination "sub/My-Docs" is used as the package name. Project name must start with a lowercase letter or digit and contain only lowercase letters, digits, dots, underscores, and hyphens

--name overrides the derived name while the positional argument still supplies the directory — that is how you scaffold into a directory whose name is not a valid package name:

# Creates ./sub/My-Docs, package name "ref-doc"
pnpm create zudo-doc sub/My-Docs --name ref-doc --yes

--name itself remains a bare package name: a path passed to it is still rejected. And a destination with no last segment stays rejected even when --name is present.

What the CLI generates

The scaffold is minimal — roughly a dozen files (see Project Structure). The layout, chrome, and schema all ship from @takazudo/zudo-doc and are consumed from node_modules, so the generated project holds only what is genuinely its own.

Your feature choices are written into a single zfb.config.ts as a zudoDoc({ … }) call containing only the fields that differ from the defaults (siteName is always emitted). There is no separate src/config/settings.ts — the one config file is the whole configuration surface. See Configuration for every field.

A few things a fresh scaffold intentionally does not include:

  • No .zudo-doc.json — the eject provenance file is lazy-created on your first zudo-doc eject, so an un-ejected project never carries it.

  • No check:html / HTML-validation step and no gen:z-index / check:z-index codegen — both were pruned from generated projects. The default z-index tiers ship from the package; HTML validation and pre-push suites are opt-in add-backs (see Customizing → Restoring pre-push and HTML validation).

  • No deploy adapter or wrangler.toml — the default build is a pure static export. Deploying (or enabling SSR features like the AI assistant) is an explicit extend step.

Note

@takazudo/zdtp is added only when you enable designTokenPanel. It is an optional peer of @takazudo/zudo-doc, so a project that leaves the panel off never installs it. The package's chrome still imports the panel bootstrap module at module scope (so zfb's island scanner can find it), but that module reaches @takazudo/zdtp solely through a rejection-handled dynamic import() on first use (see Lazy loading) — an edge the bundler tolerates when the package is absent, so a panel-less project builds without it. If you turn designTokenPanel on in a project that was scaffolded without it, install @takazudo/zdtp yourself and add @import "@takazudo/zdtp/styles.css"; to src/styles/global.css.

Color Schemes

zudo-doc ships exactly two color schemes — Default Light and Default Dark — sharing one set of OKLCH ramps. There is no bundled catalog of community or terminal presets to pick a --scheme value from.

Customization happens one level deeper, through the colorSchemes escape-hatch field in zfb.config.ts: pass your own { ramps, map } palette map to zudoDoc({ colorSchemes: { … } }) (see Color — "Adding a Custom Color Scheme"). Preview the result live in the Design Token Panel's Palette tab (see Color Scheme Preview) before committing it to source.

Theme Packs

Independently of the color scheme, the scaffold can start a project on any bundled theme pack — a prebuilt design bundle layered on top of the scheme system. Pass --theme-pack <slug>, or answer the interactive "Theme pack:" prompt (asked between the color-scheme questions and the feature multiselect). The chosen slug is written to themePack in the generated zfb.config.ts (only when it differs from default, per the diff-from-defaults rule). The on-page switcher UI is a separate feature toggle, --theme-pack-switcher.

Options

Project

FlagDescriptionDefault
[destination]Directory to scaffold into (first positional arg); may be a path whose last segment becomes the project namemy-docs
--name <name>Project name written to the generated package.json; overrides the name derived from the destinationdestination's last segment
--lang <code>Default language codeen
--additional-langs <a,b>Ordered additional locale codes; implies i18n and replaces a preset list
--changelog-packages <a,b>Generate a nested changelog landing page and one package index per comma-separated slug
--github-url <url>GitHub repository URL (drives header link + source link)
--pm <manager>Package manager: pnpm, npm, yarn, bunpnpm
--[no-]installInstall dependencies after scaffoldingprompt
--[no-]gitInitialize a git repository + initial commit (enables doc-history metadata)on

--changelog-packages core,cli implies --changelog and scaffolds a multi-package changelog layout.

Color Scheme

FlagDescriptionDefault
--color-scheme-mode <mode>single or light-darklight-dark
--scheme <name>Color scheme (single mode)Default Dark
--light-scheme <name>Light scheme (light-dark mode)Default Light
--dark-scheme <name>Dark scheme (light-dark mode)Default Dark
--default-mode <mode>light or dark (light-dark mode)dark
--[no-]respect-system-preferenceRespect OS color scheme preferencetrue

Theme Pack

FlagDescriptionDefault
--theme-pack <slug>Theme pack applied to the generated project — any slug from the Theme Gallerydefault

Features

FlagDescriptionDefault
--[no-]i18nLegacy multi-language toggle; without a list, infers one additional localeoff
--[no-]searchBuilt-in full-text/word-match search over a generated search-index.jsonon
--[no-]sidebar-filterReal-time sidebar filteringon
--[no-]claude-resourcesClaude Code docs generationoff
--[no-]codex-resourcesCodex docs generationoff
--[no-]claude-skillsShip zudo-doc-* Claude Code skills (design-system, translate, version-bump)off
--[no-]claude-skills-writingShip the zudo-doc-writing skill (doc-writing + navigation-structure guide for AI-assisted authoring)off
--[no-]design-token-panelInteractive tabbed panel for tweaking spacing, font, size, and color tokensoff
--[no-]theme-pack-switcherBottom-right flyout (and browse-all dialog) to switch between installed theme packsoff
--[no-]sidebar-resizerDraggable sidebar widthon
--[no-]sidebar-toggleShow/hide desktop sidebaron
--[no-]toc-toggleShow/hide the desktop table of contents (edge chevron, xl screens)on
--[no-]versioningMulti-version documentation supportoff
--[no-]doc-historyDocument edit historyon
--[no-]body-foot-utilRight-aligned strip below each doc: doc history trigger + View source on GitHub linkoff
--[no-]llms-txtGenerate llms.txt for LLM consumptionon
--[no-]skill-symlinkerSymlink documentation skills into Claude Code or Codexoff
--[no-]tauriTauri desktop app (Mode 1) — macOS offline reader with in-page searchoff
--[no-]tauri-devTauri dev wrapper (Mode 2) — configurable desktop dev wrapper for any projectoff
--[no-]footer-nav-groupNavigation links in the footeroff
--[no-]image-enlargeClick-to-enlarge for oversized markdown imageson
--[no-]asset-viewerViewer pages for files under public/assetson
--[no-]dynamic-page-transitionSPA-style page transition with history handlingon
--[no-]footer-copyrightCopyright notice in the footeron
--[no-]changelogChangelog pageoff
--[no-]tag-governanceVocabulary-aware tag audit + suggest scriptsoff
--[no-]doc-tagsPer-tag and tag-index browsing routes (docs/tags/...)off
--[no-]footer-taglistGrouped tag index in the footer (requires tagGovernance)off
--[no-]noindexAvoid robots indexing — noindex meta + robots.txtoff

Preset

FlagDescription
--preset <path>Load settings from a JSON preset file (use "-" for stdin)

The --preset flag accepts the JSON output from the Setup Preset Generator. When a preset is loaded, all prompts are skipped (same as --yes). Individual CLI flags override preset values. In particular, --additional-langs ja,de replaces (rather than appends to) a preset's additionalLangs list. A non-empty explicit list enables i18n even if the preset or CLI also contains --no-i18n; the CLI prints a warning when it overrides that explicit disable.

General

FlagDescription
-y, --yesUse defaults for unspecified options, skip all prompts
-h, --helpShow help message

Supported Languages

The --lang flag accepts any of the following language codes:

CodeLanguage
enEnglish
jaJapanese
zh-cnChinese (Simplified)
zh-twChinese (Traditional)
koKorean
esSpanish
frFrench
deGerman
ptPortuguese

--lang selects the primary locale used for root pages (/docs/...). Pass any number of additional locale codes with --additional-langs, for example --lang en --additional-langs ja,de; each creates a src/content/docs-<code>/ tree and a /<code>/docs/... route. The order is preserved in generated config and in the language switcher, whose labels come from the configured locale map rather than hard-coded JP/JA values.

Omitting --additional-langs, passing a blank interactive value, or omitting additionalLangs in a preset creates a single-locale project. A legacy preset or API call that supplies only i18n: true retains compatibility inference: ja is inferred for primary en, and en for any other primary code. Explicit locale codes are lowercased and validated for safe path/URL use; invalid codes, separators, traversal segments, duplicates, and the primary code are rejected before files are written.

The ja tree receives Japanese starter prose. Every other arbitrary additional locale receives English placeholder starter prose that should be translated before publishing. Built-in UI translations resolve as requested locale → configured default locale → package English → raw UI-string key.

Examples

Interactive mode

pnpm create zudo-doc

Non-interactive with all defaults

pnpm create zudo-doc my-docs --yes

Scaffolding into a subdirectory

pnpm create zudo-doc sub/ref-doc --yes

Creates sub/ref-doc/ with the package name ref-doc. Useful for generating several projects side by side under one scratch directory — for example to diff two template versions against each other.

Japanese site, single dark scheme

pnpm create zudo-doc my-docs --lang ja --scheme "Default Dark" --no-i18n --pm pnpm --install

Light/dark mode with an explicit default mode

pnpm create zudo-doc my-docs \
  --color-scheme-mode light-dark \
  --default-mode light \
  --no-respect-system-preference \
  --yes

Using a preset file

Generate a preset JSON from the Setup Preset Generator, save it to a file, then pass it to the CLI:

pnpm create zudo-doc --preset setup.json --install

Or pipe JSON directly via stdin:

cat setup.json | pnpm create zudo-doc --preset - --install

CI/automation usage

pnpm create zudo-doc my-docs \
  --lang en \
  --scheme "Default Dark" \
  --no-i18n \
  --search \
  --no-claude-resources \
  --no-codex-resources \
  --pm pnpm \
  --install \
  --yes

Programmatic API

The package also exports a programmatic API. The options object accepts the same fields as the JSON preset, plus install and git options. The programmatic API defaults both to false. There is no destination option — the destination path is a CLI-level concept, so here projectName doubles as the directory name, resolved against the current working directory.

import { createZudoDoc } from "create-zudo-doc";

await createZudoDoc({
  projectName: "my-docs",
  defaultLang: "en",
  colorSchemeMode: "light-dark",
  lightScheme: "Default Light",
  darkScheme: "Default Dark",
  defaultMode: "dark",
  respectPrefersColorScheme: true,
  themePack: "foundry",
  features: [
    "search",
    "sidebarFilter",
    "sidebarResizer",
    "sidebarToggle",
    "tocToggle",
    "docHistory",
    "footerCopyright",
  ],
  cjkFriendly: false,
  minifyHtml: true,
  headerRightItems: [
    { type: "component", component: "theme-toggle" },
    { type: "component", component: "github-link" },
  ],
  metaTags: {
    description: true,
    ogImage: "/img/ogp.png",
  },
  packageManager: "pnpm",
  install: true,
  git: true,
});

headerRightItems (see the Header Right Items guide) and metaTags (see Configuration) accept the same shapes as the JSON preset fields of the same name, and are validated with the same rules — an unknown headerRightItems component/trigger name or an invalid metaTags sub-field type throws before scaffolding starts.

Revision History

Takeshi TakatsudoCreated: 2026-03-15T19:02:13+09:00Updated: 2026-09-07T12:42:46+09:00

AI Assistant

Ask a question about the documentation.

Preview theme

Loading theme previews…