Skip to main content
Better Skills is organized as a Bun monorepo with Turborepo for optimized builds.

Workspace overview

The monorepo contains three applications and six shared packages:
better-skills/
├── apps/
│   ├── cli/           # Command-line interface
│   ├── server/        # Hono API server
│   └── web/           # Next.js web app
├── packages/
│   ├── api/           # tRPC router and business logic
│   ├── auth/          # Better Auth configuration
│   ├── config/        # Shared TypeScript config
│   ├── db/            # Drizzle schema and migrations
│   ├── env/           # Environment validation
│   └── markdown/      # Markdown utilities
├── scripts/
│   └── sync-neon-to-local.sh
├── package.json       # Root workspace config
├── turbo.json         # Turborepo configuration
└── bun.lock          # Lock file

Workspace configuration

The root package.json defines the workspace:
package.json
{
  "name": "better-skills",
  "private": true,
  "workspaces": {
    "packages": [
      "apps/*",
      "packages/*"
    ],
    "catalog": {
      "dotenv": "^17.2.2",
      "zod": "^4.1.13",
      "typescript": "^5",
      "@types/bun": "^1.3.4",
      "hono": "^4.8.2",
      "@trpc/server": "^11.7.2",
      "better-auth": "^1.4.18",
      "@trpc/client": "^11.7.2"
    }
  }
}

Workspace packages

