Moving eniem.dev to a new monorepo

February 16, 2026

The Problem

eniem.dev is a Next.js SaaS boilerplate that ships with a CLI and a docs site built with Fumadocs. Until recently, it lived across 3 separate repos:

  • eniem-boilerplate: the customer-facing boilerplate
  • eniem-cli: the npm-published CLI
  • eniem-docs: the documentation site

This worked fine when I was writing code manually. But once I started building with AI agents, the multi-repo setup became a real bottleneck.

The main pain point: agents can't see across repo boundaries. Building a feature on the boilerplate that needed matching docs meant switching repos, spinning up a new agent session, and re-feeding all the context. Tightly coupled CLI and boilerplate features couldn't be implemented in one session. I was constantly context-switching, and so was the AI.

On top of that, I had duplicated CI workflows across repos and conventions that were slowly diverging. Three repos for what is essentially one product didn't make sense anymore.

The Decision

I went with Turborepo + pnpm workspaces. It's what I know, it's fast, and it handles the filtering and caching I need. No comparison shopping needed, I just wanted something that works.

The constraints were clear:

  • Preserve full git history for all packages (no squash-and-start-fresh)
  • Keep the customer-facing boilerplate repo accessible. Customers shouldn't see the monorepo internals, they just need the boilerplate
  • No collaborator migration on GitHub
  • Consolidate CI across all projects

The Migration

The whole migration happened in 4 phases, each as a separate PR.

Phase 1 — Repo Consolidation

The first step was getting all the code into one place. I created a fresh repo and used git subtree add for each of the 3 existing repos:

git subtree add --prefix=apps/boilerplate <boilerplate-remote> main
git subtree add --prefix=apps/docs <docs-remote> main
git subtree add --prefix=packages/cli <cli-remote> main

The beauty of git subtree is that it preserves the full commit history. Running git log -- apps/boilerplate/ shows every commit from the original repo. No history lost.

The resulting structure:

apps/
├── boilerplate/    # The Next.js SaaS boilerplate
├── docs/           # Fumadocs documentation site
packages/
└── cli/            # npm-published CLI

Phase 2 — Post-Migration Config

With everything in one repo, the packages needed new names to avoid conflicts. I renamed eniem to @eniem/boilerplate and eniem-docs to @eniem/docs. The CLI stayed as eniem-cli since that's what's published to npm.

I also cleaned up the duplicate .github/workflows/ directories that came over with each subtree, and added semantic-release-monorepo so CLI releases would only trigger from commits touching packages/cli/.

Phase 3 — Unified GitHub Actions

This was the most involved phase. Three workflows to rule them all:

ci.yml: Runs on every PR to main. One command does it all:

pnpm turbo build lint typecheck test

Turborepo handles the dependency graph and caching. If only the docs changed, only docs tasks run.

release-cli.yml: Triggers on push to main when files in packages/cli/ change. Runs semantic-release with npm OIDC trusted publishing. semantic-release-monorepo ensures it only analyzes commits touching the CLI package.

sync-boilerplate.yml: This is the clever one. Customers clone the eniem-boilerplate repo, not the monorepo. So I needed a way to keep that repo in sync. Here's how it works:

  1. Triggers on boilerplate@* tags
  2. Rsyncs apps/boilerplate/ to a temp directory
  3. Restores the standalone package name (@eniem/boilerplateeniem) with a sed swap in package.json
  4. Generates a standalone pnpm-lock.yaml with pnpm install --lockfile-only
  5. Clones the customer repo, swaps the .git directory, commits with a changelog, and pushes with the version tag

The customer repo gets a clean, standalone package with its own lock file. No trace of the monorepo.

Phase 4 — AI Workflow Setup

The whole reason for the migration. I set up monorepo-aware .eni/ prompts at the root so agents can plan and build across all packages in one session. The standalone .eni/ in apps/boilerplate/ stays for customers who use the boilerplate on its own.

Tricky Bits

A few things that weren't obvious:

React version mismatch. The CLI uses React 18 (it builds templates, not a running app), while the boilerplate and docs run React 19. pnpm workspaces handles this fine — no forced version unification needed. Each package gets its own dependency tree.

Standalone lock file. The customer repo needs its own pnpm-lock.yaml that doesn't reference monorepo workspace packages. The trick is restoring the original package name first (sed to swap @eniem/boilerplate back to eniem), then running pnpm install --lockfile-only in the extracted directory. pnpm resolves everything as if it's a standalone project.

Scoped releases. semantic-release-monorepo plugs into semantic-release and filters the commit analysis to only consider commits that touch the package's directory. So a docs commit doesn't bump the CLI version.

Done With AI in a Day

The entire migration:

  • repo consolidation
  • config wiring
  • three CI workflows
  • AI workflow setup

All was done in a single day with AI assistance.

Without AI this would have easily been a multi-day project. The CI workflows alone would have taken hours of trial and error debugging. Instead, I described what I wanted, the agent wrote the workflows, I reviewed, and we iterated until everything passed.

The Result

One repo, one CI, one place to work.

  • Turborepo filtering for targeted builds: pnpm turbo build --filter=@eniem/boilerplate
  • Customer repo still gets a clean standalone package via the sync workflow
  • Full git history preserved for every package
  • AI agents now have full context across boilerplate, CLI, and docs in a single session

The multi-repo setup was holding back the AI workflow. Now an agent can implement a boilerplate feature, update the CLI to support it, and write the docs. All in one session, with full context. That's the real win.