gv-kit
v0.2.0
Published
Scaffolding CLI for Turborepo monorepos (SvelteKit + Hono on CF Workers)
Maintainers
Readme
gv-kit
Scaffolding CLI for full-stack product monorepos. Pick your project shape and stack, answer prompts, and get a project that compiles, tests, and deploys.
Quickstart
npx gv-kit new my-app
cd my-app
pnpm install
pnpm devWhat you get
A Turborepo monorepo with:
apps/marketing— static-first Astro marketing site at the apex (recommended project shape)apps/web— SvelteKit (Svelte 5 runes, Tailwind 4) on your chosen runtimeapps/api/auth+apps/api/users— independent Hono Workers (services container) whenbackend=honopackages/backend— primitives only:core,authfactory,helpers, plusmiddlewarefor hono modepackages/db— Drizzle (Postgres or SQLite, deploy-aware driver)packages/i18n— Paraglide v2 (optional)packages/openapi-client— Hey API per-service codegen + TanStack Query (optional)packages/mailer— Resend or Notifuse adapter (optional).claude/+AGENTS.md— AI tooling, multi-select between Claude / Codex / Opencode
Decision tree
| Choice | Options | Default |
|---|---|---|
| name | kebab-case | (required) |
| frontend | sveltekit | sveltekit |
| marketing | astro (separate marketing + app) / inside-web (integrated) | astro |
| backend | hono (services container) / inside-frontend (one Worker) | hono |
| i18n | paraglide / skip | skip |
| monitoring | multi: umami, posthog | [] |
| db | postgres / sqlite | sqlite |
| apiClient | hey-api / skip (must be skip if backend is inside-frontend) | skip |
| auth | multi: emailOTP, google | [] |
| email | resend / notifuse / skip | skip |
| aiTooling | multi: claude, codex, opencode | [] |
| deploy | cf-workers / docker / skip | skip |
Stack matrix
| Layer | Pin |
|---|---|
| Node | >=24.0.0 <25.0.0 |
| pnpm | @11.1.1 (pinned via packageManager) |
| TypeScript | ~5.9.0 |
| Astro | ^7.1.1 |
| @astrojs/svelte | ^9.0.1 |
| @astrojs/sitemap | ^3.7.3 |
| Zod | ^4.3.0 (workspace overrides) |
| Hono | ^4.12.0 |
| @hono/zod-openapi | ^1.3.0 |
| Drizzle ORM | ^0.45.0 |
| drizzle-zod | ^0.8.3 |
| better-auth | ^1.6.0 |
| @hey-api/openapi-ts | ^0.97.3 |
| @hey-api/client-fetch | ^0.13.1 |
| @tanstack/svelte-query | ^6.1.33 |
| SvelteKit | ^2.58.0 |
| Svelte | ^5.56.6 |
| Vite | ^8.0.0 |
| @sveltejs/vite-plugin-svelte | ^7.0.0 |
| Tailwind | ^4.3.3 (@theme only, no JS config) |
| @inlang/paraglide-js | ^2.17.0 (no paraglide-sveltekit) |
| Wrangler | ^4.112.0 (always wrangler.jsonc) |
| Turbo | ^2.9.0 |
| Changesets | ^2.31.0 |
Architecture
User answers prompts → choices validated by Zod → pure generators emit FileEntry[] → user confirms → files written + pnpm install + git init.
Pipeline stages: collect → validate → plan → confirm → execute. Generators are pure functions (config) => FileEntry[]. No I/O inside generators.
Project shapes
The recommended marketing=astro shape keeps the public site and application separate:
apps/marketing Astro static site https://<domain>
apps/web SvelteKit app https://app.<domain>Astro owns the homepage, canonical metadata, robots, localized sitemap, and 404. Its CTA reads the complete build-time PUBLIC_APP_URL; it does not inspect auth or derive a hostname. When app auth is selected, anonymous visits to the SvelteKit root go to /login and authenticated visits go to /me.
The marketing=inside-web shape retains the previous single-SvelteKit topology, with its public page and application together at the apex. Existing v1 configs migrate to this shape. New configs are strict v2, so older CLIs cannot silently ignore the topology choice.
Astro remains static-first on both supported deploy targets: Workers Static Assets serves dist/ directly, while Docker serves the same output from an unprivileged nginx runtime. Local defaults are marketing on http://localhost:4321 and the app on http://localhost:5173 (or :3000 under Compose).
Service boundary (when backend=hono)
apps/api/auth is the only Worker that:
- holds
BETTER_AUTH_SECRETand OAuth secrets - imports
@repo/backend/auth - exposes
/api/auth/*(public) and/internal/session(binding-only RPC)
apps/api/users and any future service:
- declare a CF service binding to
<project>-auth - call
/internal/sessionvia inline ~10 LOCauth-client.ts - never read auth secrets, never query the auth tables
This is a hard boundary — no service-client factory in packages/backend, no auth/types subpath, no shared SDK package.
Inside-frontend mode
When backend=inside-frontend, the SvelteKit Worker is the only deployable surface. Auth, API routes, and the Hono app all live in apps/web via +server.ts. The same @repo/backend/auth factory is consumed directly. One Worker, no cross-Worker secret leakage possible.
Commands
bun run dev # run CLI from source
bun run build # bundle to dist/cli.js
bun test # schema + plan-snapshot + contract tests
bun run snap # regenerate snapshots (UPDATE_SNAPSHOTS=1)
bun run typecheck # tsc --noEmit
bun run lint # eslintContributing
- Add or change a fixture under
fixtures/ bun run snapto regenerate snapshots- Inspect the diff carefully — snapshots are intentional
- Commit fixture + snapshot together
bun changeset addto record the change
License
MIT — see LICENSE.