Bun discovers packages matching apps/* and packages/* patterns.

Catalog dependencies

The catalog feature ensures consistent versions across all packages:
packages/api/package.json
{
  "dependencies": {
    "zod": "catalog:",
    "@trpc/server": "catalog:"
  }
}
catalog: resolves to the version defined in the root package.json.
Catalog dependencies are a Bun feature for version consistency in monorepos.

Package dependencies

Dependency graph

Internal dependencies

Packages reference each other using the workspace:* protocol:
apps/web/package.json
{
  "dependencies": {
    "@better-skills/api": "workspace:*",
    "@better-skills/auth": "workspace:*",
    "@better-skills/env": "workspace:*"
  }
}
This always uses the local version during development.

Turborepo configuration

Turborepo orchestrates tasks across the monorepo:
turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "ui": "tui",
  "globalEnv": [
    "DATABASE_URL",
    "BETTER_AUTH_SECRET",
    "BETTER_AUTH_URL",
    "CORS_ORIGIN"
  ],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": ["dist/**", ".next/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "check-types": {
      "dependsOn": ["^check-types"]
    }
  }
}

Task configuration

Build task

"build": {
  "dependsOn": ["^build"],
  "outputs": ["dist/**", ".next/**"]
}
  • dependsOn: ["^build"] - Build dependencies first
  • outputs - Cache these directories

Dev task

"dev": {
  "cache": false,
  "persistent": true
}
  • cache: false - Don’t cache dev server output
  • persistent: true - Keep process running

Check-types task

"check-types": {
  "dependsOn": ["^check-types"]
}
Type-check dependencies before current package.

Global environment variables

"globalEnv": [
  "DATABASE_URL",
  "BETTER_AUTH_SECRET"
]
Turbo invalidates cache when these environment variables change.

Running workspace commands

Root scripts

The root package.json provides convenience scripts:
package.json
{
  "scripts": {
    "dev": "turbo dev --filter=!cli",
    "cli": "bun run --cwd apps/cli src/index.ts",
    "build": "turbo build",
    "check-types": "turbo check-types",
    "dev:web": "turbo -F web dev",
    "dev:server": "turbo -F server dev",
    "dev:cli": "turbo -F cli dev",
    "db:push": "turbo -F @better-skills/db db:push",
    "db:studio": "turbo -F @better-skills/db db:studio",
    "check": "oxlint && oxfmt --write"
  }
}

Running all apps

Start web and server (excludes CLI):
bun run dev
The --filter=!cli flag excludes the CLI from the dev task since it’s typically run separately.

Running specific apps

bun run dev:web

Turbo filters

Turborepo supports powerful filtering:
# Run task in specific package
turbo build --filter=web

# Run task in package by name
turbo build --filter=@better-skills/api

# Run task in multiple packages
turbo build --filter=web --filter=server

# Run task in all packages except one
turbo dev --filter=!cli

# Run task in package and its dependencies
turbo build --filter=web...

Database commands

Database tasks target the db package:
bun run db:push     # Push schema to database
bun run db:generate # Generate migration files
bun run db:migrate  # Run migrations
bun run db:studio   # Open Drizzle Studio
These use the Turbo filter -F @better-skills/db.

Build pipeline

Building for production

Build all packages and apps:
bun run build
Turborepo:
  1. Analyzes dependency graph
  2. Builds packages in topological order
  3. Caches outputs for future builds
  4. Runs builds in parallel when possible

Build order example

1. Build packages/config (no dependencies)
2. Build packages/env (depends on config)
3. Build packages/markdown (depends on config)
4. Build packages/db (depends on env, config)
5. Build packages/auth (depends on db, env, markdown, config)
6. Build packages/api (depends on auth, db, env, markdown, config)
7. Build apps in parallel:
   - apps/web (depends on api, auth, env, markdown)
   - apps/server (depends on api, auth, db, env)
   - apps/cli (depends on api, env, markdown)

Caching

Turbo caches build outputs based on:
  • Input files (tracked by git)
  • Environment variables (listed in globalEnv)
  • Dependencies
On subsequent builds, unchanged packages are restored from cache.
Cache hits are indicated by >>> FULL TURBO in the build output.

Package exports

Shared packages use conditional exports for Bun and Node.js:
packages/api/package.json
{
  "exports": {
    ".": {
      "bun": "./src/index.ts",
      "default": "./dist/index.mjs"
    }
  }
}
  • Bun: Uses source .ts files directly (no build needed)
  • Node.js: Uses compiled .mjs files
During development, Bun loads source TypeScript directly. For production Node.js deployments, packages must be built.

Adding new packages

Create package structure

mkdir -p packages/my-package/src
cd packages/my-package

Initialize package.json

packages/my-package/package.json
{
  "name": "@better-skills/my-package",
  "type": "module",
  "exports": {
    ".": {
      "bun": "./src/index.ts",
      "default": "./dist/index.mjs"
    }
  },
  "scripts": {
    "build": "bun build ./src/index.ts --outfile ./dist/index.mjs --target=node --format=esm"
  },
  "devDependencies": {
    "@better-skills/config": "workspace:*",
    "typescript": "catalog:"
  }
}

Create source file

packages/my-package/src/index.ts
export function hello() {
  return "Hello from my-package!";
}

Use in other packages

Add dependency:
apps/web/package.json
{
  "dependencies": {
    "@better-skills/my-package": "workspace:*"
  }
}
Import and use:
import { hello } from "@better-skills/my-package";

console.log(hello());

Linting and formatting

Better Skills uses Oxlint and Oxfmt:
bun run check
This runs:
  1. oxlint - Fast ESLint alternative
  2. oxfmt --write - Code formatter

Pre-commit hooks

Husky and lint-staged run checks before commits:
package.json
{
  "lint-staged": {
    "*": "oxlint",
    "*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}": "oxfmt --write"
  }
}
Initialize hooks:
bun run prepare

Type checking

Check types across all packages:
bun run check-types
Turbo runs tsc --noEmit in each package with dependencies checked first.

Common workflows

Add a new dependency

bun add <package>

Update all dependencies

bun update

Clean build artifacts

find . -name 'dist' -type d -exec rm -rf {} +
find . -name '.next' -type d -exec rm -rf {} +

Reset node_modules

rm -rf node_modules apps/*/node_modules packages/*/node_modules
bun install

Troubleshooting

Clear Turbo cache:
rm -rf .turbo node_modules/.cache/turbo
Ensure:
  1. Package is listed in workspace packages array
  2. Package has a valid package.json
  3. Run bun install to update workspace links
Restart TypeScript server:
  • VS Code: Cmd/Ctrl + Shift + P → “Restart TS Server”
  • Check that packages/config/tsconfig.json is extended
Try:
  1. Clean build artifacts
  2. Run bun install to ensure dependencies are up to date
  3. Check Turbo logs for specific errors

Next steps

Architecture

Understand system architecture

Tech Stack

Explore the technology choices

Development Setup

Get started with development

Database Setup

Configure your database

Build docs developers (and LLMs) love