GenerateSaaS

Update

Pull the latest boilerplate version - the CLI stages a re-shaped template and your AI assistant applies the diff while preserving your customizations.

generatesaas update (packages/cli/src/commands/update.ts) 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 this is different

Every boilerplate sells lifetime updates. Almost nobody applies them. The normal path is a manual git merge of your project against thousands of changed upstream lines - then resolving every conflict by hand, hoping you do not break something you cannot see. So projects drift months behind, and the "lifetime updates" go unused.

GenerateSaaS makes updating a guided, AI-driven three-way merge instead:

  • The CLI re-shapes the new version into your exact configuration - same frontend, payment provider, database, and feature flags - so a diff only ever shows changes that actually apply to your project. You never sift through another framework's files or a provider you don't use.
  • Your AI assistant compares three versions of every file - the original baseline you started from, the new version, and your customizations - so it can tell an upstream improvement apart from your own edits.
  • Files you never touched are applied automatically. Files you customized are walked one by one, each with a plain-language explanation, a risk rating, and a recommended merge - and you approve every one.
  • A type check and the test suite verify the result before you finish, and the whole thing runs on a clean git tree so it is always one git restore from undone.

The result: staying current is a short, reversible, low-risk routine - not a dreaded merge you keep putting off.

update only writes to .generatesaas/. It never mutates your source - applying the staged diff is a separate, reviewed step driven by your AI assistant.

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.

Verify the API key against the version index; a 401 re-prompts.
Refuse to continue if the installed CLI is older than the latest release requires - re-run as npx generatesaas@latest update.
Refresh the AI skill files.
If you are already on the latest version, stop here.
Refresh the license token in the manifest for the target version. If your license's update window has ended, the update stops here with the last version it covers.
Stage the new version, re-shaped for this project's exact config, into .generatesaas/staging/, and fetch the changelog for every release between your version and the target - breaking releases are flagged, so a multi-version jump never hides an intermediate breaking change.
Backfill the pristine baseline (.generatesaas/template/ + hashes) if an older CLI never wrote one.
Write .generatesaas/staging.json and prompt you to ask your AI assistant to apply it.

What gets staged

Everything lands under .generatesaas/ - all of it buyer-shaped, so a diff never shows another framework, a provider you don't use, internal tooling, or the owner key.

ArtifactContents
staging/The new version, shaped for your exact config.
template/Pristine baseline - the version you started from, for a 3-way merge.
staging.jsoncurrentVersiontargetVersion 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 update skill drives the apply, and you decide every change:

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.
Review each modified file as a 3-way merge (old template, new template, your version) and pick accept-merge, keep-mine, or take-upstream.
Validate with the type check and the test suite, then finish.

Pick an interaction level when prompted: Recommended (auto-apply safe, walk the modified files), Careful (approve everything), or Quick (auto-merge low-risk, ask on medium/high).

The desktop-AI migration

The separate desktop-AI option is retired: a desktop app's AI now follows the AI option (--ai). If your project predates that change, update asks one blocking question before it stages anything - because turning AI on exposes your website's AI surface, and turning it off deletes a working desktop AI stack, and neither is a call an update should make for you. Your answer is written to the manifest before the new version is shaped, so the re-shape matches your choice.

Which question you get depends on your project's shape:

  • Your desktop app ran AI through the old desktop-AI option. Keep it? Turn AI on to keep the desktop AI stack - this also adds the companion daemon's source to your repo (apps/companion, ~106MB per packaged build) and makes your website's AI surface available (keep it hidden with config.ai.enabled: false). Or turn desktop AI off and the app becomes a client shell (no AI, no Terminal, no bundled daemon).
  • Your desktop app has no AI, but your project has AI on elsewhere. Keep it? Keeping AI means the desktop app gains the local AI stack, the Terminal, and the bundled companion daemon (apps/companion, ~106MB per packaged build). Or drop AI (this also drops the web companion - it requires AI), or drop the desktop app and leave your website's AI exactly as it is.
  • Your desktop app bundles a daemon, but your web install flow points at a shared companion. A bundled app needs its own daemon, so this contradiction is resolved explicitly: vendor your own companion (its source moves into apps/companion and your web install UI switches to it), or drop the desktop app and keep the shared companion as-is. Your explicit shared choice is never silently rewritten.
  • Your project is already on the new shape (desktop + AI, no web companion): no question, but if it did not carry the daemon source yet, the update materializes it - apps/companion and about 106MB per packaged build - and the summary says so rather than springing it.

