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 type | Path under | Examples |
|---|---|---|
| Deployable app | apps/ | apps/web, apps/admin, apps/dev-docs |
| Shared library | packages/ | packages/auth-client, packages/utils |
| Design system | design-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:*"
}
}
EOFCritical points:
- Package name
@todayai-labs/<name>— the@todayai-labs/scope is the convention private: true— workspace internals never publish to npmexports— 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.tsfiles
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: trueAdd 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 installThis 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 succeedMoon 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:
- Vercel side: Create a project under the
todayaiteam, set root directory toapps/<name>, framework preset to Next.js / Vite as appropriate, enable Deployment Protection if internal - GitHub side: Add
VERCEL_PROJECT_ID_<NAME>to repo variables - Workflow: Create
.github/workflows/deploy-<name>.yml, copyingdeploy-dev-docs.ymlor another deploy workflow as a template - GitHub environments: Create
<name>-previewand<name>-production vercel.json: Createapps/<name>/vercel.jsonwithinstallCommand,buildCommand,outputDirectory, and a tightignoreCommandthat 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 topnpm-workspace.yaml'scatalog:map - Moon says "no project found" —
moon.ymlmissing or has invalid YAML - Lint passes locally but warns on CI — local
oxlintmatched ignore patterns; CI didn't. Checkoxlint.config.tsignore globs.
If any of these bite, check the debugging tools section for diagnostic commands.
Docs sites — boundary
The hard rule for which of the three Fumadocs sites a given doc belongs in. Read once so you never write a doc in the wrong place.
pnpm workspaces
pnpm 9 with a strict catalog and workspace protocol — single source of dependency versions across every app, package, and design-system project.