create-frontstack
CLAUDE.md103 starsChanged 18 days agoMITView on GitHub
create-frontstack is a scaffolder published to npm. Running it produces a React + TypeScript + Vite frontend repo (TanStack Router/Query, Zustand, shadcn/ui, Docker, agent skills, MCP). This is the single most important thing to understand before editing anything. The repo contains two separate codebases with two separate toolchains: They are mutually excluded on purpose. Root pnpm lint / pnpm format / pnpm test never touch template/. Nothing under template/ is linted, formatted, typechecked, or tested by any root command —…
- Installs packages
In this repository23 other files it gives its agents.
- template/CLAUDE.mdCLAUDE.md
- ask-mattSkill
- codebase-designSkill
- code-reviewSkill
- diagnosing-bugsSkill
- domain-modelingSkill
- grillingSkill
- grill-meSkill
- grill-with-docsSkill
- handoffSkill
- implementSkill
- improve-codebase-architectureSkill
- prototypeSkill
- researchSkill
- resolving-merge-conflictsSkill
- setup-matt-pocock-skillsSkill
- tddSkill
- teachSkill
- to-specSkill
- to-ticketsSkill
- triageSkill
- wayfinderSkill
- writing-great-skillsSkill
The file
Show its rules and text
We split this CLAUDE.md into the rules it gives an agent
15 rules. About 0 are common, 14 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈14
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 138
If multiple interpretations exist, present them - don't pick silently.
- Distinctive: line 139
If a simpler approach exists, say so.
- Distinctive: line 140
If something is unclear, stop.
- Distinctive: line 146
No features beyond what was asked.
- Distinctive: line 147
No abstractions for single-use code.
- Distinctive: line 149
No error handling for impossible scenarios.
- Distinctive: line 150
If you write 200 lines and it could be 50, rewrite it.
- Distinctive: line 152
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
- Distinctive: line 158
When editing existing code:
- Distinctive: line 160
Don't "improve" adjacent code, comments, or formatting.
- Distinctive: line 161
Don't refactor things that aren't broken.
- Distinctive: line 163
If you notice unrelated dead code, mention it - don't delete it.
- Distinctive: line 165
When your changes create orphans:
- Very specific: line 167
Remove imports/variables/functions that YOUR changes made unused.
- Distinctive: line 168
Don't remove pre-existing dead code unless asked.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this repo is
create-frontstack is a scaffolder published to npm. Running it produces a React + TypeScript + Vite frontend repo (TanStack Router/Query, Zustand, shadcn/ui, Docker, agent skills, MCP).
The two-codebase split
This is the single most important thing to understand before editing anything.
The repo contains two separate codebases with two separate toolchains:
| Packager | Template | |
| Code | bin/index.js, test/** | template/** |
| Runtime | Node built-ins only, zero runtime deps | React 19 / TS 6 / Vite 8 |
| ESLint | eslint.config.js (ignores template/**) | template/eslint.config.js |
| Prettier | prettier.config.js + .prettierignore (ignores template/) | template/prettier.config.js + template/.prettierignore |
| Vitest | vitest.config.js (include: test/**, exclude: template/**) | template/vite.config.ts (include: src/**) |
They are mutually excluded on purpose. Root pnpm lint / pnpm format / pnpm test never touch template/. Nothing under template/ is linted, formatted, typechecked, or tested by any root command — see "Working on the template" for how to do that.
Commands
pnpm install
pnpm test # packager unit tests (vitest run)
pnpm test:watch
pnpm lint # eslint (bin/ + test/ only)
pnpm check # lint + test
pnpm format # prettier — root files only, NOT template/Single test file / single test:
pnpm vitest run test/cli.test.js
pnpm vitest run -t 'parses a positional name'The generation gate (see below) is opt-in and skipped by default:
RUN_GENERATION_GATE=1 pnpm vitest run test/generation-gate.test.jsPublish safety — the packager ships only files: ["bin", "template"], so verify nothing was dropped:
npm pack --dry-runCI (.github/workflows/ci.yml) runs three jobs: packager lint+test, the generation gate with RUN_GENERATION_GATE=1, and npm pack --dry-run.
Design decisions
Settled during a validation spike. The planning tree that recorded them has been removed, so the reasoning lives here — treat these as constraints, not preferences.
- The CLI has zero runtime dependencies.
npxmust be instant and the supply-chain surface minimal, so arg parsing and prompts are hand-rolled onnode:readlineinstead of pullingcommander/prompts. The cost is bespoke code; it is kept in small exported helpers that are unit-tested. - Copy, don't template. The only substitution needed is
{{PROJECT_NAME}}plus three dotfile renames, so files are copied byte-for-byte rather than run through a templating engine — that keeps binaries safe. - Symlinks are materialized after the copy, never stored in the template, because npm publish and cross-platform git mangle or drop them.
- MCP is fixed and unconditional; skills are the only opt-in surface. Keeping the two concerns separate keeps the wizard simple.
- The generated project is the unit under test — see the generation gate below.
Non-goals, deliberately: no backend/database/auth in the template, no monorepo, no per-MCP-server selection, no router/state alternatives, no auto-bumping of pins.
Version pins are deliberate: exact versions, no ^ ranges except @types/node. Notably TypeScript is pinned to 6.0.3, not 7 — typescript-eslint@8 exits non-zero under TS 7, which would mean no linting at all, not merely degraded linting. React bumps need template/src/test/render.test.tsx to pass — react@19.2.8 changed createRoot().render() so the initial commit was no longer flushed synchronously, and under Vitest + @testing-library/react every component test rendered an empty container (no error, just missing elements); 19.3.1 with @testing-library/react@16.3.3 renders correctly again. If that guard goes red after a React bump, hold React back. Bumping pins means re-running the generation gate.
How the CLI works (bin/index.js)
Zero runtime dependencies by design — hand-rolled arg parsing and node:readline prompts, so npx is instant. Pure helpers are exported for unit tests; the entry point runs only when the file is invoked directly, not when imported.
Three transformations happen during the copy, all in copyDir:
- Dotfile restore. npm mangles dotfiles on publish, so the template stores them underscore-prefixed and
DOTFILE_MAPrenames them back:_gitignore→.gitignore,_npmrc→.npmrc,_nvmrc→.nvmrc. Any new npm-mangled dotfile must be added there. (Other dotfiles —.mcp.json,.dockerignore,.prettierignore— survive publish and are stored as-is.) - Placeholder substitution.
{{PROJECT_NAME}}is the only placeholder, substituted in every copied file. - Skills wiring.
template/skills/ships as real files because symlinks don't survive npm publish.skills/is skipped in the main copy and only copied when skills are enabled; thenlinkSkillsmaterializes a relative../skillssymlink into.claude/and/or.agents/, falling back symlink → Windows junction → plain copy, warning on the two fallbacks.
MCP (template/.mcp.json, four fixed servers) is always copied and never selectable; skills are the only opt-in agent surface.
AGENTS.md mirrors CLAUDE.md — also materialized after the copy, never stored as a symlink in the template. linkAgents writes a relative CLAUDE.md symlink at the generated project root, falling back to a plain file copy on Windows, so every agent client (Claude Code, Cursor, …) reads the same instructions. It is always created, unconditionally.
Interactive vs non-interactive is decided by process.stdin.isTTY. Non-interactive requires a positional name and always uses pnpm — that is what makes CI deterministic.
Working on the template
template/ is not a runnable project in place: no node_modules, dotfiles are underscore-prefixed, and {{PROJECT_NAME}} is unsubstituted. To exercise it, generate a project:
cd $(mktemp -d)
node /path/to/create-frontstack/bin/index.js myapp --no-skills
cd myapp && pnpm install && pnpm checkThe generation gate is the real verification: the generated project is the unit under test, because the packager's own lint/test prove nothing about the template. test/generation-gate.test.js scaffolds into a temp dir and runs pnpm install + pnpm check + pnpm format:check + pnpm build there. Run it after any template change.
Note pnpm check in the generated project is typecheck + lint + test — it does not include format:check, so the gate runs format:check as a separate step.
Formatting template files
template/prettier.config.js loads prettier-plugin-tailwindcss, and Prettier resolves plugins relative to the CWD, not to the config file. Formatting template/ from the repo root fails with Cannot find package 'prettier-plugin-tailwindcss' — and with --write it fails silently, changing nothing. Run Prettier from inside a generated project that has the plugin installed, pointing at the template:
cd /path/to/generated-app
./node_modules/.bin/prettier --config ./prettier.config.js --write /path/to/create-frontstack/templateWithout the plugin, Tailwind class ordering is left unsorted and pnpm format:check still fails in generated projects.
Type-aware linting in the template
template/eslint.config.js applies type-aware rules only to files a tsconfig project actually covers — src/** (tsconfig.app.json) and e2e/** (tsconfig.node.json). Adding a new top-level TS directory means adding it to a tsconfig include and to the eslint files list; the eslint change alone gives no type information. Root config files belong to no project and stay on the untyped baseline.
src/routeTree.gen.ts is generated by the router plugin and must stay excluded from ESLint, Prettier, and coverage. typecheck/build prefix tsr generate so the file exists before tsc runs.
Agent skills in this repo
Two unrelated sets of skills — don't confuse them:
skills/at the repo root — vendored third-party skills (mattpocock/skills), tracked byskills-lock.jsonwith source hashes..claude/skills,.agents/skills, and.codex/skillsare symlinks to it. Not authored here; out of scope for review and edits.template/skills/— the 11 skills shipped to generated projects (add-route,add-query-hook,write-vitest-test, …). These are the documented coding conventions for template code, sotemplate/src/**must obey them: query keys live in anxxxKeysfactory rather than inline arrays, tests are colocated (button.tsx→button.test.tsx), and so on. Template code that contradicts its own SKILL.md is a defect.
CLAUDE.md
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
1. Think Before Coding
Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
2. Simplicity First
Minimum code that solves the problem. Nothing speculative.
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
3. Surgical Changes
Touch only what you must. Clean up only your own mess.
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
4. Goal-Driven Execution
Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

