create-bunship
| Install | |
|---|---|
npm install create-bunship |
|
| Latest Version: | 0.6.0 |
| License: | MIT |
| Last Updated: | Oct 16, 2026 |
| Links: | GitHub · npm |
create-bunship
Scaffold a production-ready Bun monorepo in one command — and ship it. Instead of maintaining its own boilerplate, bunship orchestrates each framework's official scaffolder non-interactively, then wires a single coherent toolchain on top.
bun create bunship my-startup # interactive
# or, fully non-interactive (AI-agent mode):
bunx create-bunship my-startup --web next --api nest --docker --cicd --json
Supported frameworks
Pick at most one per slot (none skips a slot). Apps are generated by the framework's
own scaffolder, so they always match upstream output.
| Slot | Framework | Flag | Scaffolded with | Dir | Dev port |
|---|---|---|---|---|---|
| Web | Next.js | --web next |
create-next-app |
apps/web |
3000 |
| Web | React + Vite | --web react-vite |
create-vite (react-ts) |
apps/web |
5173 |
| Web | Nuxt | --web nuxt |
create-nuxt |
apps/web |
3000 |
| Mobile | Expo | --mobile expo |
create-expo-app |
apps/mobile |
8081 |
| API | NestJS | --api nest |
@nestjs/cli (nest new) |
apps/api |
3001 |
| API | Express | --api express |
built-in minimal TS template | apps/api |
3001 |
| API | Hono | --api hono |
create-hono (bun template) |
apps/api |
3001 |
| API | Fastify | --api fastify |
fastify generate (ts) |
apps/api |
3001 |
Each app is then patched to fit the monorepo: workspace package name
(@<project>/<app>), non-conflicting dev ports, a /health endpoint on API stacks, and
formatting normalized with oxfmt.
What you get after running it
my-startup/
├── apps/
│ ├── web/ your chosen web framework (official scaffolder output)
│ ├── mobile/ Expo (if selected)
│ └── api/ your chosen API framework
├── package.json bun workspaces + scripts (below), engines pinned to Bun 1.3
├── .oxlintrc.json linting (oxlint)
├── .oxfmtrc.json formatting (oxfmt)
├── knip.json unused files / exports / deps
├── .husky/ pre-commit lint-staged · pre-push full check
├── .vscode/ settings + recommended extensions
├── AGENTS.md + CLAUDE.md conventions for AI coding agents
├── .gitignore · .gitattributes · .env.example · README.md
│
│ with --agents (default: claude):
├── scripts/agent-format.ts oxlint --fix + oxfmt on every file an agent edits
├── .claude/settings.json …and one tiny hook config per chosen agent
│
│ with --skills (default: stack):
├── .agents/skills/ SKILL.md best-practice playbooks per framework (+ .claude/skills, .windsurf/skills)
├── skills-lock.json pinned sources + hashes: bunx skills update
│
│ with --docker:
├── apps/*/Dockerfile per-app production images
├── compose.yaml local prod-parity: bun run docker:prod
├── dev.Dockerfile shared dev image (bun + node) for the dev stack
├── compose.dev.yaml every app's dev server, hot reload: bun run docker:dev
│
│ with --cicd (implies --docker):
├── .github/workflows/ ci.yml (check + build) · deploy.yml (GHCR → SSH → compose)
└── deploy/ VPS bundle: image-based compose, nginx + TLS, bootstrap guide
The generated root package.json gives you:
| Script | Does |
|---|---|
bun run dev |
all dev servers via workspace filter |
bun run dev:web / dev:api |
one app (one script per selected app) |
bun run build |
build every app |
bun run check |
lint + format:check + typecheck + knip |
bun run lint / format |
oxlint / oxfmt (with :fix / :check variants) |
bun run docker:dev |
every app in Docker with hot reload (--docker) |
bun run docker:prod |
local prod-parity run of the images CI deploys |
The CLI finishes by running git init, bun install, an oxfmt pass, and a first commit
— so every generated repo passes bun run check and bun run build from its very first
commit (enforced by this project's nightly CI matrix against latest upstream scaffolders).
The Docker dev stack
--docker writes two compose files. compose.yaml builds the per-app production images —
the same ones the deploy workflow ships. compose.dev.yaml runs each app's dev server
instead, all from one shared dev.Dockerfile, so you can bring the whole stack up in
containers while you work:
bun run docker:dev # docker compose -f compose.dev.yaml up --watch
bun run docker:prod # docker compose up --build
The --watch is load-bearing. There are no bind mounts — Compose Watch syncs your
edits into the running containers, and rebuilds when a package.json or bun.lock
changes (a new dependency can't be synced into an image whose node_modules is already
baked in). Bind mounts don't reliably deliver file-change events from Windows or macOS
hosts, which is exactly where hot reload silently stops working. Plain up (no --watch)
boots the same stack with the source frozen at image-build time.
Each framework's dev server is started so it binds 0.0.0.0 and is reachable through the
published port (-H 0.0.0.0 for Next, --host 0.0.0.0 for Vite and Nuxt,
FASTIFY_ADDRESS for Fastify; Nest, Express, and Hono already do). Expo is not in either
stack — Metro needs LAN reachability and QR pairing, so bun run dev:mobile on the host
stays the path.
Flags
| Flag | Meaning |
|---|---|
--web <next|react-vite|nuxt|none> |
Web frontend |
--mobile <expo|none> |
Mobile app |
--api <nest|express|hono|fastify|none> |
API backend |
--docker / --no-docker |
Dockerfiles + compose.yaml |
--cicd / --no-cicd |
GitHub Actions CI + VPS deploy pipeline (implies docker) |
--agents <claude,cursor,copilot,codex,gemini,windsurf|none> |
Format-on-edit hooks for AI coding agents (see below) |
--skills <stack,web-design,frontend-design|none> |
Best-practice skills for those agents (see below) |
--json |
Agent mode: no prompts, stdout is exactly one JSON manifest |
-y, --yes |
Defaults: next + nest + docker + cicd + claude hooks + stack skills |
--dry-run |
Print the manifest without writing anything |
--no-git, --no-install, --force, --verbose, --keep-on-error |
What they say |
Agent mode
--json makes the CLI a good citizen for AI coding agents: all progress goes to stderr,
stdout carries a single JSON manifest (apps, ports, scripts, required deploy secrets, next
steps), exit codes are meaningful (0 ok / 1 pipeline / 2 usage), and the generated repo
contains an AGENTS.md so agents don't re-derive conventions. Boilerplate comes from
official scaffolders, not from model output — that's the whole point.
Format-on-edit hooks for AI agents
An agent that edits a file rarely formats it. --agents fixes that at the source: every
file the agent touches is run through oxlint --fix + oxfmt the moment the edit lands,
using the exact rules lint-staged applies at commit — so by the time you look at the
diff, it is already clean.
One shared script does the work (scripts/agent-format.ts, run by bun, no extra
dependencies), and each agent gets a two-line config pointing at it:
| Agent | Config written | Fires on |
|---|---|---|
| Claude Code (recommended) | .claude/settings.json |
PostToolUse for Edit / Write |
| Cursor | .cursor/hooks.json |
afterFileEdit |
| GitHub Copilot (CLI + agent) | .github/hooks/format.json |
postToolUse for edit / create |
| OpenAI Codex | .codex/hooks.json |
PostToolUse for apply_patch |
| Gemini CLI | .gemini/settings.json |
AfterTool for write / replace |
| Windsurf | .windsurf/hooks.json |
post_write_code |
Pick several (--agents claude,cursor) if your team is mixed — the configs are
independent and all commit cleanly. The script also works by hand:
bun scripts/agent-format.ts path/to/file.ts.
Interactive runs preselect Claude Code; --yes picks it; --json writes none unless you
ask (agents shouldn't get hooks they didn't request). In interactive mode Esc goes back
one question (your earlier answer is preselected) and Ctrl+C quits.
Best-practice skills for AI agents
A hook keeps agent output formatted; a skill keeps it idiomatic. --skills installs
Agent Skills — SKILL.md playbooks an agent loads on demand —
for the agents you chose, using the same rule as the apps: nothing is vendored here, the
skills CLI fetches each one from its maintainer's repo at scaffold
time, so you get what they ship today.
Why bother: agents otherwise write React, Nest or Fastify from whatever their training data
remembers. A skill puts the framework's current conventions and pitfalls one file away —
Next.js measures a clear gain when agents read bundled docs instead of guessing, and the
per-framework skills below are the same idea for the rest of the stack. The generated
AGENTS.md tells the agent which skill to load before touching which app.
| Pack | Installs |
|---|---|
stack (default) |
One set per selected framework: Next.js / React + Vite → Vercel's vercel-react-best-practices (+ vercel-composition-patterns for Vite) · Nuxt → nuxt + vue (antfu) · Expo → expo-router, expo-project-structure, expo-native-ui, expo-data-fetching (Expo) · Hono → hono (Hono) · Fastify → fastify-best-practices (Matteo Collina) · Express → node (Matteo Collina) · NestJS → nestjs-best-practices (community; no first-party one exists yet) |
web-design |
Vercel's web-design-guidelines: 100+ accessibility/UX audit rules — "review my UI" gets a real checklist |
frontend-design |
Anthropic's frontend-design: opinionated visual direction for new UI instead of template defaults |
Where they land follows each agent's discovery rules: Cursor, Codex, Copilot and Gemini
read the shared .agents/skills/; Claude Code reads .claude/skills/ and Windsurf
.windsurf/skills/, so those get a copy (real copies, not symlinks — symlinks need
Developer Mode on Windows and the skills CLI silently writes nothing without it).
skills-lock.json pins sources and hashes; bunx skills update refreshes. Skill
directories are excluded from oxlint/oxfmt/knip so third-party content never breaks
bun run check.
A skill that cannot be fetched — repo renamed, offline — does not fail the run: it is
reported in the manifest (agentSkills.failed) and in a warning, and AGENTS.md lists
only what was installed. --skills needs at least one agent; --yes picks stack,
--json installs none unless asked.
Requirements
Bun ≥ 1.3 on PATH, plus git (unless --no-git). The CLI itself runs under Node ≥ 20,
so npx create-bunship works too.
Your chosen stack may need more, because framework CLIs (nuxt, next, vite, nest)
run under node via their bin shebang rather than under bun — so the node on your PATH
is what actually builds the generated repo:
| Stack | Needs |
|---|---|
| Nuxt | Node ≥ 22.19 |
| React + Vite | Node ≥ 20.19 |
| Next.js | Node ≥ 20.9 |
| NestJS | Node ≥ 20 |
bunship checks this during preflight and fails in about a second with the version it found, rather than letting you discover it as an upstream stack trace after a full install.
Related Packages
Scaffold the Nuxion NestJS + Nuxt/Next monorepo (Bun + Turbo) — bun create nuxio...
The Ultimate Multi-Tiered Project Orchestrator — an interactive CLI that scaffol...
Generate a complete Postman collection from your Node.js, Bun or Deno API (Expre...
Version History
| Version | Released | License |
|---|---|---|
| 0.6.0 | MIT | |
| 0.5.0 | MIT | |
| 0.4.0 | MIT | |
| 0.3.0 | MIT | |
| 0.2.2 | MIT | |
| 0.2.1 | MIT | |
| 0.2.0 | MIT | |
| 0.1.1 | MIT | |
| 0.1.0 | MIT |