agentleFS
HarryMtvHarryMtv/create-frontstack/CLAUDE.md

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

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
  1. Distinctive:

    If multiple interpretations exist, present them - don't pick silently.

    line 138
  2. Distinctive:

    If a simpler approach exists, say so.

    line 139
  3. Distinctive:

    If something is unclear, stop.

    line 140
  4. Distinctive:

    No features beyond what was asked.

    line 146
  5. Distinctive:

    No abstractions for single-use code.

    line 147
  6. Distinctive:

    No error handling for impossible scenarios.

    line 149
  7. Distinctive:

    If you write 200 lines and it could be 50, rewrite it.

    line 150
  8. Distinctive:

    Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.

    line 152
  9. Distinctive:

    When editing existing code:

    line 158
  10. Distinctive:

    Don't "improve" adjacent code, comments, or formatting.

    line 160
  11. Distinctive:

    Don't refactor things that aren't broken.

    line 161
  12. Distinctive:

    If you notice unrelated dead code, mention it - don't delete it.

    line 163
  13. Distinctive:

    When your changes create orphans:

    line 165
  14. Very specific:

    Remove imports/variables/functions that YOUR changes made unused.

    line 167
  15. Distinctive:

    Don't remove pre-existing dead code unless asked.

    line 168

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

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:

PackagerTemplate
Codebin/index.js, test/**template/**
RuntimeNode built-ins only, zero runtime depsReact 19 / TS 6 / Vite 8
ESLinteslint.config.js (ignores template/**)template/eslint.config.js
Prettierprettier.config.js + .prettierignore (ignores template/)template/prettier.config.js + template/.prettierignore
Vitestvitest.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.js

Publish safety — the packager ships only files: ["bin", "template"], so verify nothing was dropped:

npm pack --dry-run

CI (.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. npx must be instant and the supply-chain surface minimal, so arg parsing and prompts are hand-rolled on node:readline instead of pulling commander/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:

  1. Dotfile restore. npm mangles dotfiles on publish, so the template stores them underscore-prefixed and DOTFILE_MAP renames 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.)
  2. Placeholder substitution. {{PROJECT_NAME}} is the only placeholder, substituted in every copied file.
  3. 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; then linkSkills materializes a relative ../skills symlink 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 check

The 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/template

Without 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 by skills-lock.json with source hashes. .claude/skills, .agents/skills, and .codex/skills are 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, so template/src/** must obey them: query keys live in an xxxKeys factory 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.