Development
The everyday workflow - root pnpm + Turborepo scripts, the enforced code style, the pre-commit translate hook, and the workspace-safe way to add dependencies.
Every task runs from the repo root through pnpm + Turborepo, which fans it out to each package and app that defines a matching script. Prettier, ESLint, and strict TypeScript are enforced.
Dev scripts
| Command | What it does |
|---|---|
pnpm dev | Start every app in watch mode (--continue keeps the rest running if one exits). Fullstack excludes the standalone apps/backend, since the frontend serves the API in-process |
pnpm build | Build every app and package |
pnpm start | Serve the production build |
pnpm lint | ESLint across the workspace |
pnpm format | Prettier write across every source, config, and markdown file |
pnpm check-types | tsc --noEmit, no output emitted |
pnpm test | Unit tests. 0 failed, 0 skipped, with no .env, no Docker and no network - see testing |
Turbo caches build, lint, and check-types; dev and test are uncached. Run pnpm lint and pnpm check-types before pushing - CI runs the same scripts.
pnpm dev writes nothing into your repo and prints no cache stack traces: Next's agent-rules file writer is off (agentRules: false), and the Redis client logs one line per state change rather than a stack per retry. A Redis that is down is a warning, never a wall of noise.
Side processes, run alongside pnpm dev:
| Command | What it does |
|---|---|
pnpm dev:mail | Email-template preview server on port 3030 |
pnpm dev:inngest | Inngest dev server for background jobs (UI on 8288) |
pnpm mastra:studio | Mastra Studio for inspecting the AI assistant and its memory (UI on 4111) |
pnpm drizzle:studio | Drizzle Studio for browsing and editing your database tables |
pnpm infra / pnpm infra:stop | Start/stop the local Docker services (present only if you chose Docker-backed infra at init) |
Code style
Formatting is owned by Prettier; TypeScript is strict - no any, as any, or unsafe casts. The committed .prettierrc:
{
"useTabs": true, // indent with tabs
"tabWidth": 2, // tab renders as 2 spaces
"printWidth": 100, // 100-character line width
"trailingComma": "none", // no trailing commas
"semi": true, // semicolons required
"singleQuote": false, // double quotes
"arrowParens": "always", // always parenthesize arrow params
"endOfLine": "lf"
}Pre-commit hook
A simple-git-hooks pre-commit hook is installed by the prepare script on pnpm install. It regenerates non-English locale files for any changed keys, then git adds the output into the commit: packages/i18n/translations, packages/content, packages/translate/.meta, and the generated locale artifacts.
Accept that auto-staged output. Edit only the source en/*.json files and the locales record in i18n.ts; everything else is generated. Translation runs only when OPENROUTER_API_KEY is set - without it the hook skips, so commits never fail.
Adding dependencies
Install into the workspace that uses a package, never at the root by hand-editing package.json.
# add to an app or a backend package (<app> = your web app's package name)
pnpm --filter <app> add <pkg>
pnpm --filter @repo/api add <pkg>
# dev-only dependency
pnpm --filter @repo/api add -D <pkg>
# remove
pnpm --filter @repo/api remove <pkg>pnpm --filter <pkg> add command for the target workspace.pnpm install at the root to relink siblings whose peer hashes shifted, or their types stop resolving.Never hand-edit pnpm-lock.yaml - let pnpm own it. Pin a shared version once in the catalog: block of pnpm-workspace.yaml, then set the dep to "catalog:" in each package's package.json.
Editor setup
The project ships .vscode/launch.json with debug configurations for your stack. There is no committed .vscode/settings.json - create one with these values:
| Setting | Value |
|---|---|
eslint.workingDirectories | [{ "mode": "auto" }] so ESLint resolves the right config per package |
files.associations | { "*.css": "tailwindcss" } for class IntelliSense |
tailwindCSS.experimental.configFile | tooling/tailwind/globals.css |
editor.quickSuggestions | { "strings": "on" } so class names autocomplete inside strings |
Install the Prettier, ESLint, and Tailwind CSS IntelliSense extensions, then enable format-on-save.