ZeroStarter

Scripts

Every bun script, grouped by when you'd reach for it.

Every script lives in the root package.json; run any of them with bun run <name>. They're grouped below by when you actually reach for one.

Dev loop

  • bun run dev: start both apps through portless on named .localhost URLs (run bunx portless list to see them; branch-prefixed in a worktree). PORTLESS=0 bun run dev uses fixed ports instead: web :3000, api :4000.
  • bun run clean: remove build artifacts and caches (.next, .turbo, dist), every node_modules, and generated typedefs.
  • bun run devtools: open Turborepo DevTools.

bun run clean deletes every node_modules and the generated typedefs, so re-run bun install afterward.

Database

bun run auth:schema runs the Better Auth CLI; each db:* script builds @packages/env first, then runs Drizzle Kit against packages/db.

  • bun run auth:schema: regenerate the Better Auth tables in packages/db/src/schema/auth.ts from packages/auth/src/schema.ts (--check only compares).
  • bun run db:generate: generate a migration from schema changes.
  • bun run db:migrate: apply pending migrations.
  • bun run db:studio: open Drizzle Studio to browse and edit data.

Quality

  • bun run lint: Oxlint over the whole tree (also what CI runs).
  • bun run format: Oxfmt, writing fixes.
  • bun run format:check: Oxfmt, report only (also what CI runs).
  • bun run check-types: type-check every workspace, then the repo scripts (check-types:scripts, tsc over .github/scripts) and the test tree (check-types:tests, one tsc run per tests/**/tsconfig.json).
  • bun run test: build the shared packages/*, regenerate the data-table font metrics, then run the whole suite from the repo-root tests/ mirror. The build comes first because a test reaches a shared package the way the apps do, through its published exports, which point at dist; without it a fresh checkout fails to resolve the import rather than failing an assertion. Turbo caches the build, so a repeat run costs milliseconds. PR CI runs this and check-types too, so a failing test or type error blocks the merge.
  • bun run test:e2e: the end-to-end suite, every *.e2e.test.ts under tests/, against a running local-stage stack (NODE_ENV=local with AGENT_SIGNIN_ENABLED=true, so the agent can sign in). E2E_API_URL and E2E_WEB_URL default to the compose ports (localhost:4000, localhost:3000) and are refused unless local, since the suite writes through the API; E2E_POSTGRES_URL unlocks the flows that seed a second account. Golden: every contract response is snapshotted, and bun run test:e2e --update-snapshots accepts a deliberate change. Skipped when no stack is named, so bun run test stays green without one.

Release / production

  • bun run build: build every app with Turbo, then print each workspace's size. This is the same build the pre-commit hook runs.
  • bun run release:version: print the version decision for the current release window as JSON (the last tag, the tree, what the window earned, and the larger of the two); --write moves package.json to it. The draft-PR workflow runs it on a push to canary whenever a release window is open, meaning main exists and canary is ahead of it; running it by hand only previews, and it needs no network. It reads the v* tags, so fetch them first: with tags present but none reachable from HEAD it stops rather than computing from v0.0.0.
  • bun run start: serve the production build.

The changelog, version bump, tag, and GitHub release are automated: you don't run them by hand. See Releases.

shadcn

  • bun run shadcn:update: update every shadcn/ui component, then bun i.

bun run shadcn:update overwrites everything under web/next/src/components/ui/, so hand-edits to those files are lost. Put durable customizations in .github/scripts/shadcn-customize.ts, which re-applies them after each sync; the shadcn-sync skill covers the workflow.

Console

  • bun run console:roles <grant|revoke|list> [email] [role]: set a user's rung on the console role ladder. grant takes owner, admin or member, defaulting to admin, or to owner while the install has none, which is how a fresh install gets its first way in. revoke returns them to user, and list shows everyone with console access. grant and revoke both record a line in Activity attributed to console:roles, since nobody is signed in when it runs.

Runs automatically

  • prepare installs the Lefthook git hooks; postinstall normalizes the catalog: dependency entries. Both run for you after bun install.

Next

  • Code Quality: what lint, format, and build enforce on every commit.
  • Architecture: the two apps and four packages these scripts drive.