pnpm workspace monorepo with TypeScript, Nx build orchestration, Biome formatting/linting, and Vitest testing. Contains CLI tools (OCLIF-based) and libraries in packages/.
# Setup
corepack enable # REQUIRED before pnpm works
pnpm install --ignore-scripts # --ignore-scripts avoids slow native deps (re2, sharp)
# Build & test
pnpm build # Build all packages (Nx-cached)
pnpm test # Test all packages
pnpm nx run <pkg>:<task> # Target one package: pnpm nx run cli:build
pnpm nx affected -t build # Build only packages affected by your changes
pnpm nx affected -t test # Test only affected packages
# Run a single test file or pattern
pnpm vitest <pattern> # e.g. pnpm vitest sort-tsconfig
# Format, lint, policy
pnpm format # Auto-fix formatting (Biome)
pnpm lint:fix # Auto-fix lint issues (Biome)
pnpm run fix:policy # Auto-fix repo policy violations (repopo)
# Pre-commit validation
pnpm run ci:check # format + deps + policies + lint
pnpm run ci # full CI pipeline (check, build, lint, test)Nx uses orchestration targets (e.g. build, test, ci) defined in nx.json that coordinate implementation tasks (e.g. build:compile, test:vitest) defined in each package's package.json scripts. Orchestration targets have no implementation themselves — they only declare dependsOn relationships.
Package-type build pipelines:
- Libraries:
build:compile→build:api→build:docs - CLI tools:
build:compile→build:manifest→build:readme - Doc sites (Astro):
build:site
All orchestration config lives in root nx.json — there are no package-level project.json files.
CLI packages (cli, dill-cli, repopo, sort-tsconfig) use OCLIF:
- Commands live in
src/commands/, compiled toesm/commands/ bin/dev.js— development mode (runs from TypeScript source)bin/run.js— production mode (runs from compiled output)oclif.manifest.jsonand README are auto-generated during build
Vitest with shared base config (config/vitest.config.ts). Test files: test/**/*.test.ts.
| File | Purpose |
|---|---|
nx.json |
Task orchestration, caching, dependency chains |
biome.jsonc |
Formatting and linting rules |
repopo.config.ts |
Repository policy enforcement |
syncpack.config.cjs |
Dependency version synchronization |
config/tsconfig*.json |
Shared TypeScript base configs |
- pnpm only — never use npm or yarn. Version enforced via
packageManagerfield. - Tabs for indentation — not spaces.
- No
.jsfiles — use.mjsor.cjsfor JavaScript. Enforced by repopoNoJsFileExtensionspolicy. - Use
patheinstead ofnode:pathfor cross-platform path operations. - Async file I/O — prefer
fs/promises(e.g.readFile) over sync variants. - No barrel re-exports — import from the source module directly, not through index files.
workspace:^for all internal workspace dependencies.- Sorted configs —
package.jsonandtsconfig.jsonfiles must be sorted (enforced by repopo). type: "module"— all packages are ESM.
- pnpm not found: Run
corepack enablefirst. - Stale Nx cache:
pnpm nx reset && pnpm build - Format/lint/policy errors:
pnpm format && pnpm lint:fix && pnpm run fix:policy - xkcd2-api test failures: Pre-existing — ignore unless modifying that package.
- Uncommitted files after build: CLI manifests and READMEs are auto-generated. Commit them if expected.
CLAUDE.mdat root has comprehensive architecture documentation.- Many packages have their own
CLAUDE.mdwith package-specific guidance — checkpackages/<name>/CLAUDE.md.