Standardizes monorepo operations like builds, tests, and package scaffolding across the codebase.

Install

mkdir -p .claude/skills/monorepo && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/6267" && unzip -o skill.zip -d .claude/skills/monorepo && rm skill.zip

Installs to .claude/skills/monorepo

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Monorepo scripts, package boilerplate, conventions. Use when: "how do I run", "bun run", "build this", "run tests", "typecheck", "create a new package", linting, scaffolding packages.
183 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Fix formatting and linting
  • Run type checking
  • Execute unit tests and benchmarks
  • Scaffold new packages

How it works

It uses consistent script naming conventions and workspace filtering to execute tasks across the monorepo from the root directory.

Inputs & outputs

You give it
Monorepo script command
You get back
Formatted code, test reports, or scaffolded files

When to use monorepo

  • Run monorepo tests
  • Type-check the entire project
  • Scaffold a new package
  • Run linting scripts

About this skill

Script Commands

The monorepo uses consistent script naming conventions.

Commands

CommandPurposeWhen to use
bun formatFix formatting (biome)Development
bun format:checkCheck formattingCI
bun lintFix lint issues (biome)Development
bun lint:checkCheck lint issuesCI
bun typecheckType checking (tsc, svelte-check, astro check)Both
bun testRun unit tests (*.test.ts only)Both
bun benchRun benchmarks (*.bench.ts; reports, no assertions)Manual

Convention

  • No suffix = fix (modifies files)
  • :check suffix = check only (for CI, no modifications)
  • typecheck alone = type checking (separate concern, cannot auto-fix)
  • test runs only *.test.ts; bench runs only *.bench.ts. A file is one or the other : never both. Benchmarks print reports; tests assert.

The app SDK build gate

@epicenter/lens and @epicenter/app export ./dist only, because their declarations are published and then typechecked inside a stranger's project (ADR-0186). Every in-repo consumer therefore resolves them through node_modules to build output, so a test that reaches either one is testing the last build rather than the working tree.

Root test runs build:app-sdk first for exactly that reason. It is one gate rather than a pretest in each affected package, and it is not redundant with postinstall: postinstall makes dist fresh once, and an edit after that is invisible until something rebuilds.

Two things follow. Running one package's tests directly (bun test <path>, or bun run --cwd packages/app test) does not rebuild, so build first when the change is in lens or app. And a module that both clients depend on for correctness earns a test inside its own package, where the import is source: packages/lens/src/carrier.test.ts is the worked example.

Do not fix this with a development or bun export condition. In-repo tests and published consumers would then run different code, which is the same problem in a place nobody looks.

Dev Scripts

Start apps from the repo root, not by cd-ing into the app. Root bun dev:<app> runs every process the app needs; for apps that talk to the hosted API (tab-manager, honeycrisp, vocab, whispering, and the api dashboard), it also starts @epicenter/api on localhost:8787 via bun run --filter. Root bun dev:<app>:ui runs the app's frontend alone when that split exists; for Tauri apps, it maps to the package's dev:web. bun dev:api runs just the backend. Local Books, Local Mail, and Super Chat have their own multi-process flows documented in their READMEs; they have no root dev:* target.

Inside a single package, the conventions are:

Non-Tauri apps use a single dev script that runs the underlying tool directly (vite dev, astro dev, wrangler dev, wxt). Tauri desktop apps (honeycrisp, whispering, matter) have two dev surfaces and name them explicitly: dev launches the desktop shell (aliasing dev:desktop), and dev:web runs Vite alone, which each app's tauri.conf.json invokes as its beforeDevCommand. The suffix convention applies primarily to database commands:

ScriptMeaning
devThe default local workflow. May still require Infisical login for app secrets (e.g. API keys), but only ever talks to local infrastructure at runtime.
dev:webTauri apps: the Vite dev server alone, no desktop shell. Invoked by tauri.conf.json as beforeDevCommand.
dev:desktopTauri apps: launches the native desktop app (tauri dev). dev aliases this.
db:*:localRuns against local Postgres. Works without Infisical login.
db:*:remoteWraps with infisical run --env=prod. Production data; treat as admin.

There is no dev:remote. Production data is reached only through :remote db scripts and deploy, never through a development server.

CLI (epicenter)

From the monorepo root, target the local API with bun run cli:local (it sets EPICENTER_API_URL=http://localhost:8787); bun run cli runs from source against the default hosted target.

bun run cli:local auth login
bun run cli:local up -C <project-dir>

The full targeting matrix (prod, published binary, per-target token storage) lives in packages/cli/README.md. Keep it there so this section cannot drift.

After Completing Code Changes

Run type checking to verify:

bun typecheck

This runs bun run --filter '*' typecheck which executes the typecheck script in each package (e.g., tsc --noEmit, svelte-check).

New Package Boilerplate

When creating a new package in packages/, follow this exact structure.

package.json

{
  "name": "@epicenter/<package-name>",
  "version": "0.0.1",
  "exports": {
    ".": "./src/index.ts"
  },
  "license": "AGPL-3.0-or-later",
  "scripts": {
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {},
  "devDependencies": {
    "@types/bun": "catalog:",
    "typescript": "catalog:"
  }
}

Key conventions:

  • exports only, no main/types: modern resolvers ignore main/types when exports is present. The entry point is ./src/index.ts; there is no build step, consumers import the source directly.
  • Use "workspace:*" for internal deps (e.g., "@epicenter/workspace": "workspace:*").
  • Use "catalog:" for shared versions managed in the root package.json catalogs.
  • peerDependencies for packages consumers must also install (e.g., yjs).
  • license: default AGPL-3.0-or-later (everything Epicenter ships or runs). Use MIT only if the package is meant for third-party developers to embed in their own software (the toolkit). See docs/licensing/licensing-strategy.md; bun run check:licenses fails if an MIT package can reach an AGPL one.

tsconfig.json

A leaf config picks a tier and adds nothing that repeats a base. For a Bun library:

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "types": ["bun"],
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

A Svelte or browser library extends ../../tsconfig.dom.json instead. For all eight leaf tiers, the never-redeclare list, and the module strategy, see the tsconfig skill.

After creating the package, run bun install from the repo root to register it in the workspace.

When not to use it

  • When running dev scripts inside sub-packages instead of the root

Prerequisites

Bun runtime

Limitations

  • Benchmarks and tests cannot be in the same file
  • Production data is restricted to specific remote scripts

How it compares

It provides a centralized, consistent command interface for all packages rather than individual package-level scripts.

Compared to similar skills

monorepo side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
monorepo (this skill)12moReviewIntermediate
turborepo612moReviewIntermediate
run-nx-generator53moReviewIntermediate
upgrading-expo34moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry