generatesaas 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -122,8 +122,8 @@ Creates:
122
122
  `added` files; `unmodified`, `deleted`, and `removed` files have none.
123
123
  - `references/update-manifest.json` - lists of added, modified, and removed files,
124
124
  plus `renamed` (upstream renames detected by identical content: `[{from, to}]`)
125
- and `sensitive` (database schema, `packages/database/drizzle/`, and
126
- `.env.example` changes that carry follow-up work: migrations, new env vars)
125
+ and `sensitive` (database schema and `.env.example` changes, which carry follow-up work:
126
+ a schema push, new env vars)
127
127
 
128
128
  ### Step 2: Present Changelog
129
129
 
@@ -134,7 +134,7 @@ Each release's notes may be one of two shapes:
134
134
  - **Curated** - contains explicit `## Breaking`, `## Migration`, `## Features`, `## Fixes` (or similar) sections. Use those sections verbatim; they are authoritative.
135
135
  - **Raw** - an auto-generated flat list of change titles (one line per commit or PR, no sections). This is the common case. You can group the titles into features/fixes by reading them, but you **cannot** reliably infer breaking changes or migration steps from titles alone.
136
136
 
137
- Also read `sensitive` in `references/update-manifest.json`: database schema changes mean a migration is likely required, anything under `packages/database/drizzle/` means upstream migrations that must be regenerated into the project's own history before they will run (see [Database](#database-schema-migrations)), and `.env.example` changes mean new or changed environment variables. Mention all three in the summary and again in the final post-update steps.
137
+ Also read `sensitive` in `references/update-manifest.json`: database schema changes mean the live database has to be pushed to match, and may drop or rewrite existing data (see [Database](#database-schema)), and `.env.example` changes mean new or changed environment variables. Mention both in the summary and again in the final post-update steps.
138
138
 
139
139
  Present a clear, organized summary:
140
140
 
@@ -143,9 +143,9 @@ Present a clear, organized summary:
143
143
  > **Breaking changes:** {from a Breaking section, or "⚠️ Not specified in the changelog - review the diffs in Step 7 carefully, especially database/schema and config files"}
144
144
  > **New features:** {list}
145
145
  > **Bug fixes:** {list}
146
- > **Migration steps required:** {from a Migration section, or "⚠️ None listed - if this update changes the database schema or required env vars, you may still need to migrate. I'll flag schema/config changes as we review them."}
146
+ > **Follow-up steps required:** {from a Migration section, or "⚠️ None listed - if this update changes the database schema or required env vars, you may still have follow-up work. I'll flag schema/config changes as we review them."}
147
147
 
148
- **Be honest about uncertainty.** Never present an inferred "Breaking changes: None" or "Migration steps: None" as if it were authoritative when the changelog is raw PR titles - say it's not specified and that you'll watch for schema/config/env changes during the file review. A silently-missed migration (e.g. a new DB column) is the worst failure mode.
148
+ **Be honest about uncertainty.** Never present an inferred "Breaking changes: None" or "Follow-up steps: None" as if it were authoritative when the changelog is raw PR titles - say it's not specified and that you'll watch for schema/config/env changes during the file review. A silently-missed schema change (e.g. a new DB column that never reaches the database) is the worst failure mode.
149
149
 
150
150
  Then ask: *"Ready to proceed with the file analysis?"*
151
151
 
@@ -341,15 +341,13 @@ Preview first (especially useful in Careful mode or when unsure), then apply:
341
341
  node __SKILL_ROOT__/generatesaas-update/scripts/apply-auto.js --dry-run # lists every file it would update/create, changes nothing
342
342
  node __SKILL_ROOT__/generatesaas-update/scripts/apply-auto.js
343
343
  ```
344
- `apply-auto.js` copies only `unmodified` (safe replace) + `new` (any upstream-added file whose tree the buyer has not deleted). It will **never** touch `newInDeletedTree` files, and it aborts without writing anything if any source is missing from staging (no half-applied tree). Report what was applied. Mark all auto-update and new file items as `[x]` in the plan - **except any file the script reports as held back**, which was never applied and moves to **Skipped** with the regenerate reason. Marking a held-back migration `[x]` records a table that does not exist as delivered, in the one artifact that outlives the session.
345
-
346
- It also holds back everything under `packages/database/drizzle/` and lists what it skipped. Those are NOT pending work you can copy in afterwards - migrations must be regenerated into the project's own history or they never run. See [Database](#database-schema-migrations).
344
+ `apply-auto.js` copies only `unmodified` (safe replace) + `new` (any upstream-added file whose tree the buyer has not deleted). It will **never** touch `newInDeletedTree` files, and it aborts without writing anything if any source is missing from staging (no half-applied tree). Report what was applied, and mark all auto-update and new file items as `[x]` in the plan.
347
345
 
348
346
  **Careful mode:**
349
347
  Present the list of safe auto-updates and new files to the user:
350
348
  > These {N} files haven't been customized and can be safely replaced with the new version. {N} new files will be created. Want me to apply all of these, or would you like to review any specific ones first?
351
349
 
352
- If the user wants to review specific files, show them the diff for each requested file. Apply only after approval. Do not count held-back migrations in either number - they are not being created, and presenting them as applied is the same error as marking them `[x]`.
350
+ If the user wants to review specific files, show them the diff for each requested file. Apply only after approval.
353
351
 
354
352
  ### Step 7: Review Modified Files
355
353
 
@@ -539,7 +537,7 @@ This updates `.generatesaas/manifest.json` to the new version, regenerates file
539
537
 
540
538
  ### Post-update steps
541
539
  - Run `pnpm install` if dependencies changed
542
- - Run database migrations if schema was updated
540
+ - Push the schema if it was updated (`pnpm --filter @repo/database setup` in development)
543
541
  - {any other steps from the changelog}
544
542
 
545
543
  ### Skipped changes
@@ -576,28 +574,44 @@ Config files are the most commonly customized.
576
574
  - Keep user-added business logic intact
577
575
  - Watch for new imports or dependency changes
578
576
 
579
- ### Database (schema, migrations)
577
+ ### Database (schema)
580
578
 
581
579
  - NEVER auto-merge schema files - always present for user review
582
- - Warn about schema changes that may require new migrations
583
-
584
- **Never copy an upstream migration into `packages/database/drizzle/` as-is, and never overwrite the project's `drizzle/meta/_journal.json` or `drizzle/meta/*_snapshot.json` with the upstream versions.** A project's migration history is its own: upstream changes get re-derived into it, never pasted over it. Regenerate instead:
585
-
586
- 1. Merge the upstream schema change into `packages/database/src/db/*.ts` (user review first, as above).
587
- 2. Run `pnpm --filter @repo/database generate`. Drizzle diffs the project's own latest snapshot against the merged schema and writes a fresh migration carrying a current timestamp, the next `idx`, a correct `prevId`, and a snapshot that describes the project's real schema.
588
- 3. Open the upstream `.sql` under `.generatesaas/staging/packages/database/drizzle/` and copy any hand-written statements into the generated file: seeds, backfills, data migrations, `ON CONFLICT` guards. `generate` infers structure only, so those are lost otherwise.
589
- 4. Run `pnpm --filter @repo/database migrate`.
590
-
591
- **Why the upstream file cannot just be added.** Drizzle applies a migration only when its journal `when` value is greater than the `created_at` of the last migration already applied to the database. A project that generated its own migration after the upstream one was authored sits above that mark, so a copied-in upstream file is **skipped silently and permanently**: no error, no warning, no log line, and `migrate` still exits 0. The table or column it was supposed to create never exists, and the miss surfaces later as runtime failures on whatever code path needs it. Taking the upstream `_journal.json` and snapshots instead of the project's own is the second half of the same trap: the final snapshot then knows nothing about the project's own tables, so the next `generate` re-emits `CREATE TABLE` for tables that already exist.
592
-
593
- **If step 2 reports no schema changes**, the upstream migration is data-only and there is nothing to regenerate. This is the ONLY case where the `.sql` is copied in by hand. Copying the file is not enough on its own: drizzle reads `_journal.json` and runs only the entries listed there, so a `.sql` sitting in the folder with no entry is never opened. Do all four, in this order:
594
-
595
- 1. **Append an entry** to the project's own `_journal.json`, copied from the upstream entry. This is the load-bearing step - without it nothing else matters.
596
- 2. Set that entry's `when` **greater than the largest `when` already applied** to the database, never the upstream value. Drizzle takes the highest `created_at` in `drizzle.__drizzle_migrations` as its floor and skips everything at or below it.
597
- 3. Set that entry's `idx` to continue the project's own sequence (last `idx` + 1).
598
- 4. Rename the `.sql` file so its name matches the entry's `tag`, or set the `tag` to the file's existing name. Drizzle loads the file **by tag** and throws if the two disagree.
599
-
600
- Still do not bring the upstream `meta/*_snapshot.json`, here least of all. Nothing needs it: `migrate` never reads snapshots, and `generate` needs only ONE previous snapshot, which must be the project's own. Adding an upstream one breaks `generate` whichever way it sorts - `meta/` is read in filename order, so an upstream snapshot that sorts LAST silently becomes the diff base (re-emitting `CREATE TABLE` for the project's own tables, and `DROP TABLE` for anything upstream carries that the project removed), while one that sorts earlier collides on `prevId` with the project's own, which prints a warning, writes no migration, and still exits 0.
580
+ - Warn about schema changes, and say what applying them will do to live data
581
+
582
+ **This boilerplate keeps no migration history.** The schema files under `packages/database/src/db/`
583
+ are the source of truth, and `db:deploy` applies them with `drizzle-kit push` and then verifies the
584
+ live database really matches. There is no `drizzle/` directory, no `generate`, no `migrate`, and
585
+ nothing to regenerate into a project's own history. An upstream schema change is an ordinary file
586
+ change here: merge it after review, then apply it.
587
+
588
+ 1. Merge the upstream change into `packages/database/src/db/*.ts` (user review first, as above).
589
+ 2. Apply it: `pnpm --filter @repo/database setup` in development, and `db:deploy` on the next
590
+ deploy in production.
591
+
592
+ **Say what push will do BEFORE the user applies it to a database with data in it.** Push makes the
593
+ database match the schema files, and `db:deploy` passes `--force`, so it does not stop to ask:
594
+
595
+ - a new table or column is added, which is safe;
596
+ - a **removed** column or table is **dropped, with its data**;
597
+ - a **narrowed** type is rewritten in place, and Postgres refuses the whole statement when a value
598
+ does not fit - it never truncates one silently.
599
+
600
+ For anything in the last two categories, tell the user what will be dropped or rewritten and let
601
+ them decide, rather than applying it as part of a routine update. If they need the old data, it has
602
+ to be copied somewhere else first; push has no place to put a data transform and no down step.
603
+
604
+ **Push can report success without applying anything.** `drizzle-kit push` prints its error and
605
+ still exits 0 when a change cannot be applied. `db:deploy` is why that no longer matters in
606
+ production: it re-reads the live schema afterwards and fails the boot naming the tables and columns
607
+ that differ. If a user applies a change by hand with `push` instead, have them run `db:deploy`
608
+ after it, or check the column themselves - a green push is not evidence on its own.
609
+
610
+ **A project generated before push-only** may still have `packages/database/drizzle/` and `generate`
611
+ / `migrate` scripts. Those are vestigial: the update removes the scripts, and the directory can be
612
+ deleted whenever the user is comfortable. Their database is already at whatever state the old
613
+ migrations left it, so the first push simply diffs from there - usually a no-op if the schema
614
+ matched. Review that first push on a production database rather than letting it ride a deploy.
601
615
 
602
616
  ### Package Dependencies (`package.json`)
603
617
 
@@ -47,9 +47,9 @@ function hashFile(filePath) {
47
47
  const WALK_EXCLUSIONS = new Set([
48
48
  ".git", "node_modules", ".pnpm-store", ".env", ".env.test",
49
49
  ".turbo", ".data", "dist",
50
- ".next", ".svelte-kit", ".wrangler",
50
+ ".next", ".next-e2e-web", ".next-e2e-desktop", ".svelte-kit", ".wrangler",
51
51
  ".devcontainer", "playwright-report", "test-results",
52
- ".drive-out",
52
+ ".drive-out", ".mastra",
53
53
  "pnpm-lock.yaml",
54
54
  ]);
55
55
 
@@ -10,9 +10,6 @@ const fs = require("node:fs");
10
10
  const path = require("node:path");
11
11
  const { findProjectRoot, ensureDir, STAGING_DIR } = require("./_helpers.js");
12
12
 
13
- /** Drizzle migration history. Held back from every auto-copy - see filesToCopy. */
14
- const MIGRATION_DIR = "packages/database/drizzle/";
15
-
16
13
  function main() {
17
14
  const root = findProjectRoot();
18
15
  if (!root) {
@@ -44,19 +41,15 @@ function main() {
44
41
  // handles by prompt (never here).
45
42
  const candidates = [...classification.unmodified, ...classification.new];
46
43
 
47
- // Migrations are NEVER auto-applied, and both failure modes are silent.
48
- // Drizzle runs a migration only when its journal `when` beats the newest one
49
- // already applied, so an upstream .sql copied in below that mark never runs
50
- // and `migrate` still exits 0; and an upstream meta snapshot becomes the diff
51
- // base for the next `generate`, which then re-emits the buyer's own tables.
52
- // The skill's Database section regenerates them into the project's own
53
- // history instead, which is only possible if this step leaves them alone.
54
- const heldMigrations = candidates.filter((f) => f.startsWith(MIGRATION_DIR));
55
- const filesToCopy = candidates.filter((f) => !f.startsWith(MIGRATION_DIR));
44
+ // NOTHING IS HELD BACK ANY MORE. This used to quarantine `packages/database/drizzle/`,
45
+ // because a pasted-in migration silently never runs. The boilerplate keeps no migration
46
+ // history now - the schema files are the source of truth and `db:deploy` pushes them - so
47
+ // there is no such directory upstream and nothing to quarantine. A schema change arrives as a
48
+ // change to `packages/database/src/db/`, which is an ordinary file like any other here.
49
+ const filesToCopy = candidates;
56
50
 
57
51
  if (filesToCopy.length === 0) {
58
52
  console.log("No files to auto-update. All changed files need manual merge.");
59
- reportHeldMigrations(heldMigrations);
60
53
  return;
61
54
  }
62
55
 
@@ -84,7 +77,6 @@ function main() {
84
77
  console.log(` ${unmodifiedSet.has(filePath) ? "update" : "create"} ${filePath}`);
85
78
  }
86
79
  console.log("\n[dry run] No files were modified.");
87
- reportHeldMigrations(heldMigrations);
88
80
  return;
89
81
  }
90
82
 
@@ -118,36 +110,10 @@ function main() {
118
110
  }
119
111
  }
120
112
 
121
- reportHeldMigrations(heldMigrations);
122
113
 
123
114
  console.log("\nAfter merging all files, run complete-update.js to finalize.");
124
115
  }
125
116
 
126
- /**
127
- * Report migration files deliberately NOT copied, so the AI knows they are
128
- * waiting rather than assuming the tree is settled. Silent when there are none.
129
- *
130
- * States the whole procedure rather than pointing at SKILL.md. The AI loads
131
- * SKILL.md at the start of the update, BEFORE step 1a refreshes the bundle, so
132
- * for one release after this ships it is following the previous release's prose
133
- * - which says migrations can be added directly. This output has to stand alone
134
- * against that reader, or the fix reads as an obstacle to work around.
135
- */
136
- function reportHeldMigrations(heldMigrations) {
137
- if (heldMigrations.length === 0) return;
138
- console.log(`\n${heldMigrations.length} migration file(s) held back - NOT copied into the project:`);
139
- for (const f of heldMigrations) console.log(` - ${f}`);
140
- console.log("\nDo NOT copy these in by hand, and do not take the upstream meta/ snapshots either.");
141
- console.log("Drizzle runs a migration only when its journal entry is newer than the newest one");
142
- console.log("already applied, so a pasted-in file never runs, `migrate` still exits 0, and the");
143
- console.log("table it should have created silently does not exist. Regenerate instead:");
144
- console.log(" 1. Merge the upstream schema change into packages/database/src/db/");
145
- console.log(" 2. Run: pnpm --filter @repo/database generate");
146
- console.log(" 3. Copy any hand-written statements (seeds, backfills, data migrations) out of the");
147
- console.log(" staged .sql at .generatesaas/staging/<path above> into the file step 2 wrote -");
148
- console.log(" `generate` infers structure only, so those statements are lost otherwise");
149
- console.log(" 4. Run: pnpm --filter @repo/database migrate");
150
- }
151
117
 
152
118
  try {
153
119
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generatesaas",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding and managing GenerateSaaS projects",
6
6
  "license": "UNLICENSED",
@@ -13,16 +13,16 @@
13
13
  "dist"
14
14
  ],
15
15
  "dependencies": {
16
- "@clack/prompts": "^1.3.0",
17
- "commander": "^14.0.3",
16
+ "@clack/prompts": "^1.7.0",
17
+ "commander": "^15.0.0",
18
18
  "picocolors": "^1.1.1",
19
- "tar": "^7.5.13"
19
+ "tar": "^7.5.22"
20
20
  },
21
21
  "devDependencies": {
22
- "@types/node": "^25.6.0",
22
+ "@types/node": "^26.2.0",
23
23
  "tsup": "^8.5.1",
24
24
  "typescript": "^6.0.3",
25
- "vitest": "^4.1.5",
25
+ "vitest": "^4.1.10",
26
26
  "@repo/tsconfig": "1.0.0"
27
27
  },
28
28
  "engines": {
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup",
33
- "postbuild": "node -e \"const fs=require('node:fs');fs.rmSync('dist/skill/content',{recursive:true,force:true});fs.mkdirSync('dist/skill',{recursive:true});fs.cpSync('src/skill/content','dist/skill/content',{recursive:true})\"",
33
+ "postbuild": "node -e \"const fs=require('node:fs');fs.rmSync('dist/skill/content',{recursive:true,force:true});fs.mkdirSync('dist/skill',{recursive:true});fs.cpSync('src/skill/content','dist/skill/content',{recursive:true});fs.rmSync('dist/generators/content',{recursive:true,force:true});fs.mkdirSync('dist/generators',{recursive:true});fs.cpSync('src/generators/content','dist/generators/content',{recursive:true})\"",
34
34
  "dev": "tsup --watch",
35
35
  "check-types": "tsc --noEmit",
36
36
  "test": "vitest run",