GenerateSaaS

Update

Pull the latest boilerplate version - the CLI stages a template re-shaped for your exact config, and your AI assistant applies the diff file by file while preserving your customizations.

generatesaas update keeps a generated project in sync with the boilerplate. Two actors share the work: the CLI stages the new version, and your AI assistant applies it file by file. It never edits your code on its own.

Why it is a guided merge

  • The CLI re-shapes the new version into your exact configuration, so a diff only ever shows changes that apply to your project.
  • Your assistant compares three versions of every file - the baseline you started from, the new version, and your edits - so an upstream improvement is told apart from your own work.
  • Untouched files are applied automatically; every file you customized is merged with the upstream change, and the run stops to ask only where a change is risky, uncertain, or alters what the app does or how it looks.
  • A full validation ladder - lint, types, tests, and the e2e suite - verifies the result, and the run happens on a clean git tree, so it is one git restore from undone.

update only writes to .generatesaas/. It never mutates your source.

What the command does

Run it from the project root, or pass --cwd <path>. The API key saved at ~/.generatesaas lets it run non-interactively.

Stop before anything is fetched if the manifest records frontend: "nuxt" - see Nuxt projects.

Verify the API key against the version index; a 401 re-prompts.

Refuse to continue if the installed CLI is older than the release requires - re-run as npx generatesaas@latest update.

Refresh the bundled AI skills in every tool root. If you are already on the latest version, stop here.

Refresh the license token for the target version. If your license's update window has ended, stop with the last version it covers.

Stage the new version and fetch the changelog for every release between your version and the target, so a multi-version jump never hides an intermediate change.

Backfill the pristine baseline if an older CLI never wrote one, then write .generatesaas/staging.json.

Nuxt projects

v2.4.1 is the last release that ships a Nuxt frontend. From v3.0.0 on, Next.js is the only one.

  • update refuses a project whose manifest records frontend: "nuxt", before it fetches, writes or stages anything.
  • There is no migration: Next.js and Nuxt share the backend packages but no page, component or route.
  • Your project keeps working. Stay on v2.4.1, or run init on Next.js when you want to move and port your own screens across.

What gets staged

Everything lands under .generatesaas/, all of it shaped for your project.

ArtifactContents
staging/The new version, shaped for your exact config.
template/Pristine baseline - the version you started from, for the 3-way merge.
staging.jsoncurrentVersion to targetVersion, plus the combined changelog (one section per release spanned).
template-hashes.jsonBaseline per-file hashes that classify what you customized.
manifest.jsonLicense token refreshed; the version bumps when the apply completes.

How your AI applies it

The bundled generatesaas-update skill drives the apply. It runs unattended, and stops to ask only where a change needs your decision.

Pre-flight: requires a clean git tree as a rollback point. It never commits, stashes, or resets without your explicit choice.

Classify each file: unmodified, modified, new, removed, or deleted-by-you.
Auto-apply only the safe files - unmodified replacements and genuinely-new ones.

Merge each modified file as a 3-way merge, keeping your customizations. It stops to ask - accept-merge, keep-mine, or take-upstream - only at a mandatory stop.

Validate with the full ladder - lint, type check, tests, dependency parity against the staged versions, an auth handler probe when auth changed, and the e2e suite (pnpm infra, then pnpm e2e:ci) - then finish. Static gates alone cannot see hook-level auth breakage or missing assets.

Deploy deliberately - if the host auto-deploys on push, nothing is pushed until the ladder is green, and CI is watched to its conclusion.

Where it stops to ask

The apply is unattended, but it always stops for a decision only you can make. Everything else - untouched files and clean merges - is applied without a prompt.

It stops forBecause
A database schema changeSchema is never auto-merged; the migration is regenerated into your history.
A new required env var or config valueThe app will not boot without a value only you can supply.
A newly available optionAdopting a feature is never assumed - each is offered with a recommendation.
A removed file another file still importsDeleting it would break the importer, so you decide.
A file new upstream inside a tree you deletedAuto-creating it would resurrect a feature you removed on purpose.
A merge it is not confident preserves your intentWhen unsure, it asks rather than guess.
A merge that changes what the app does - a route, default, or flowYou see both versions side by side and pick.
A merge that changes how a screen looks - layout, your copy, or themeYou see both versions side by side and pick.
A verification failure it cannot confidently fixA red gate is surfaced, never worked around.

Answering the CLI's questions

A few decisions belong to you alone - the desktop-AI shape and the two money- or privacy-affecting retired options below. Your AI assistant runs update without a terminal, so the CLI cannot prompt it. Instead:

update prints the question with a stable id and its allowed choices, then exits non-zero. Nothing is staged.
Your assistant relays the question and choices to you, and collects your answer.
It re-runs with your choice: npx generatesaas@latest update --answer <id>=<choice>. The flag repeats, one per question, and an unknown id or choice is rejected with the valid values.

