l-generator-cli-tester
Test a single create-zudo-doc CLI generation pattern. Scaffolds a project, builds it, starts dev server, and verifies expected features. Use when testing generator output for a specific pattern.
Generator CLI Pattern Tester
Test a single create-zudo-doc CLI generation pattern by scaffolding a project, building it, running the dev server briefly, and verifying the expected files and settings.
Usage
/l-generator-cli-tester <pattern>
/l-generator-cli-tester <pattern> --headless Where <pattern> is one of the test patterns listed below.
Options
--headless— After standard checks, also run headless browser verification using/to confirm pages actually render (Step 8.5). Without this flag, headless checks are skipped.headless- browser
Test Patterns
| Pattern | Description |
|---|---|
barebone | Everything OFF — the locked 12-file minimal manifest |
search | Only search enabled |
i18n | Only i18n enabled |
sidebar-filter | Only sidebar filter enabled |
claude-resources | Only claude resources enabled |
design-token-panel | Only design token panel enabled (uses --design-token-panel CLI flag) |
light-dark | Light-dark color mode |
lang-ja | Japanese as default language |
all-features | Everything ON (except tauri/tauriDev — Rust toolchain, out of scope for this smoke pattern) |
Architecture context (read before verifying files)
Since the minimal-scaffold cutover (epic zudolab/zudo-doc#2651), a generated project is: one config file (zfb.config.ts, zudoDoc({...only fields you
chose})) + markdown content + a handful of unavoidable root files. Everything else — layout, chrome, islands, default @theme tokens, even the doc ROUTES themselves — ships from @takazudo/zudo-doc in node_modules.
Consequence for this skill: almost every feature is now a pure zfb.config.ts field, not a file. Only 5 features ship or mutate real files:
| Feature | What it touches |
|---|---|
i18n | Adds pages/[locale]/docs/[[...slug]].tsx + src/ |
tagGovernance | Adds one explicit src/ module consumed by zfb and passed to the package-owned audit/suggest bins with --config |
docHistory | (postProcess) patches the doc-route stub(s) in place to thread the real DocHistory component — no new file |
designTokenPanel | (postProcess) inserts one @import "@takazudo/ line into src/ — no new file |
tauri / tauriDev | Ship src-tauri/** / src-tauri-dev/** Rust project shells |
skillSymlinker | Copies scripts/ |
claudeSkills | Copies .claude/skills/{zudo-doc-design-system,zudo-doc-translate,zudo-doc-version-bump}/** from the monorepo |
claudeSkillsWriting | Copies . (scaffold.ts, from templates/) |
changelog (scaffold.ts, not a feature module) | Adds src/ (+ locale mirror if i18n is also on) |
Every other feature (search, sidebarFilter, sidebarResizer, sidebarToggle, claudeResources, versioning, bodyFootUtil, llmsTxt, docTags, footerNavGroup, footerCopyright, footerTaglist, imageEnlarge, dynamicPageTransition, noindex) changes ONLY the emitted zfb.config.ts fields (or, for search, the generated package.json devDependencies) — verify those with a content check, not a file-presence check.
Note on file extensions: all components are
.tsx— there are no.astrofiles anywhere in the generated project or the package it depends on.
Step 0: Build the CLI
Before running any test, set REPO_ROOT and build the CLI:
REPO_ROOT=$(git rev-parse --show-toplevel)
cd packages/create-zudo-doc && pnpm buildIf the build fails, stop and report the error.
Step 1: Create Temp Directory
mkdir -p __inbox/generator-test-<pattern>Step 2: Run the Generator
Set REPO_ROOT to the repository root (absolute path). Run the generator from within the temp directory. Always use --no-install to handle installation separately.
CLI Commands per Pattern
barebone — every flag with a default: true in constants.ts (search, sidebarFilter, imageEnlarge, dynamicPageTransition, footerCopyright) must be EXPLICITLY turned off, or --yes fills it in as ON:
cd __inbox/generator-test-barebone && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --no-sidebar-filter --no-i18n --no-claude-resources \
--no-image-enlarge --no-dynamic-page-transition --no-footer-copyright --no-tag-governance \
--color-scheme-mode single --scheme "Default Dark" --no-installThis exact invocation is the one verified against the locked 12-file manifest in
packages/(create- zudo- doc/ src/ _ _ tests_ _ / scaffold. test. ts BAREBONE_MANIFEST) — do not drop--no-dynamic-page-transition,--no-image-enlarge, or--no-footer-copyright; all three default totrueand, while they don't add any files, dropping them would emit extra fields intozfb.config.tsand defeat the "everything OFF" premise of this pattern.
search:
cd __inbox/generator-test-search && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--search --no-sidebar-filter --no-i18n --no-claude-resources \
--color-scheme-mode single --scheme "Default Dark" --no-installi18n:
cd __inbox/generator-test-i18n && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --no-sidebar-filter --i18n --no-claude-resources \
--color-scheme-mode single --scheme "Default Dark" --no-installsidebar-filter:
cd __inbox/generator-test-sidebar-filter && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --sidebar-filter --no-i18n --no-claude-resources \
--color-scheme-mode single --scheme "Default Dark" --no-installNote:
sidebarFilterhas NO backing file and NOZudoDocConfigfield — the filtering UI is entirely package-owned (sidebar-treeisland ships from@takazudo/zudo-doc) and always includes filtering.--no-sidebar-filter/--sidebar-filtercurrently has zero structural effect on the generated project. This test only verifies the flag does not cause a CLI error.
claude-resources:
cd __inbox/generator-test-claude-resources && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --no-sidebar-filter --no-i18n --claude-resources \
--color-scheme-mode single --scheme "Default Dark" --no-installdesign-token-panel:
cd __inbox/generator-test-design-token-panel && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --no-sidebar-filter --no-i18n --no-claude-resources \
--no-image-enlarge --no-tag-governance --design-token-panel \
--color-scheme-mode single --scheme "Default Dark" --no-installlight-dark:
cd __inbox/generator-test-light-dark && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --no-sidebar-filter --no-i18n --no-claude-resources \
--color-scheme-mode light-dark --light-scheme "Default Light" --dark-scheme "Default Dark" \
--default-mode light --no-install
--default-mode lightis deliberate, not optional:"Default Light"/"Default Dark"/defaultMode: "dark"/respectPrefersColorScheme: trueis the exactZudoDocConfigpackage default forcolorMode(seeDEFAULT_SETTINGS.colorModeinpackages/, mirrored byzudo- doc/ src/ config. ts DEFAULT_MIRRORinzfb-config-gen.ts). Diff-from-defaults (locked #2653 Decision 2, verified byzfb-config-gen.test.ts's "packageDefaultChoices resolves colorMode/colorScheme to the exact default" case) correctly OMITScolorModewhen every sub-field matches the default — dropping--default-mode lighthere silently degenerates this pattern into a no-op that never demonstrates thecolorModeobject at all (found empirically during the #2667 final-confirm gate).
lang-ja:
cd __inbox/generator-test-lang-ja && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--no-search --no-sidebar-filter --no-i18n --no-claude-resources \
--lang ja --color-scheme-mode single --scheme "Default Dark" --no-installall-features (mirrors scaffold.test.ts's ALL_FEATURES minus tauri/tauriDev):
cd __inbox/generator-test-all-features && \
node $REPO_ROOT/packages/create-zudo-doc/dist/index.js test-project --yes \
--i18n --search --sidebar-filter --claude-resources --claude-skills \
--claude-skills-writing \
--design-token-panel --sidebar-resizer --sidebar-toggle --versioning \
--doc-history --body-foot-util --llms-txt --skill-symlinker \
--footer-nav-group --image-enlarge --footer-copyright --changelog \
--tag-governance --doc-tags --footer-taglist \
--color-scheme-mode light-dark --light-scheme "Default Light" \
--dark-scheme "Default Dark" --default-mode light \
--github-url "https://github.com/example/test-project" --no-install
--default-mode light(notdark): same reason as the light-dark pattern's callout above —Default Light/Default Dark/dark/respectPrefersColorScheme: trueis the exact package default, and diff-from-defaults omitscolorModeentirely when every sub-field matches it. Usingdarkhere would silently dropcolorModefrom this pattern'szfb.config.tstoo (found empirically during the #2667 final-confirm gate).
Step 3: Install Dependencies
cd __inbox/generator-test-<pattern>/test-project && pnpm installIf installation fails, report the error and stop.
Step 4: Build
cd __inbox/generator-test-<pattern>/test-project && pnpm buildIf the build fails, report the error and stop.
Step 5: Dev Server Smoke Test
Start the dev server, wait for startup, check it didn't crash, then kill it:
cd __inbox/generator-test-<pattern>/test-project && \
timeout 15 pnpm dev 2>&1 &
DEV_PID=$!
sleep 8
if kill -0 $DEV_PID 2>/dev/null; then
echo "DEV_SERVER: OK — process still running"
kill $DEV_PID 2>/dev/null
wait $DEV_PID 2>/dev/null
else
wait $DEV_PID
EXIT_CODE=$?
echo "DEV_SERVER: FAILED — process exited with code $EXIT_CODE"
fiIf the dev server crashed, report the error.
Step 6: Verify Files
Check that expected files exist or don't exist in __inbox/generator-test-<pattern>/test-project/. Use test -e <path> for each row.
Baseline — PRESENT in every pattern (the locked 12-file manifest)
| File | Expected |
|---|---|
.gitignore | PRESENT |
.npmrc | PRESENT |
CLAUDE.md | PRESENT |
package.json | PRESENT |
zfb.config.ts | PRESENT |
tsconfig.json | PRESENT |
pages/ | PRESENT |
pages/docs/[[...slug]].tsx | PRESENT |
src/ | PRESENT |
src/ | PRESENT |
src/ | PRESENT |
src/ | PRESENT |
Baseline — ABSENT in every pattern (never-resurrected; see scaffold.test.ts's NEVER_RESURRECTED for the full 52-entry table this condenses)
| File / directory | Expected |
|---|---|
pages/lib/ | ABSENT (whole directory) |
pages/ | ABSENT |
pages/, pages/, pages/, pages/ | ABSENT (package-injected routes, never emitted as files) |
src/components/ | ABSENT (whole directory) |
src/utils/ | ABSENT (whole directory) |
src/types/ | ABSENT (whole directory) |
src/config/ | ABSENT (whole directory) — except when tagGovernance is selected (see below) |
zfb-shim.d.ts | ABSENT |
.htmlvalidate.json | ABSENT |
.zfb/ | ABSENT (whole directory) |
.zudo-doc.json | ABSENT (lazy-created on first zudo-doc eject, never seeded) |
scripts/ | ABSENT |
Confirm the 5 directories above (pages/lib, src/components, src/utils, src/types, src/config) don't exist AT ALL for barebone — test -d <path> should fail for each, not just the specific files listed.
Per-pattern deltas (on top of the baseline above)
barebone — baseline only, nothing added:
| File | Expected |
|---|---|
| (all baseline PRESENT rows) | PRESENT |
| (all baseline ABSENT rows) | ABSENT |
zfb.config.ts content | Only colorMode: false, siteName, headerNav, headerRightItems fields (every other field matches its default and is omitted — diff-from-defaults) |
search:
| File | Expected |
|---|---|
| (baseline unchanged — search has no file footprint) | — |
package.json devDependencies["pagefind"] | PRESENT |
package.json dependencies["minisearch"] | PRESENT |
zfb.config.ts headerRightItems | contains { type: "component", component: "search" } |
i18n:
| File | Expected |
|---|---|
pages/[locale]/docs/[[...slug]].tsx | PRESENT |
src/ | PRESENT |
src/ | PRESENT |
src/ | PRESENT |
pages/ | ABSENT (old home-route template, never resurrected — package-injected now) |
zfb.config.ts locales | contains a ja entry with dir: |
sidebar-filter:
| File | Expected |
|---|---|
| (baseline unchanged — sidebarFilter has no file footprint and no settings field) | — |
claude-resources:
| File | Expected |
|---|---|
| (baseline unchanged — claudeResources has no file footprint) | — |
zfb.config.ts claudeResources | { claudeDir: ".claude" } |
zfb.config.ts defaultLocaleOnlyPrefixes | contains the 4 / prefixes |
zfb.config.ts headerNav | contains a { label: "Claude", path: "/docs/claude", categoryMatch: "claude" } entry |
design-token-panel:
| File | Expected |
|---|---|
| (baseline unchanged — no new file) | — |
src/ | contains @import "@takazudo/ right after the @layer zd-preflight, zd-flow; line |
zfb.config.ts designTokenPanel | true |
package.json dependencies["@takazudo/zdtp"] | PRESENT (unconditional dep regardless of this feature — verify it's there even in barebone too) |
light-dark:
| File | Expected |
|---|---|
| (baseline unchanged) | — |
zfb.config.ts colorMode | an object: { defaultMode: "light", lightScheme: "Default Light", darkScheme: "Default Dark", respectPrefersColorScheme: true } (note the non-default defaultMode: "light" — see the CLI command's callout above) |
lang-ja:
| File | Expected |
|---|---|
src/ | ABSENT (this pattern sets the DEFAULT language to ja, i18n is off — content stays in src/, just written in Japanese) |
zfb.config.ts defaultLocale | "ja" |
all-features:
| File | Expected |
|---|---|
pages/[locale]/docs/[[...slug]].tsx | PRESENT |
src/ | PRESENT |
src/ | PRESENT |
src/ | PRESENT (i18n is also on) |
scripts/ | ABSENT (the package-owned bin is the only current CLI) |
scripts/ | ABSENT (the package-owned bin is the only current CLI) |
src/ | PRESENT (explicit TagCliConfig + zfb vocabulary module) |
src/ | ABSENT |
scripts/ | PRESENT (skillSymlinker) |
. | PRESENT (claudeSkills) |
. | PRESENT (claudeSkills) |
. | PRESENT (claudeSkills) |
. | PRESENT (claudeSkillsWriting) |
src-tauri/ | ABSENT (tauri excluded from this pattern) |
pages/ | ABSENT (docTags routes are package-injected, never a file — even with docTags: true) |
src/components/ | ABSENT (still true even with every feature on) |
Step 7: Verify Settings
There is no src/ to read in a fresh scaffold. Read _ instead — it is the ONE config file, a defineConfig(zudoDoc({...})) call with only diff-from-default fields.
zfb.config.ts Expectations per Pattern
barebone:
colorMode: falseNo
localesfield (omitted — matches the{}default)No
imageEnlarge/dynamicPageTransitionfield (both explicitly set to their defaults via the CLI flags, so diff-from-defaults omits them)No
tagGovernance/tagVocabularyfield (both off, matching defaults)No
designTokenPanelfield (off, matching default)No
claudeResourcesfield (off, matching default)
search:
headerRightItemsincludes{ type: "component", component: "search" }No
colorModefield (single-scheme mode, matches defaultfalse)
i18n:
localescontains ajaentry withdir:"src/ content/ docs- ja"
sidebar-filter:
sidebarFilteritself sets no field, but this pattern's CLI command doesn't turn offimageEnlarge/dynamicPageTransition/footerCopyright(all default totrue), so — unlike barebone, which explicitly disables all three — the generatedzfb.config.tsadditionally hasimageEnlarge: true,dynamicPageTransition: true, and afooter: { copyright: "..." }field
claude-resources:
claudeResources: { claudeDir: ".claude" }
design-token-panel:
designTokenPanel: true
light-dark:
colorModeis an object withdefaultMode: "light"(non-default — required for the field to emit at all, see the CLI command's callout),lightScheme: "Default Light",darkScheme: "Default Dark"
lang-ja:
defaultLocale: "ja"— this is the authoritative field; there is no more separatesrc/deriving it at runtime,config/ i18n. ts @takazudo/zudo-doc's own i18n module readssettings.defaultLocaledirectly
all-features:
colorModeis an object (light-dark mode,defaultMode: "light"— non-default, required to emit)localescontains ajaentryclaudeResources: { claudeDir: ".claude" }designTokenPanel: truedocTags: truetagGovernance: "warn",tagVocabulary: true,tagVocabularyEntries: tagVocabulary(raw import reference)versions: []footeris an object withlinks,copyright, andtaglist
Step 8: Compare Against Showcase
For the feature being tested, briefly compare the generated project's zfb.config.ts field against the equivalent field in this repo's own src/ (spread into the showcase's zfb.config.ts). This is a sanity check that the generator's emitted VALUE shape (not file layout — the showcase legitimately keeps more real files than a fresh scaffold, see src/ and packages/) matches what the showcase demonstrates for the same feature.
This is a sanity check, not a full diff. Focus on the feature under test.
Step 8.5: Headless Browser Check (only with --headless)
Skip this step unless --headless was passed.
Start the dev server and use / (Tier 1: headless-check.js) to verify pages actually render in a browser.
8.5a. Start dev server
cd __inbox/generator-test-<pattern>/test-project
pnpm dev --port 14350 &
DEV_PID=$!
sleep 68.5b. Check pages with headless browser
Check the index page and a docs page:
HC=~/.claude/skills/headless-browser/scripts/headless-check.js
node $HC --url "http://localhost:14350/" --screenshot viewport --no-block-resources
node $HC --url "http://localhost:14350/docs/getting-started" --screenshot viewport --no-block-resourcesFor i18n and all-features patterns, also check the Japanese page:
node $HC --url "http://localhost:14350/ja/docs/getting-started" --screenshot viewport --no-block-resources8.5c. Verify results
All pages should return
statusCode: 200pageErrorsshould be empty (no JS errors)networkErrors.failedRequests— ignorenet::ERR_ABORTED(Vite HMR re-optimization, normal in dev). Flag any other failures.Read the screenshots with the Read tool and visually confirm:
search: search icon (magnifying glass) visible in header
i18n: "EN / JA" language switcher in header
light-dark: theme toggle icon in header
design-token-panel: design token icon in header
claude-resources: page renders without errors
all-features: all icons present (search, theme toggle, language switcher, color tweak)
barebone: no extra icons in header (no search, no theme toggle, no language switcher)
lang-ja: Japanese content ("ようこそ" title)
8.5d. Kill dev server
kill $DEV_PID 2>/dev/null; wait $DEV_PID 2>/dev/nullStep 9: Clean Up
rm -rf ./__inbox/generator-test-<pattern>Always use relative path with . prefix for cleanup.
Step 10: Report Results
Provide a clear pass/fail report:
## Pattern: <pattern>
### Scaffold: PASS/FAIL
### Install: PASS/FAIL
### Build: PASS/FAIL
### Dev Server: PASS/FAIL
### File Verification: PASS/FAIL
- [list any unexpected files present/absent]
### zfb.config.ts Verification: PASS/FAIL
- [list any mismatches]
### Showcase Comparison: PASS/FAIL
- [notes]
### Headless Browser: PASS/FAIL/SKIPPED
- [only if --headless was passed]
### Overall: PASS/FAIL Important Notes
Always
cdback to the repo root between major steps (use absolute paths)The
--yesflag auto-fills all unspecified options with defaults. Feature defaults with--yes: search=true, sidebarFilter=true, imageEnlarge=true, dynamicPageTransition=true, footerCopyright=true, tagGovernance=false, i18n=false, claudeResources=false, designTokenPanel=false (all other features false)Use
--no-installwith CLI to prevent auto-install, then install manually for better error visibilitysidebarFilterhas zero structural effect in the minimal manifest (no TODO, no strip step needed — it never had a file or field to remove)The dev server smoke test uses
pnpm dev(generated projects have a singledevscript)If any step fails, still report all steps attempted before stopping
The
--headlessflag enables Step 8.5 (headless browser visual check). Without it, only process-level checks are performedpackages/is the authoritative, CI-enforced version of these tables — if this skill and that test file ever disagree, the test file wins; update this skill to matchcreate- zudo- doc/ src/ _ _ tests_ _ / scaffold. test. ts