This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
strong-mode is an ultra-strict TypeScript CLI tool focused on strong-mode defaults for AI-assisted coding. It retrofits existing projects with strict TypeScript configurations and quality gates via the apply command.
Published as strong-mode on npm, invoked via npx strong-mode.
This is an npm workspace monorepo with two key packages:
packages/scaffold-ultra/template/: Source of truth for all scaffold files (tsconfig, eslint, vitest, etc.)packages/strong-mode/: Published CLI package that contains the generator logic
Critical sync mechanism: scripts/sync-template.mjs copies scaffold-ultra/template/ → strong-mode/template/ for npm publishing. This runs automatically before build/pack via prebuild/prepack hooks.
npm install # Install all workspace dependencies
npm run sync:template # Manually sync template (auto-runs before build)
npm run check # Run typecheck, lint, test in strong-mode
npm run build # Build the CLI packagenpm run build -w strong-mode
node packages/strong-mode/dist/cli.js --dry-run --yesnpx vitest run src/args.test.ts -w strong-mode
npx vitest run src/apply/patchers.test.ts -w strong-modeScripts added to target projects by strong-mode:
check: Fast gate (typecheck + lint + format:check + dead-code)quality: Full gate (check + test:coverage + deps:graph + deps:cycles + audit)test: Run tests with vitestbuild: Compile TypeScriptdead-code: Find unused exports with knipdeps:graph: Validate dependencies with dependency-cruiserdeps:cycles: Detect circular dependencies with madge
src/cli.ts: Main entry point. Calls parseCliArgs then runApplyCommand.
src/apply-command.ts: Applies template to existing project — detects conflicts, patches package.json, creates backups.
parseCliArgs(argv) returns ApplyCliOptions. Supports --flag value and --flag=value formats. Rejects unknown flags and positional arguments.
src/template.ts: Core template operations. Files use __PROJECT_NAME__ as a token placeholder, replaced with the actual project name during copy/detect. Template validation uses sanitizePackageName() and assertValidPackageName().
10 managed template files (defined in src/apply/constants.ts): tsconfig.json, eslint.config.mjs, prettier.config.mjs, vitest.config.ts, knip.config.ts, depcruise.config.cjs, lefthook.yml, scripts/run-package-manager.sh, .gitignore, src/env.ts.
The apply command (for existing projects) uses a detect → plan → execute pipeline:
src/apply/detect.ts: Reads target project state — existing package.json, which managed files already exist, and their current contentsrc/apply/plan.ts: Splits managed files intofilesToCreate(new) andconflictingFiles(existing), builds package.json merge plansrc/apply/patchers.ts: Generates package.json merge plan — adds template dependencies/scripts without removing existing ones. Special handling forpreparescript (appendslefthook installif missing)src/apply/execute.ts: Executes the plan with dry-run, backup ({file}.strong-mode-backup.{ISO-timestamp}), and force options. Prompts for conflict resolution (overwrite/skip/diff preview)
Detection order: lockfile presence (package-lock.json → npm, pnpm-lock.yaml → pnpm, yarn.lock → yarn, bun.lockb → bun) → npm_config_user_agent env var → defaults to npm.
tsup bundles src/cli.ts → dist/cli.js as ESM with #!/usr/bin/env node shebang, targeting Node 22. Published package includes dist/ and template/ directories with one bin entry: strong-mode.
Generated projects enforce extreme type safety:
tsconfig.json flags: noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitOverride, skipLibCheck: false
ESLint anti-escape rules (see packages/scaffold-ultra/template/eslint.config.mjs):
no-explicit-any,no-unsafe-*as errorsban-ts-commentwith 10-char minimum descriptions- No chained assertions (
value as unknown as T) process.envaccess restricted tosrc/env.ts- Complexity limits: max 10 cyclomatic, max 3 depth, max 4 params
Runtime validation pattern: All environment variables must be validated through src/env.ts using Zod with .safeParse() on unknown input.
- Cross-platform: Uses
cross-spawnfor command execution with shell enabled on Windows - Interactive prompts: Uses
@clack/promptswithexitOnCancelwrapper (src/ui.ts) - CLI flags:
--yes,--dry-run,--force,--backup,--install/--no-install,--check/--no-check,--pm=<manager>,--cwd=<path> - Template sync: MUST run
sync:templatebefore building to ensure CLI bundles latest scaffold - Node requirement: Requires Node.js >= 22
- ESLint config (CLI project itself): enforces
explicit-function-return-type,no-floating-promises,consistent-type-imports; relaxes return type requirement in test files
- Uses Vitest with globals mode (
describe,it,expectavailable without imports) - Test files:
*.test.tscolocated inpackages/strong-mode/src/ - Key test suites:
args.test.ts(flag parsing),template.test.ts(name validation),apply/patchers.test.ts(package.json merge),apply/plan.test.ts(file splitting)
- Never modify
packages/scaffold-ultra/template/without runningnpm run sync:template - Package.json patching must preserve user's existing fields (see
patchers.ts) - ESLint config enforces kebab-case filenames, but allows
.test.tsand config files - All TypeScript code must have explicit return types and pass strict type checking