Running update auto (the unattended mode) never guesses: it stops and names the decision so you can run generatesaas update interactively and answer it. Cancelling the question aborts before anything is staged - nothing is written or shaped.

Retired init-time options

Eight init-time options became plain values you edit in your own project. Nothing reads them from the manifest any more, so a project that recorded one gets whatever the current generators write - and because an untouched config file is auto-applied, that would reset your original choice without a word. update compares each recorded value against what the new version emits and tells you about every one it is about to change, then drops the dead field so it never comes back.

Recorded optionWhere the value lives now
socialProvidersconfig.auth.socialProviders - starts empty, so add your provider IDs back or their sign-in buttons stop rendering
billingScopebillingScope in packages/config/src/tenancy-flags.mjs - generated as user
mcpServerconfig.mcpServer.enabled - the server's code now 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
companionLocalPaththe GENERATESAAS_COMPANION_BRAND_JSON env var, pointed at a local brand.json
desktopAutoReleasegone - releases are always manual (workflow_dispatch); add a workflow_run block to .github/workflows/desktop-release.yml to chain it to CI

A value that already matches what the new version writes is dropped silently, and so is one that describes a feature your project does not generate (a billing scope on a single-tenant project, a release trigger on a project with no desktop app).

Two of them are blocking questions, not warnings. Applying these on your behalf does something you cannot undo after the fact, so update asks before it stages anything - and update auto refuses to guess them and exits, exactly like the desktop-AI question. Every other retirement is a one-line config edit reported as a warning, and all the warnings print before the first question so one run tells you everything.

  • billingScope - if you recorded organization-scoped billing, the staged config bills the individual member instead: new checkouts create member-scoped subscriptions and org-level entitlements stop resolving. Continue and put billingScope: "organization" back in packages/config/src/tenancy-flags.mjs, or stop and edit it first.
  • 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.

In every case, stopping is safe: nothing is written or staged. A file you have already edited is walked as a 3-way merge instead of being replaced.

Switching the companion source

A companion-ON project can flip between pointing at a shared companion repo and vendoring its own at any time: ask your AI assistant to "switch my companion to shared" (or vendored) during an update. The flip is a manifest edit (companionSource) plus a re-stage - vendored -> shared removes apps/companion and the export pipeline (local daemon customisations are abandoned, so the assistant confirms first), while shared -> vendored vendors and brands them from your chosen companion name and starts in the publish-first state until you run pnpm companion:publish.

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.

Frequently asked questions

Do I run the CLI or does my assistant? Either. Saying "update my GenerateSaaS project" lets the assistant run generatesaas update itself; the saved API key makes it non-interactive.

Will it overwrite my customizations? No. Only uncustomized files are auto-applied. Every file you changed is reviewed one by one, and the pre-flight refuses to start on a dirty tree.

What if I'm already on the latest version? It refreshes your skill files, reports "already on the latest," and stops - nothing is staged and the license token is left untouched.

What if my update window has ended? Versions released after your window are not served. update stops with the last version your license covers and an upgrade pointer; your project keeps working as-is, and init can still scaffold that last covered version.

I rewrote a file heavily. What happens? You get a 3-way merge and choose the outcome. Skipped files are recorded in held-back.json so the next update's diff still includes the change you deferred. A safety net in the completion step also detects upstream changes that were never applied and records them automatically.

Does it touch my lockfile or .env? Never. The lockfile is excluded from update diffs entirely - new dependencies are added to package.json only, and you run pnpm install after. Real .env files are left alone.

On this page