Development
Run the dev server, follow the shared code style, and add dependencies the workspace-safe way.
The everyday workflow runs from the repo root through pnpm + Turborepo, which fan each task out to every package and app that defines a matching script. Formatting and type safety are enforced by Prettier, ESLint, and strict TypeScript.
Dev scripts
Run all of these from the repo root.
| Command | What it does |
|---|---|
pnpm dev | turbo run dev - start all apps (web + api) in watch mode |
pnpm build | turbo run build - build every app and package |
pnpm start | turbo run start - serve the production build |
pnpm lint | turbo run lint - run ESLint across the workspace |
pnpm format | Prettier write over **/*.{ts,tsx,js,vue,json,md,mjs,cjs} |
pnpm check-types | turbo run check-types - tsc --noEmit, no output emitted |
pnpm test | turbo run test - run unit tests |
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.
Side processes (run alongside pnpm dev when you need them):
| 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 infra / pnpm infra:stop | Start/stop local Docker services (only present when you selected Docker-backed infra at init) |
Code style
Formatting is owned by Prettier; the project is TypeScript strict - no any, as any, or unsafe casts.
The committed .prettierrc values:
{
"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 and ships with your project. It runs the i18n translate step to regenerate non-English locale files for any changed keys, then git adds the regenerated output - packages/i18n/translations, packages/content, packages/translate/.meta, and the generated locale artifacts (i18n-locales.* plus flag-icons.generated.ts) - into the commit. Accept that auto-staged output - only edit the source en/*.json (and the locales record in i18n.ts to add or remove a language); the rest is generated.
The translate step runs only when OPENROUTER_API_KEY is set; without it the hook skips gracefully, so your 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.Never hand-edit pnpm-lock.yaml - let pnpm own it. Shared versions are pinned in the catalog: of pnpm-workspace.yaml; reference catalog: from a package's package.json to stay aligned.
Editor setup
The project ships .vscode/launch.json with debug configurations regenerated for your stack. There is no committed .vscode/settings.json - create one and add these values to wire the toolchain into the editor:
eslint.workingDirectoriesset to auto so ESLint resolves the right config per package.files.associationsmaps*.cssto tailwindcss for class IntelliSense.tailwindCSS.experimental.configFilepoints attooling/tailwind/globals.css.editor.quickSuggestions.stringsisonso class names autocomplete inside string literals.
Install the Prettier, ESLint, and Tailwind CSS IntelliSense extensions, then enable format-on-save in your own editor settings.
Frequently asked questions
Why tabs instead of spaces?
useTabs: true lets each developer pick their own display width; tabWidth: 2 only controls how Prettier estimates line length.
Why run pnpm install again after adding a dependency?
A filtered add can leave sibling packages with stale peer-hash links; a root pnpm install relinks them so type resolution stays correct.
How do I pin a shared dependency version across packages?
Add it to the catalog: block in pnpm-workspace.yaml, then set the dep to "catalog:" in each package's package.json - one source of truth keeps versions aligned.