--- name: scaffold-cli description: Scaffolds a TypeScript CLI and npm package with the house toolchain, dual tsdown outputs, CLI contracts, changesets, and publishing templates. Use when asked to "scaffold a CLI" or "start an npm package". compatibility: Requires a shell, Git, Node.js, pnpm, and npm registry access. Remote publishing requires the relevant account authentication. --- # Scaffold CLI - **IS:** bootstrapping a brand-new TypeScript CLI or npm package (Node 24, TypeScript 7) from the pinned templates in `references/`, through to a green first CI run and a package npm can publish over OIDC. - **IS NOT:** a Next.js web app (use `scaffold-nextjs`), folder structure or module contracts for an existing codebase (use `codebase-architecture`), auditing an existing CLI's ergonomics (use `dx-audit`), or shipping a release of an existing package (use `autoship`). The templates encode the house toolchain. Substitute project values and verify template APIs against installed package versions; repair proven incompatibilities instead of blindly reproducing them. The toolchain is the opinion: tsdown not tsup, vitest not jest, oxlint and oxfmt via ultracite not eslint or prettier, `node:util` `styleText` not chalk, `@clack/prompts` not ora. Swapping any of them or restructuring the layout produces a repo the templates' notes no longer describe. ## Reference Files | File | Read When | |------|-----------| | `references/scaffold-configs.md` | Step 3: package.json, tsconfig, tsdown, gitignore, license, changeset config, GitHub Actions | | `references/scaffold-source.md` | Steps 4-5: src/cli.ts, src/index.ts, src/types.ts, AGENTS.md, README.md, skills/SKILL.md | | `references/agent-friendly-cli.md` | Step 4, only when a command takes an identifier, path, or URL, or mutates state: input validation, dry-run, confirmation, schema | | `references/post-scaffold.md` | Steps 6-8: post-scaffold commands, the lefthook.yml replacement, validation checklist, GitHub and npm bootstrap, troubleshooting | ## Scaffold Workflow Copy this checklist to track progress: ```text Scaffold progress: - [ ] Step 1: Gather project info - [ ] Step 2: Create directory structure - [ ] Step 3: Generate config files - [ ] Step 4: Generate source files - [ ] Step 5: Generate docs and skill - [ ] Step 6: Run post-scaffold commands - [ ] Step 7: Validate scaffold - [ ] Step 8: Bootstrap GitHub and npm (with the user's go-ahead) ``` ### Step 1: Gather project info Ask only for what the user didn't provide: | Variable | Example | Default | Used in | |----------|---------|---------|---------| | `{{name}}` | `md-tools` | required | package.json name, README title, npm package | | `{{description}}` | `CLI tool to convert content to markdown` | required | package.json, README, SKILL.md | | `{{bin}}` | `md` | same as `{{name}}` | package.json bin field, CLI examples, skills folder | | `{{repo}}` | `acme/md-tools` | required | package.json repository, GitHub repo, npm trusted publisher | | `{{author}}` | `Your Name` | required | package.json, LICENSE | | `{{year}}` | `2026` | current year | LICENSE | `{{repo}}` must be the exact GitHub `owner/name`: npm provenance rejects a publish whose `repository.url` differs from the repo it came from. ### Step 2: Create directory structure ``` {{name}}/ .changeset/ .github/ workflows/ src/ skills/{{bin}}/ ``` ### Step 3: Generate config files Load `references/scaffold-configs.md`. Generate every file there, replacing each `{{placeholder}}`: `package.json`, `tsconfig.json`, `tsdown.config.ts`, `.gitignore`, `LICENSE.md`, `.changeset/config.json`, `.changeset/README.md`, `.github/workflows/ci.yml`, `.github/workflows/npm-publish.yml` ### Step 4: Generate source files Load `references/scaffold-source.md`. Generate: - `src/cli.ts`: Commander entry point with agent-friendly defaults (`--output text|json`, `--no-input`, stdout data / stderr log split, JSON error envelope) - `src/index.ts`: Public API exports - `src/types.ts`: Shared type definitions When a command takes an identifier, path, or URL, or mutates state, also load `references/agent-friendly-cli.md` and copy the matching pinned pattern. Skip it for a CLI with no such command. ### Step 5: Generate docs and skill From the same `references/scaffold-source.md`, generate: - `AGENTS.md`: commands, architecture, gotchas, agent invariants - `README.md`: install, usage, API, agent skill install, license - `skills/{{bin}}/SKILL.md`: agent skill definition Use AGENTS.md directly; do not create a CLAUDE.md wrapper or symlink. ### Step 6: Run post-scaffold commands Load `references/post-scaffold.md`. Run the command sequence in the order given, including the `lefthook.yml` overwrite between `ultracite init` and the first commit. ### Step 7: Validate scaffold Run the validation checklist in `references/post-scaffold.md`. Every item is a command whose output is the evidence; the checklist includes `publint`, `arethetypeswrong`, the hook exercised against real files, and the placeholder sweep. ### Step 8: Bootstrap GitHub and npm For a local scaffold, stop after Step 7. If the user already requested remote setup or publication, carry out that authorized scope. Otherwise present the prepared repository/package identity before asking to create or publish it. Otherwise follow "Bootstrap GitHub and npm" in `references/post-scaffold.md`: create and push the repo, enable Actions-created PRs, publish 0.0.1 once by hand so the package exists, then register the workflow as a trusted publisher. Terminal evidence is a green CI run on the pushed commit and `npm view {{name}} version` printing `0.0.1`. From here every release belongs to `autoship`. ## Dependencies **Runtime:** `@clack/prompts`, `commander` **Development (in the package.json template):** `@changesets/cli`, `@types/node`, `tsdown`, `typescript`, `vitest` **Added by `ultracite init`, never listed by hand:** `ultracite` (pinned exact by init), `oxlint`, `oxfmt`, `lefthook`, plus the `check`, `fix`, and `prepare` scripts. By-hand entries produce duplicate scripts and version skew against what init installs. ## Gotchas - **tsdown emits `.mjs` by default.** With `platform: node` (the default) `fixedExtension` is on, so a config without `outputOptions.entryFileNames: "[name].js"` builds `dist/cli.mjs`, `dist/index.mjs`, and `dist/index.d.mts`, and `bin` and `exports` point at files that do not exist. The `outputOptions` block in the template is what keeps them `.js`; do not trim it as noise. - **No shebang in `src/cli.ts`.** tsdown's `banner` injects `#!/usr/bin/env node`; a source shebang doubles it in `dist/cli.js`. The two build entries stay separate: the CLI entry has the banner and `dts: false`, the library entry has `dts: true` and no banner. - **`"test": "vitest run"` without `--passWithNoTests`** exits 1 on a repo with zero test files, so the first CI run goes red. - **`@changesets/cli@3` pairs with `changesets/action@v2` and the `publish-script:` input.** `@v1` cannot drive changesets v3, and `@v2` given the v1 `publish:` input versions the package and then completes green without publishing. The templates carry the matching pair; do not downgrade one side. - **npm cannot register a trusted publisher for a package that does not exist yet.** The first release run fails `E404` or `ENEEDAUTH` until Step 8's one-time manual `pnpm publish` has created the package and the workflow is registered. That publish happens before any changeset exists, so it is the one manual publish `autoship`'s rules do not forbid. - **Node 22 ships npm 10.9.x; OIDC publishing needs npm 11.5.1 or later.** Node 24 ships npm 11.19, which is why both workflows pin `node-version: 24`. Lowering it to 22 breaks publishing with `ENEEDAUTH`. The release workflow upgrades npm even though the project uses pnpm: `pnpm publish` can hand the upload to the npm CLI on `PATH`, and an older npm there fails OIDC. - **Agent-facing output is a format contract.** Data on stdout, logs and progress on stderr; a stray `console.log` breaks a consumer parsing `--output json`. Never prompt when stdin is not a TTY: honor `--no-input` and take every value as a flag, or the process hangs under a pipe. ## Related Skills - `autoship`: every release after the bootstrap publish: changeset, CI watch, Version Packages PR, publish verification, and diagnosis of a release that did not publish. - `dx-audit`: audit the CLI's flags, errors, and types once real commands exist. - `agents-md`: grow the generated AGENTS.md as the codebase gains structure. - `ghostwriter`: rewrite the README once there is a real usage story to tell. Maintenance only: `evals/evals.json` contains regression scenarios for changes to this skill; it does not load during a user task.