Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,4 @@ runs:
node-version: 22

- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9.6.0
uses: pnpm/action-setup@v4
4 changes: 1 addition & 3 deletions .github/workflows/build-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ jobs:
node-version: ${{ matrix.node-version }}

- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9.6.0
uses: pnpm/action-setup@v4

- name: Install dependencies
run: pnpm install
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/build-templates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Patch template package manager for CI
run: |
node -e "const fs = require('node:fs'); const file = 'templates/repo/package.json'; const content = fs.readFileSync(file, 'utf8'); const updated = content.replace(/PACKAGE_MANAGER@PACKAGE_MANAGER_VERSION/g, 'pnpm@10.28.2').replace(/PACKAGE_MANAGER/g, 'pnpm'); fs.writeFileSync(file, updated);"

- name: Install template dependencies
run: pnpm install --frozen-lockfile
working-directory: ./templates/repo
Expand Down Expand Up @@ -67,6 +71,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Patch template package manager for CI
run: |
node -e "const fs = require('node:fs'); const file = 'templates/repo/package.json'; const content = fs.readFileSync(file, 'utf8'); const updated = content.replace(/PACKAGE_MANAGER@PACKAGE_MANAGER_VERSION/g, 'pnpm@10.28.2').replace(/PACKAGE_MANAGER/g, 'pnpm'); fs.writeFileSync(file, updated);"

- name: Build packages
run: pnpm --filter "./packages/**" build

Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ jobs:
registry-url: 'https://registry.npmjs.org'

- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9.6.0
uses: pnpm/action-setup@v4

- name: Install dependencies
run: pnpm install
Expand Down
53 changes: 51 additions & 2 deletions .github/workflows/test-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 9.6.0
run_install: false

- uses: actions/setup-node@v4
Expand Down Expand Up @@ -48,7 +47,6 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 9.6.0
run_install: false

- uses: actions/setup-node@v4
Expand Down Expand Up @@ -100,3 +98,54 @@ jobs:
if: always()
run: rm -rf test-projects

test-cli-bun:
name: CLI Integration - Bun Install
runs-on: ubuntu-latest
needs: test-cli

steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
run_install: false

- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "pnpm"

- name: Install dependencies and build
run: |
pnpm install --frozen-lockfile
pnpm --filter startupkit build

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Create and test bun project
run: |
mkdir -p test-projects
cd test-projects

echo "🔍 Creating project with bun..."
node "$GITHUB_WORKSPACE/packages/cli/dist/cli.js" init --name "test-startup-bun" --repo "ian/startupkit#${{ github.ref_name }}" --package-manager bun

echo "📂 Verifying packages directory exists..."
test -d test-startup-bun/packages || exit 1

echo "🔍 Running typecheck..."
cd test-startup-bun
bun run typecheck

echo "🏗️ Running build..."
bun run build

echo "✅ Bun install test passed"

- name: Clean up
if: always()
run: rm -rf test-projects

44 changes: 23 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ npx startupkit init

Without structure, every project becomes a different architecture. That's **AI slop**.

| Without StartupKit | With StartupKit |
|-------------------|-----------------|
| Where should auth logic live? | `@repo/auth` → Better Auth, ready |
| Prisma or Drizzle? Which pattern? | `@repo/db` → Drizzle + Postgres, configured |
| App router or pages? RSC or client? | Next.js 16 App Router, RSC by default |
| How do I structure shared code? | Monorepo → share everything |
| Which analytics provider? | `@repo/analytics` → Provider-agnostic hooks |
| Without StartupKit | With StartupKit |
| ----------------------------------- | ------------------------------------------- |
| Where should auth logic live? | `@repo/auth` → Better Auth, ready |
| Prisma or Drizzle? Which pattern? | `@repo/db` → Drizzle + Postgres, configured |
| App router or pages? RSC or client? | Next.js 16 App Router, RSC by default |
| How do I structure shared code? | Monorepo → share everything |
| Which analytics provider? | `@repo/analytics` → Provider-agnostic hooks |

