@atharvdange/toolchain
@atharvdange/toolchain
A reusable CLI that scaffolds a complete dev toolchain - ESLint, Prettier, Husky, commitlint, and strict TypeScript - into any JS/TS project with one command.
pnpm dlx @atharvdange/toolchain init
Features
- Framework-aware - detects Next.js, Express, React, or plain TS and tailors ESLint rules accordingly
- Monorepo-ready - detects
workspaces/pnpm.workspacesand generates tsconfig project references with per-package eslint overrides - Zero-config - interactive prompts guide you through, or use
--yesto skip - All in one - linter, formatter, hooks, commit linting, and type checking
- Project scaffolding - create new projects from scratch with
scaffoldcommand - Opinionated defaults - strict TypeScript config, sensible ESLint rules, proper monorepo structure
Commands
init - Add toolchain to existing project
# Interactive
pnpm dlx @atharvdange/toolchain init
# Non-interactive (defaults)
pnpm dlx @atharvdange/toolchain init --yes
scaffold - Create a new project
# Create a React project with npm
pnpm dlx @atharvdange/toolchain scaffold my-app --template react --pm npm
# Create a Next.js project with pnpm
pnpm dlx @atharvdange/toolchain scaffold my-app --template nextjs --pm pnpm
# Create a plain TypeScript project
pnpm dlx @atharvdange/toolchain scaffold my-app --template plain
# Create a monorepo with Turborepo
pnpm dlx @atharvdange/toolchain scaffold my-app --template turbo --pm pnpm
# Interactive template selection
pnpm dlx @atharvdange/toolchain scaffold my-app
Available templates
| Template | Description | Source |
|---|---|---|
plain |
Basic TypeScript project | Generated by toolchain |
react |
React 19 with Vite and TypeScript | create-vite |
nextjs |
Next.js 15 with App Router | create-next-app |
express |
Express 5 REST API with TypeScript | Generated by toolchain |
pnpm-monorepo |
pnpm workspaces monorepo with apps and packages | Generated by toolchain |
turbo |
Turborepo monorepo with apps and packages | Generated by toolchain |
Package managers
Use --pm to specify the package manager: npm, pnpm, yarn, or bun.
Note: React and Next.js projects use their official scaffolding tools (
create-viteandcreate-next-app), then the toolchain is added on top.
Note:
pnpm-monorepotemplate requirespnpmand will override--pmif set to a different package manager.
Monorepo structure
Both pnpm-monorepo and turbo templates generate a standard monorepo structure:
my-app/
├── apps/
│ ├── web/ # Web application
│ ├── mobile/ # Mobile application
│ └── api/ # API server
├── packages/
│ ├── shared/ # Shared utilities (logger, formatDate)
│ ├── config/ # Environment configuration (typed)
│ └── db/ # Database client
├── pnpm-workspace.yaml
├── turbo.json # (turbo only)
└── package.json
Each package has its own package.json, tsconfig.json, and source files with workspace dependencies wired up.
What it sets up
| Config | Generated variant |
|---|---|
.editorconfig |
Static |
.prettierrc / .prettierignore |
Static |
commitlint.config.js |
Static |
eslint.config.mjs |
Plain TS / React / Next.js / Express rules, ignores .expo/ |
tsconfig.json |
Strict config with project references (monorepo) |
.husky/pre-commit |
lint-staged + typecheck |
.husky/commit-msg |
commitlint |
package.json |
Scripts, lint-staged config, devDependencies |
TypeScript configuration
The generated tsconfig.json includes strict options:
| Option | Description |
|---|---|
strict |
Enable all strict type-checking options |
noUnusedLocals |
Error on unused local variables |
noUnusedParameters |
Error on unused function parameters |
noFallthroughCasesInSwitch |
Error on fallthrough cases in switch statements |
noUncheckedIndexedAccess |
Add undefined to any untyped index access |
noImplicitOverride |
Require override keyword in derived classes |
exactOptionalPropertyTypes |
Distinguish between undefined and missing props |
noPropertyAccessFromIndexSignature |
Force bracket notation for index signatures |
verbatimModuleSyntax |
Enforce import type for type-only imports |
moduleResolution: bundler |
Modern module resolution for bundlers |
ESLint variants
The generated eslint.config.mjs adapts based on what's in your package.json:
| Detected | Plugins included |
|---|---|
| Plain TS | typescript-eslint, unicorn, perfectionist |
| React | Above + eslint-plugin-react, eslint-plugin-react-hooks |
| Next.js | Above + @next/eslint-plugin-next |
| Express | Same as Plain TS but no-unsafe-* rules relaxed to warn |
Ignored directories: node_modules, dist, .next, build, .expo, .pnpm-store, *.config.*, .env*
For monorepos, also ignores **/packages/*/dist/**.
Detection logic
- Package manager: checks
npm_config_user_agentfirst (set by the package manager that invoked the CLI), then lockfiles (pnpm-lock.yaml→ pnpm,yarn.lock→ yarn,bun.lockb→ bun), thendevEnginesin package.json, else npm - Monorepo: checks for
workspacesin package.json orpnpm.workspaces - Framework: checks
dependencies/devDependenciesfornext,express,react
Troubleshooting
Husky hooks not running after git init
If you delete .git and re-initialize with git init, husky hooks will stop working. Run:
pnpm run prepare
This reconfigures core.hooksPath to point to .husky.
npx fails with EBADDEVENGINES
npx @atharvdange/toolchain init will not work when the package's packageManager is set to pnpm (if you initialized a blank project with pnpm init), causing npm to throw an EBADDEVENGINES error. Use pnpm dlx, yarn dlx, or bunx instead.