The desktop-AI migration

The separate desktop-AI option is retired: a desktop app's AI now follows the AI option (--ai). A project that predates the change gets one blocking question before anything is staged, because turning AI on exposes your website's AI surface and turning it off deletes a working desktop AI stack.

Your projectThe question
Desktop app ran AI through the old optionKeep AI on to keep the desktop AI stack (hide the website surface with config.ai.enabled: false), or turn it off and the app becomes a client shell.
Desktop app has no AI, but AI is on elsewhereKeep AI and the desktop app gains the local AI stack and the Terminal, or drop AI, or drop the desktop app.
Already on the new shapeNo question at all.

Your answer is written to the manifest before the new version is shaped. A non-TTY run never guesses: it names the decision and its --answer id instead (see Answering the CLI's questions).

Retired init-time options

Several init-time options became plain values you edit in your own project. update compares each recorded value against what the new version emits, tells you about every one it is about to change, then drops the dead field. A value that already matches, or that describes a feature your project does not generate, is dropped silently.

Recorded optionWhere the value lives now
socialProvidersconfig.auth.socialProviders - starts empty, so add your provider IDs back or their sign-in buttons stop rendering
billingScopegone - money lives only on a user, and an organization is funded by its owner
mcpServerconfig.mcpServer.enabled - the code ships to every project, generated off
emailTrackingconfig.email.tracking.enabled - generated on
aiBuiltinconfig.ai.builtin - generated on for any project with a payment provider
aiByokconfig.ai.byok - generated on
desktopAutoReleasegone - releases are always manual; add a workflow_run block to .github/workflows/desktop-release.yml to chain one to CI
dockerServicesgone - infra/docker-compose.yml is derived from your stack; add extras by hand
aiToolsgone - the skill bundle ships to every AI-tool root; delete the ones you do not use
bloggone - content is no longer an option. Every project ships the /blog, /alternatives and /compare routes; each one 404s, and stays out of your navbar, sitemap and llms.txt, until you add a file. Delete an entry from config.content.sections to drop a section
runnergone - the paired-device lane was removed, so pairing, the dispatch transport, the relay, the /runner/* routes and the Runners page leave your project; chat and automations keep running on your own AI backend (config.ai)
companionSourcegone - there is no daemon source to choose; the lane it belonged to is gone too
companionNamegone - there is no install flow left to brand
companionRepoUrlgone - there is no install flow left to point anywhere
companionLocalPathgone - nothing in your project is built from a local daemon checkout
errorTracking, companionmigrated automatically - errorTracking becomes observability, and companion is read as the runner row above

Two of them are blocking questions, not warnings, because applying them for you does something you cannot undo. A non-TTY run refuses to guess them and exits with the --answer id. Every other retirement prints as a warning, before the first question, so one run tells you everything.

  • billingScope - organization billing was removed, not moved, so there is nothing to put back. An organization has no balance, plan, or payment-provider customer; the payer is its owner, and every credit spent in that workspace comes off the owner's balance. This update does not migrate stored balances. Move each paying organization's credits and active plan onto its owner before you take another checkout, or customers lose entitlements they already paid for. Payments in flight are safe: a webhook arriving with organization metadata is credited to that organization's owner rather than dropped.
  • emailTracking - if you recorded tracking off, the staged config turns it on, so opens and clicks are recorded for every user you mail. Continue and set config.email.tracking.enabled back to false, or stop and edit it first.

Stopping is always safe: nothing is written or staged.

Running it

Tell your AI assistant: "update my GenerateSaaS project." It runs the CLI for you, then walks the diff.

Or run npx generatesaas@latest update yourself first, then ask the assistant to apply the staged update.

Review each change, then run pnpm install if dependencies changed, plus any migrations the changelog lists.

Common questions

QuestionAnswer
Does it touch my lockfile or .env?Never. The lockfile is excluded from update diffs entirely - new dependencies land in package.json only. Real .env files are left alone.
I skipped a file. Is that change lost?No. Skipped files are recorded in held-back.json, so the next update's diff still includes the change you deferred.
Do upstream migration files land in my project?No. Everything under packages/database/drizzle/ is held back, because a pasted-in migration sorts below your own history and never runs. The assistant merges the schema change, then regenerates the migration into your history. See Database.
I am already on the latest version.It refreshes the skill bundle, reports "already on the latest," and stops - nothing is staged, the license token is untouched.
My update window has ended.Versions released after it are not served. update stops with the last version your license covers, and your project keeps working as-is.
My project is newer than the latest release.It refuses and stages nothing, so an older template is never written over your project. Check that your API key points at the right account, or correct version in the manifest.

On this page