**Start at 70%.** AI handles the details, not the foundation.

Expand All @@ -35,7 +35,7 @@ Without structure, every project becomes a different architecture. That's **AI s
StartupKit is designed to work seamlessly with AI development tools:

- ✅ **Devin** ready
- ✅ **Claude** ready
- ✅ **Claude** ready
- ✅ **Amp** ready
- ✅ **OpenCode** ready

Expand All @@ -47,11 +47,13 @@ Every project includes `AGENTS.md` with clear conventions, file placement guidel
npx startupkit init
cd my-project
cp .env.example .env.local
pnpm dev
pnpm dev # or: bun dev
```

Visit [http://localhost:3000](http://localhost:3000)

**Package Manager:** StartupKit supports both [pnpm](https://pnpm.io) and [bun](https://bun.sh). Use whichever you prefer.

## What's Included

### 📦 Pre-Built Packages
Expand Down Expand Up @@ -107,32 +109,32 @@ my-project/
### Development

```bash
pnpm dev # Start all apps
pnpm --filter web dev # Start specific app
pnpm build # Build all packages
pnpm dev # Start all apps (or: bun dev)
pnpm --filter web dev # Start specific app (or: bun --filter web dev)
pnpm build # Build all packages (or: bun run build)
```

### Database

```bash
pnpm db:generate # Generate migration files
pnpm db:migrate # Apply migrations
pnpm db:studio # Open database GUI
pnpm db:generate # Generate migration files (or: bun db:generate)
pnpm db:migrate # Apply migrations (or: bun db:migrate)
pnpm db:studio # Open database GUI (or: bun db:studio)
```

### UI Components

```bash
pnpm shadcn add button
pnpm shadcn add dialog
pnpm shadcn add button # or: bun shadcn add button
pnpm shadcn add dialog # or: bun shadcn add dialog
```

### Code Quality

```bash
pnpm lint # Check all files
pnpm lint:fix # Fix issues
pnpm typecheck # Type check all packages
pnpm lint # Check all files (or: bun lint)
pnpm lint:fix # Fix issues (or: bun lint:fix)
pnpm typecheck # Type check all packages (or: bun typecheck)
```

## Add New Services
Expand Down Expand Up @@ -168,7 +170,7 @@ NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
- **Database:** PostgreSQL + Drizzle ORM
- **Auth:** Better Auth
- **Email:** React Email + Resend
- **Monorepo:** pnpm + Turbo
- **Monorepo:** pnpm or bun + Turbo
- **Linting:** Biome

## Support & Resources
Expand Down
112 changes: 56 additions & 56 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
{
"name": "startupkit",
"private": true,
"author": "Ian Hunter <ian@01.studio>",
"description": "The 0 to 1000 SaaS Startup Framework",
"license": "ISC",
"type": "module",
"packageManager": "pnpm@9.6.0",
"scripts": {
"clean": "pnpm --stream -r run clean",
"nuke": "find . -name 'node_modules' -type d -exec rm -rf {} +",
"build": "pnpm build:packages && pnpm --stream -r --filter \"./apps/**\" run build",
"build:packages": "pnpm --stream -r --filter \"./packages/**\" run build",
"dev": "turbo run dev --continue --ui stream",
"link:local": "node scripts/link-local.js link",
"unlink:local": "node scripts/link-local.js unlink",
"format": "turbo run format --continue --ui stream",
"format:fix": "turbo run format:fix --continue --ui stream",
"lint": "turbo run lint --continue --ui stream",
"lint:fix": "turbo run lint:fix --continue --ui stream",
"typecheck": "turbo run typecheck --ui stream",
"bump": "./scripts/bump",
"release": "pnpm build:packages && pnpm --filter \"./packages/**\" -r publish --access public --no-git-checks",
"with-env": "dotenv -e .env.local --",
"with-test-env": "dotenv -e .env.test --",
"agents.md": "ruler apply",
"startupkit": "node packages/cli/dist/cli.js"
},
"pnpm": {
"overrides": {
"react": "19.0.0",
"react-dom": "19.0.0",
"@types/react": "19.0.1",
"@types/react-dom": "19.0.2",
"drizzle-orm": "0.38.4",
"prettier": "3.4.2"
}
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@intellectronica/ruler": "^0.3.11",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-swc": "^0.3.1",
"@swc/core": "^1.7.6",
"dotenv-cli": "7.4.4",
"glob": "^11.0.3",
"pg": "^8.13.1",
"prettier": "3.4.2",
"rollup": "^4.20.0",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-preserve-directives": "^1.1.1",
"tsup": "^8.2.4",
"turbo": "2.5.0",
"typescript": "5.9.2",
"validate-package-exports": "^0.6.0"
}
}
"name": "startupkit",
"private": true,
"author": "Ian Hunter <ian@01.studio>",
"description": "The 0 to 1000 SaaS Startup Framework",
"license": "ISC",
"type": "module",
"packageManager": "pnpm@10.28.2",
"scripts": {
"clean": "pnpm --stream -r run clean",
"nuke": "find . -name 'node_modules' -type d -exec rm -rf {} +",
"build": "pnpm build:packages && pnpm --stream -r --filter \"./apps/**\" run build",
"build:packages": "pnpm --stream -r --filter \"./packages/**\" run build",
"dev": "turbo run dev --continue --ui stream",
"link:local": "node scripts/link-local.js link",
"unlink:local": "node scripts/link-local.js unlink",
"format": "turbo run format --continue --ui stream",
"format:fix": "turbo run format:fix --continue --ui stream",
"lint": "turbo run lint --continue --ui stream",
"lint:fix": "turbo run lint:fix --continue --ui stream",
"typecheck": "turbo run typecheck --ui stream",
"bump": "./scripts/bump",
"release": "pnpm build:packages && pnpm --filter \"./packages/**\" -r publish --access public --no-git-checks",
"with-env": "dotenv -e .env.local --",
"with-test-env": "dotenv -e .env.test --",
"agents.md": "ruler apply",
"startupkit": "node packages/cli/dist/cli.js"
},
"pnpm": {
"overrides": {
"react": "19.0.0",
"react-dom": "19.0.0",
"@types/react": "19.0.1",
"@types/react-dom": "19.0.2",
"drizzle-orm": "0.38.4",
"prettier": "3.4.2"
}
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@intellectronica/ruler": "^0.3.11",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-swc": "^0.3.1",
"@swc/core": "^1.7.6",
"dotenv-cli": "7.4.4",
"glob": "^11.0.3",
"pg": "^8.13.1",
"prettier": "3.4.2",
"rollup": "^4.20.0",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-preserve-directives": "^1.1.1",
"tsup": "^8.2.4",
"turbo": "2.5.0",
"typescript": "5.9.2",
"validate-package-exports": "^0.6.0"
}
}
26 changes: 24 additions & 2 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ import { init } from "./cmd/init"
import { initRalphConfig, make } from "./cmd/make"
import { upgrade } from "./cmd/upgrade"

interface InitCommandOptions {
name?: string
repo?: string
dir?: string
packageManager?: string
}

function parsePackageManager(
value: string | undefined
): "pnpm" | "bun" | undefined {
if (!value) return undefined
const normalized = value.trim().toLowerCase()
if (normalized === "pnpm" || normalized === "bun") return normalized
throw new Error(`Unsupported package manager: ${value}. Use "pnpm" or "bun".`)
}

export async function run() {
const program = new Command()

Expand All @@ -17,11 +33,17 @@ export async function run() {
.option("--name <name>", "Name of the app")
.option("--repo <repo>", "Template repo to use")
.option("--dir <dir>", "Directory to create project in (use . for current)")
.action(async (options) => {
.option(
"--package-manager <packageManager>",
"Package manager to use (pnpm or bun)"
)
.action(async (options: InitCommandOptions) => {
const packageManager = parsePackageManager(options.packageManager)
await init({
name: options.name,
repoArg: options.repo,
dir: options.dir
dir: options.dir,
packageManager
})
})

Expand Down
Loading
Loading