Every side project I started followed the same ritual.
Find a starter. Clone it. Realize it doesn't have the exact
combination I need — say, Next.js with Express as a separate
backend, PostgreSQL, JWT auth, and Tailwind. Start adding things.
Watch the tsconfig break. Spend 45 minutes on Stack Overflow.
Finally start actually building at 11pm, mentally exhausted.
I did this enough times that I started keeping a folder of
"my personal starters." Which worked until I had six of them and
they were all slightly different and none of them had the latest
versions of anything.
So I built Foundation CLI instead.
What it is
Foundation CLI is a dependency-aware project assembler. You describe
your stack — frontend, backend, database, auth, UI, deployment — and
instead of copying static template files, it:
- Resolves the full module dependency graph
- Detects and handles conflicts automatically
- Merges configs intelligently (deep merge for package.json, key-dedup for .env, semver intersection for requirements.txt)
- Commits everything atomically — or not at all
npx @systemlabs/foundation-cli create
The part that was interesting to build
The dependency resolver was the core challenge. Modules don't
conflict by name — they conflict by capability. "Auth JWT" and
"Auth OAuth" are different implementations of the same capability.
The resolver uses capability tokens, not module IDs, to detect this.
Then it runs Kahn's algorithm to build a topological sort of the
module execution order — because if Tailwind needs to run after
Next.js (to patch the config correctly), that dependency needs to be
explicit and enforced.
Conflicts come in two tiers:
- Hard conflicts — two auth modules, two frontends. Block the entire scaffold.
- Advisory conflicts — Express + Cloudflare Workers (Express doesn't run on the edge). Warn, but don't block.
The config merge problem
This is the part that template copiers get wrong. When you add
Tailwind to a Next.js + Express project, three different files need
to change:
-
package.json— new devDependencies -
tailwind.config.js— new file -
globals.css— Tailwind directives - Possibly
next.config.mjs— PostCSS config
A static template can't do this. You either have a template per
combination (exponential), or you have a module system that knows
how to merge.
Foundation CLI's merge engine handles this per file type. JSON files
get deep-merged with conflict detection. .env files get
key-deduplicated. docker-compose.yml gets service-merged.
requirements.txt gets semver-intersected.
Zero partial scaffolds
Every write goes through a FileTransaction. Files are staged to a
temp directory. If anything fails — a template render error, a
missing dependency, a hook failure — the temp dir is deleted and
the output directory is left completely untouched.
You either get a complete, working project or you get nothing. No
mystery half-generated directories.
What's in it
- 28 built-in modules across 7 categories
- 8 project archetypes (SaaS, AI App, E-commerce, API Backend, etc.) with pre-filled smart defaults
- Plugin SDK — third-party modules publish to npm with
foundation-pluginkeyword - TypeScript throughout, strict mode, all generated projects
pass
tsc --noEmit
Try it
npx @systemlabs/foundation-cli create
Repo: https://github.com/ronak-create/Foundation-Cli
I'd love feedback — especially if you've built something similar
and solved the config merge problem differently. The current
approach works but I'm not convinced it's the best design.

Top comments (0)