create-macostack
v2.2.0
Published
Scaffold a production-ready Macostack app with Cloudflare, VPS or Laravel backends and a TanStack web app.
Maintainers
Readme
create-macostack
Scaffold a production-ready full-stack app from the macostack templates — a backend selected for its deployment target and a TanStack (React) web app — with a single command.
bun create macostack my-appAn interactive wizard asks each question once for the whole app, then applies the answers to every part that supports it:
- Which parts you need — backend API, web app, or both.
- Backend stack — where the API runs: Cloudflare all-in (Workers + D1), Cloudflare with your own Postgres & Redis, a VPS (Bun + Postgres + Redis in Docker), or Laravel on cPanel (PHP + MariaDB). This one is asked before anything is downloaded, because it decides which template you get — the four are separate repos, never one repo with runtime adapters. Whichever you pick, the folder is
server/and all four share the same six-table portability contract. - Working language — the language your agent talks to you in, and writes your documents in. Code and skills stay in English.
- Authentication — the whole
/authbundle on or off, then which login methods (credentials, magic link, email OTP, OAuth, passkeys, SIWE). Unchosen methods stay as dormant, reversible code. - Cross-cutting security — CORS, CSRF, secure headers (default: all on).
- API keys — optional paste-in values (email provider, anti-bot widget…). Generated secrets are never asked for; each template mints its own.
Password hashing is not a setup question. The Bun/Workers backends share native scrypt. Laravel uses bcrypt because PHP cannot calculate that scrypt profile. Data moves both ways, but password hashes do not yet: the Bun/Workers backends verify only scrypt and Laravel cannot verify scrypt, so a user whose password crosses between Laravel and another stack must reset it.
The CLI then downloads each template without its git history, installs dependencies, runs each template's own setup (app name, fresh secrets, module wiring), and leaves every part as its own independent git repository, ready to push.
Finally it scaffolds the docs system: AGENTS.md (how agents work here), CLAUDE.md, a docs/ folder (product, architecture, API contract, testing strategy, UI map, current plan, ADRs), the workflow commands (/start, /product, /layout, /design, /e2e, /upgrade), and the stack skills — the technical rules for this stack, indexed in docs/CONVENTIONS.md. All of it works in Claude Code, OpenCode and Codex.
Requirements
- Bun 1.x
- GitHub access to the template repositories (they are private by default):
gh auth login
# or
export GITHUB_TOKEN=github_pat_… # fine-grained token, Contents: Read-onlyAccess is verified before anything is written to disk — a typo'd repo or missing permission fails cleanly, with no half-created folders.
Usage
bun create macostack my-app # scaffold into ./my-app
bun create macostack . # scaffold into the current (empty) directoryResult:
my-app/ # NOT a git repo — deliberately (see below)
├── AGENTS.md # how AI agents work here (Claude Code, OpenCode & Codex)
├── CLAUDE.md # Claude Code adapter → AGENTS.md
├── .claude/ # commands + skills (source)
├── .opencode/ # the same, for OpenCode (generated mirror)
├── .agents/ # skills for Codex (generated mirror)
├── package.json # `bun run sync-agents` regenerates the two mirrors
├── scripts/ # the mirror generator
├── docs/ # product, architecture, contracts — its own git repo
├── server/ # the backend stack you picked — its own git repo
└── client/ # TanStack web app — its own git repoThree repos, and a gitless root. docs/, server/ and client/ each version and deploy independently. The container root is left without git on purpose: with no repo at the top, an agent's @-file picker walks the whole tree and can see into every part. A root repo would hide them behind its own index and .gitignore.
First session
Open Claude Code, OpenCode or Codex at the workspace root. Use /start in Claude/OpenCode or $getting-started in Codex. It reads the project, tells you what the template already provides (so you don't rebuild it), and points you at your first concrete step.
| Claude Code / OpenCode | Codex | What it does |
| --- | --- | --- |
| /start | $getting-started | Orientation: what exists, and where to begin |
| /product | $product | Define or evolve the product vision in docs/PRODUCT.md |
| /layout | $layout | Build UI — a targeted piece, or a page structure in a workshop you later graduate |
Everything else is asked for directly in the chat. Full reference: docs/CHEATSHEET.md.
Managing an existing app
Run the CLI again inside an app it created — it detects the existing setup and switches to manage mode:
cd my-app
bun create macostack .From there you can update the docs system (syncs AGENTS.md, the commands, the skills and the cheat sheet from the template repo — your project's own documents are never touched), add the docs system to an app created before it existed, add a missing part, or toggle feature modules on and off. It opens by reporting which template version each part came from.
Provenance
Every scaffolded part carries a .macostack/ folder inside its own repo — the reconciliation machinery, kept together and out of the way:
server/.macostack/
├── provenance.json # where this copy came from
├── UPGRADE.md # migration notes shipped with this template version
└── skills/ # backend-only knowledge, mirrored into agent directoriesprovenance.json:
{
"provenanceVersion": 1,
"part": "server",
"owner": "macobits",
"repo": "macostack-server-vps",
"ref": "main",
"sha": "a1b2c3d…",
"version": "1.1.0",
"cli": "2.1.0",
"stack": "vps",
"scaffoldedAt": "2026-07-29T16:42:45.611Z"
}The sha is the point: a branch moves, a commit doesn't. Without an exact anchor there is no common ancestor between your project and a newer template, so reconciling them can only overwrite your work or do nothing. With one, the changes between two template versions can be diffed, explained by that template's UPGRADE.md, and applied to your code deliberately.
It lives inside each part's git repo rather than at the workspace root, because the root is deliberately gitless — a record there would be unversioned and unrecoverable — and because the parts have independent lifecycles. Projects created before provenance existed keep working; they just can't be compared against a newer template until the file is written.
UPGRADE.md holds that template's migration notes, written when each change was made rather than inferred from a diff months later. It ships with the version so the notes and the code can never drift apart. It's hidden alongside the provenance on purpose: it's input for the upgrade flow, not a document competing with the README — a fresh project shouldn't open with migration notes for versions it never had.
Upgrading
Projects scaffolded before provenance existed have no record, so manage mode offers Start tracking template versions first. It writes the lineage — which template each part came from — but deliberately not a baseline commit: your code sits at some older state nobody wrote down, and stamping today's commit there would claim you're already current and hide every change from the first upgrade. So that first upgrade is notes-driven (the template's UPGRADE.md reviewed against your code, no diff); once it lands, the anchor is real and every upgrade after it is a proper diff. It explains that, then offers to fetch the comparison right away — you can decline and do it whenever.
With a record in place, manage mode offers Prepare a template upgrade. It downloads the template at the version you're on and at the version you could move to, and writes the difference to .macostack-upgrade/ — one folder per part, with the migration notes, a --stat summary and the full template-to-template diff. Backend-specific skills remain in that comparison, so D1, CPR, VPS and Laravel update only their own overlay. It modifies nothing.
From there, /upgrade (Claude Code, OpenCode) or $upgrade (Codex) reads it, proposes each change with its tag, applies only what you approve, runs your gates, and records what you refused so it stops being offered. Approved overlay changes update their versioned server source and all three agent mirrors; customised mirrors are backed up first.
The review directory stays at the deliberately gitless workspace root. Backups of stack-specific skills live in server/.macostack-backup/, which every server template ignores. The upgrade skill identifies every temporary support directory and offers cleanup when it is safe; it never removes a backup without approval.
The changeset is template-against-template, not template-against-your-code — small, and the same size whether your project is 500 lines or 50,000. Landing it on code you've edited is the judgement call, which is why a human approves each entry and [security] items are applied literally or not at all.
Flags
| Flag | Description |
| --- | --- |
| --server / --client | Scaffold only that part (default: interactive) |
| --yes | Non-interactive: both parts, the default stack, the wizard's preselected answers (OTP-only login, security all on), English |
| --stack <id> | Where the backend runs: cloudflare-d1, cloudflare-postgres, vps (default) or laravel. Picks which server template is downloaded |
| --owner <org> | GitHub owner of the template repos (default: macobits) |
| --ref <ref> | Branch or tag to download (default: main) |
| --server-repo <name> | Server template repo — overrides the one --stack picks |
| --client-repo <name> | Client template repo (default: macostack-client) |
| --docs-repo <name> | Docs system repo (default: macostack-docs) |
| --skip-install | Skip bun install |
| --skip-setup | Skip each template's setup wizard |
| --skip-docs | Skip the docs system (AGENTS.md + docs/ + skills) |
| --help | Print usage |
Environment variables
| Variable | Description |
| --- | --- |
| GITHUB_TOKEN / GH_TOKEN | Token used to download the templates (falls back to gh auth token) |
| MACOSTACK_GITHUB_OWNER | Default template owner |
| MACOSTACK_SERVER_CF_REPO | Cloudflare D1 server template repo |
| MACOSTACK_SERVER_CPR_REPO | Cloudflare + Postgres server template repo |
| MACOSTACK_SERVER_REPO | VPS server template repo |
| MACOSTACK_SERVER_LARAVEL_REPO | Laravel/cPanel server template repo |
| MACOSTACK_CLIENT_REPO | Default client template repo |
| MACOSTACK_DOCS_REPO | Default docs system repo |
| MACOSTACK_REF | Default branch or tag |
Bring your own templates
The orchestrator is deliberately template-agnostic: it never hardcodes which features exist. If you find yourself writing if (module === "cors") in this CLI, it belongs in the template instead.
Point --owner, --server-repo and --client-repo at your own repositories. Bun templates expose a setup script in package.json; Laravel templates expose php artisan macostack:setup:
bun run setup --questionsorphp artisan macostack:setup --questionsprints a JSON spec of what the template can be asked — feature modules (the recommended set for a new app, and the set that is on right now), the auth bundle, cross-cutting middleware, start commands and fillable env keys.- The same command with
--yes --name <app> [--modules a,b] [--auth on|off] [--features a,b] [--env KEY=value] [--domain d]applies the answers.
Anything the template declares, the CLI renders — nothing more. Adding a new question to the wizard is one object in src/buckets.ts.
Development
bun install
bun run dev my-app --skip-install # run the CLI from source
bun run test # wiring contracts (buckets, sync paths)
bun run typecheckLicense
MIT © macobits
