Today Platform Web — Dev Docs
Workspace

Adding a package

Step-by-step checklist for adding a new app, package, or design-system project to the monorepo without breaking CI or deploys.

This is the full checklist for adding a new workspace project, in the order CI and tooling expect things to land. Most of the steps are mechanical; the order matters because each tool's discovery mechanism is different.

Choosing the path

Project typePath underExamples
Deployable appapps/apps/web, apps/admin, apps/dev-docs
Shared librarypackages/packages/auth-client, packages/utils
Design systemdesign-system/design-system/tokens, design-system/ui

If your code is consumed by multiple apps → packages/ or design-system/. If it's a standalone deployable surface → apps/. Don't add to demos/ — that directory's contents moved to todayai-labs/playground.

Step 1 — Create the directory + package.json

mkdir -p packages/my-thing/src
cat > packages/my-thing/package.json <<'EOF'
{
  "name": "@todayai-labs/my-thing",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./src/index.ts",
        "default": "./src/index.ts"
      }
    }
  },
  "scripts": {
    "typecheck": "tsgo --build --pretty",
    "test": "vitest run"
  },
  "devDependencies": {
    "@todayai-labs/tsconfig": "workspace:*"
  }
}
EOF

Critical points:

  • Package name @todayai-labs/<name> — the @todayai-labs/ scope is the convention
  • private: true — workspace internals never publish to npm
  • exports — define every entry point you want consumers to import (., ./preview, ./theme/preview, etc.). The convention emits TS source directly ("types": "./src/index.ts") because consumers typecheck against the source, not built .d.ts files

For an app, change scripts and devDependencies to match an existing app's shape (apps/admin/package.json is the cleanest reference).

Step 2 — Three tsconfig files

Create tsconfig.json, tsconfig.app.json, tsconfig.node.json per the tsconfig references pattern. For a library:

// tsconfig.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "files": [],
  "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
}
// tsconfig.app.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "@todayai-labs/tsconfig/base.json",
  "compilerOptions": {
    "noEmit": true,
    "rootDir": ".",
    "outDir": "dist/ts/app",
    "tsBuildInfoFile": "dist/ts/app/.tsbuildinfo",
  },
  "include": ["src"],
}
// tsconfig.node.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "@todayai-labs/tsconfig/base.json",
  "compilerOptions": {
    "noEmit": true,
    "lib": ["ESNext"],
    "types": ["node"],
    "rootDir": ".",
    "outDir": "dist/ts/node",
    "tsBuildInfoFile": "dist/ts/node/.tsbuildinfo",
  },
  "include": ["*.config.ts", "*.config.js"],
}

For an app (web framework), tsconfig.app.json also needs "lib": ["ESNext", "DOM", "DOM.Iterable"] + "jsx": "preserve" + "plugins": [{ "name": "next" }] (for Next apps).

Step 3 — moon.yml

# https://moonrepo.dev/docs/config/project
$schema: https://moonrepo.dev/schemas/project.json

language: typescript

tasks:
  typecheck:
    command: pnpm typecheck
    inputs:
      - src/**/*
      - package.json
      - tsconfig.json

  test:
    command: pnpm test
    inputs:
      - src/**/*
      - package.json
      - tsconfig.json
    options:
      cache: true

Add build / dev / start tasks if applicable (apps). Be honest about inputs: — missing entries cause false cache hits.

Step 4 — Register in root tsconfig.json

This is the step everyone forgets. Edit tsconfig.json at the repo root and add your project:

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "files": [],
  "references": [
    // ... existing ...
    { "path": "./packages/my-thing/tsconfig.json" },
  ],
}

Without this, tsgo --build skips your project even though pnpm and Moon know about it. The scoped pnpm --filter @todayai-labs/my-thing typecheck works, but CI's moon run :typecheck doesn't pick up errors in your project until you register it here.

Step 5 — pnpm install

pnpm install

This registers the new workspace package in pnpm-lock.yaml. pnpm picks up the new directory via the glob in pnpm-workspace.yaml; you don't need to edit that file.

If the package has external deps, add them through the catalog (see pnpm workspaces on adding catalog entries).

Step 6 — Verify Moon picked it up

moon project-graph | grep my-thing
moon run my-thing:typecheck    # should succeed

Moon picks up the new project from its workspace globs in .moon/workspace.yml. If moon project-graph doesn't list it, check the moon.yml file exists and has valid YAML.

Step 7 — scripts/affected.ts (no action needed)

The affected-scope detector at scripts/affected.ts reads pnpm-workspace.yaml dynamically — your new package is picked up automatically on the next CI run. Do not add path globs to affected.ts (an early draft of this site's PR-CA1 plan proposed exactly that and was caught in Codex review).

Step 8 — For an app: Vercel + deploy workflow

If your new project is a deployable app:

  1. Vercel side: Create a project under the todayai team, set root directory to apps/<name>, framework preset to Next.js / Vite as appropriate, enable Deployment Protection if internal
  2. GitHub side: Add VERCEL_PROJECT_ID_<NAME> to repo variables
  3. Workflow: Create .github/workflows/deploy-<name>.yml, copying deploy-dev-docs.yml or another deploy workflow as a template
  4. GitHub environments: Create <name>-preview and <name>-production
  5. vercel.json: Create apps/<name>/vercel.json with installCommand, buildCommand, outputDirectory, and a tight ignoreCommand that lists the package's own path and any dep paths it imports from

See Deployment for the full deploy-workflow template and the vercel.json shape.

Step 9 — Storybook (optional)

Most new packages should not add a Storybook — components belong in design-system/ui or design-system/opal where they get an existing Storybook for free. If your new package genuinely needs its own, see Storybook topology for the structure.

Step 10 — README

Add a README.md at the package root. Minimum content:

  • One-paragraph what-it-is
  • How to consume (import path + a tiny code sample)
  • Where the canonical docs live (link to dev-docs / design-system/docs as appropriate)
  • Local dev commands

For an app, add it to workspace/apps.mdx in this site (PR after the new app lands). For a library, add it to workspace/packages.mdx or workspace/design-system.mdx.

Common forget-points

A non-exhaustive list of "I added a package and X mysteriously fails":

  • TS6305 errors after adding — forgot Step 4 (register in root tsconfig.json)
  • Vercel deploy fails on the first PR — forgot Step 8.2 (set VERCEL_PROJECT_ID_<NAME> in repo variables)
  • pnpm install can't find catalog entry — added "foo": "catalog:" without adding to pnpm-workspace.yaml's catalog: map
  • Moon says "no project found"moon.yml missing or has invalid YAML
  • Lint passes locally but warns on CI — local oxlint matched ignore patterns; CI didn't. Check oxlint.config.ts ignore globs.

If any of these bite, check the debugging tools section for diagnostic commands.

On this page