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 restorefrom 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.
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.
updaterefuses a project whose manifest recordsfrontend: "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 runiniton 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.
| Artifact | Contents |
|---|---|
staging/ | The new version, shaped for your exact config. |
template/ | Pristine baseline - the version you started from, for the 3-way merge. |
staging.json | currentVersion to targetVersion, plus the combined changelog (one section per release spanned). |
template-hashes.json | Baseline per-file hashes that classify what you customized. |
manifest.json | License 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.
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 for | Because |
|---|---|
| A database schema change | Schema is never auto-merged; the migration is regenerated into your history. |
| A new required env var or config value | The app will not boot without a value only you can supply. |
| A newly available option | Adopting a feature is never assumed - each is offered with a recommendation. |
| A removed file another file still imports | Deleting it would break the importer, so you decide. |
| A file new upstream inside a tree you deleted | Auto-creating it would resurrect a feature you removed on purpose. |
| A merge it is not confident preserves your intent | When unsure, it asks rather than guess. |
| A merge that changes what the app does - a route, default, or flow | You see both versions side by side and pick. |
| A merge that changes how a screen looks - layout, your copy, or theme | You see both versions side by side and pick. |
| A verification failure it cannot confidently fix | A 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.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 project | The question |
|---|---|
| Desktop app ran AI through the old option | Keep 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 elsewhere | Keep 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 shape | No 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 option | Where the value lives now |
|---|---|
socialProviders | config.auth.socialProviders - starts empty, so add your provider IDs back or their sign-in buttons stop rendering |
billingScope | gone - money lives only on a user, and an organization is funded by its owner |
mcpServer | config.mcpServer.enabled - the code ships to every project, generated off |
emailTracking | config.email.tracking.enabled - generated on |
aiBuiltin | config.ai.builtin - generated on for any project with a payment provider |
aiByok | config.ai.byok - generated on |
desktopAutoRelease | gone - releases are always manual; add a workflow_run block to .github/workflows/desktop-release.yml to chain one to CI |
dockerServices | gone - infra/docker-compose.yml is derived from your stack; add extras by hand |
aiTools | gone - the skill bundle ships to every AI-tool root; delete the ones you do not use |
blog | gone - 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 |
runner | gone - 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) |
companionSource | gone - there is no daemon source to choose; the lane it belonged to is gone too |
companionName | gone - there is no install flow left to brand |
companionRepoUrl | gone - there is no install flow left to point anywhere |
companionLocalPath | gone - nothing in your project is built from a local daemon checkout |
errorTracking, companion | migrated 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 setconfig.email.tracking.enabledback tofalse, 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
| Question | Answer |
|---|---|
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. |
init
Scaffold the project and write the manifest update reads.
Manifest
Every field update reads, and how new options are surfaced and adopted.
Agent context
AGENTS.md, the docs silo, and the bundled skills update refreshes.
Licensing & heartbeat
The manifest fields update relies on to re-shape your version.
eject
The permanent opt-out - after it, update is gone.
Init
Scaffold a new SaaS project from the licensed boilerplate - answer grouped prompts to pick your stack and features, or pass every answer as a flag for non-interactive CI.
Agent context
What a generated project ships for AI coding agents - AGENTS.md, the docs silo, and the bundled update and translate skills installed into every tool root - and how update refreshes and eject removes them.