generatesaas 1.2.0 → 1.2.2

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: update
3
- description: Update a GenerateSaaS project to the latest boilerplate version. Use this whenever the user asks to "update my GenerateSaaS project", update their GenerateSaaS boilerplate/template, or pull the latest GenerateSaaS changes. Fetches and stages the new version, classifies files, analyzes dependencies, and applies changes while preserving user customizations the user controls every decision.
3
+ description: Update a GenerateSaaS project to the latest boilerplate version. Use this whenever the user asks to "update my GenerateSaaS project", update their GenerateSaaS boilerplate/template, or pull the latest GenerateSaaS changes. Fetches and stages the new version, classifies files, analyzes dependencies, and applies changes while preserving user customizations - the user controls every decision.
4
4
  ---
5
5
 
6
6
  # GenerateSaaS Update
@@ -9,10 +9,10 @@ Update your project to the latest GenerateSaaS boilerplate version while preserv
9
9
 
10
10
  ## Core Principles
11
11
 
12
- 1. **User decides everything** Present clear information and options. Never make decisions that affect user code without explicit approval. Every meaningful change requires the user's input.
13
- 2. **Never break user code** If an upstream change conflicts with user modifications, present the situation and let the user choose. When in doubt, keep the user's version and explain what was skipped.
14
- 3. **Full transparency** For every change, explain: what changed upstream, why it changed, and how it affects the user's code. The user should always know exactly what is happening.
15
- 4. **Track everything** Every change is tracked in a checklist. Mark items as completed, skipped, or failed with clear reasons.
12
+ 1. **User decides everything** - Present clear information and options. Never make decisions that affect user code without explicit approval. Every meaningful change requires the user's input.
13
+ 2. **Never break user code** - If an upstream change conflicts with user modifications, present the situation and let the user choose. When in doubt, keep the user's version and explain what was skipped.
14
+ 3. **Full transparency** - For every change, explain: what changed upstream, why it changed, and how it affects the user's code. The user should always know exactly what is happening.
15
+ 4. **Track everything** - Every change is tracked in a checklist. Mark items as completed, skipped, or failed with clear reasons.
16
16
 
17
17
  ## Workflow Overview
18
18
 
@@ -22,25 +22,25 @@ Step 1: Fetch + stage + prepare update data (you run the CLI, then the script)
22
22
  Step 2: Present changelog to user
23
23
  Step 3: Classify files (script)
24
24
  Step 4: Analyze dependencies
25
- Step 5: Present update plan user chooses interaction mode
25
+ Step 5: Present update plan - user chooses interaction mode
26
26
  Step 6: Apply safe auto-updates (script)
27
- Step 7: Review modified files with user one by one
27
+ Step 7: Review modified files with user - one by one
28
28
  Step 8: Post-update validation
29
29
  Step 9: Complete update (script)
30
30
  ```
31
31
 
32
32
  **You own the whole flow.** When the user says "update my GenerateSaaS project," do
33
- everything end-to-end including fetching and staging the new version (Step 1).
33
+ everything end-to-end - including fetching and staging the new version (Step 1).
34
34
  The user does not run any CLI command themselves.
35
35
 
36
36
  **What the staged update contains (important):** `generatesaas update` re-shapes the
37
- new version's template into THIS project's exact configuration before staging same
37
+ new version's template into THIS project's exact configuration before staging - same
38
38
  frontend, payment provider, database, feature flags, etc. So everything in
39
39
  `.generatesaas/staging/` and the `.generatesaas/template/` baseline is buyer-shaped:
40
40
  you will never see another framework's files, a provider the project doesn't use, the
41
41
  boilerplate's internal tooling, or dev-only secrets. Every diff you surface already
42
42
  applies to this project. The 3-way merge baseline (`.generatesaas/template/`) is a
43
- faithful pristine copy of what this project started from trust it.
43
+ faithful pristine copy of what this project started from - trust it.
44
44
 
45
45
  ---
46
46
 
@@ -48,13 +48,13 @@ faithful pristine copy of what this project started from — trust it.
48
48
 
49
49
  Before starting the update:
50
50
 
51
- 1. **Check git status.** Run `git status --porcelain -- . ':(exclude).generatesaas'` (or otherwise ignore changes under `.generatesaas/`). The update process writes its own artifacts there `staging.json`, `manifest.json` (license refresh), `held-back.json`, hash files so those must NOT count as a dirty tree. If, ignoring `.generatesaas/`, there are ANY uncommitted changes (staged, unstaged, or untracked files in tracked directories), **STOP immediately**. Do NOT proceed with the update. Tell the user:
51
+ 1. **Check git status.** Run `git status --porcelain -- . ':(exclude).generatesaas'` (or otherwise ignore changes under `.generatesaas/`). The update process writes its own artifacts there - `staging.json`, `manifest.json` (license refresh), `held-back.json`, hash files - so those must NOT count as a dirty tree. If, ignoring `.generatesaas/`, there are ANY uncommitted changes (staged, unstaged, or untracked files in tracked directories), **STOP immediately**. Do NOT proceed with the update. Tell the user:
52
52
 
53
- > You have uncommitted changes. The update cannot proceed until your working tree is clean this ensures you have a safe rollback point if anything needs to be reverted.
53
+ > You have uncommitted changes. The update cannot proceed until your working tree is clean - this ensures you have a safe rollback point if anything needs to be reverted.
54
54
  >
55
55
  > **What would you like to do?**
56
- > 1. **I'll handle it myself** Stop the update so you can commit, stash, or discard changes on your own terms, then re-run the update
57
- > 2. **Help me commit** I'll help you create a commit with your current changes, then we continue
56
+ > 1. **I'll handle it myself** - Stop the update so you can commit, stash, or discard changes on your own terms, then re-run the update
57
+ > 2. **Help me commit** - I'll help you create a commit with your current changes, then we continue
58
58
 
59
59
  **NEVER** commit, stash, reset, or alter git state without the user's explicit choice. The user may have intentionally reverted work or have changes they don't want committed. Wait for their decision.
60
60
 
@@ -67,30 +67,31 @@ If the user chooses option 1, stop completely. Do not continue the update.
67
67
  > It looks like a previous update was started but not completed. The plan file shows progress from a prior attempt.
68
68
  >
69
69
  > **What would you like to do?**
70
- > 1. **Start fresh** Discard the old plan and begin the update from scratch
71
- > 2. **Stop** Let me review what happened before continuing
70
+ > 1. **Start fresh** - Discard the old plan and begin the update from scratch
71
+ > 2. **Stop** - Let me review what happened before continuing
72
72
 
73
- If starting fresh, delete the old plan file. Note: files that were auto-applied in the previous attempt may now have V2 content but template-hashes.json still has V1 hashes these will be correctly classified as "modified" (since they differ from the V1 template hash) and the AI will see they match the staging file, making them trivial merges.
73
+ If starting fresh, delete the old plan file. Note: files that were auto-applied in the previous attempt may now have V2 content but template-hashes.json still has V1 hashes - these will be correctly classified as "modified" (since they differ from the V1 template hash) and the AI will see they match the staging file, making them trivial merges.
74
74
 
75
75
  ### Step 1: Fetch, Stage, and Prepare Update Data
76
76
 
77
77
  **1a. Stage the new version yourself.** Unless a staged update already exists
78
78
  (`.generatesaas/staging.json` present), run the CLI to fetch the latest version and
79
- stage it re-shaped for this project's exact configuration:
79
+ stage it - re-shaped for this project's exact configuration:
80
80
 
81
81
  ```bash
82
82
  npx generatesaas@latest update
83
83
  ```
84
84
 
85
85
  This resolves the saved API key from `~/.generatesaas` (written at init), so it runs
86
- non-interactively. It refreshes the skill files, downloads the new version, shapes it
87
- into this project's configuration, and writes `.generatesaas/staging/` + `staging.json`.
88
- If it reports "Already on the latest version," there is nothing to update — tell the
89
- user and stop. If it fails for lack of an API key or network, ask the user to run
90
- `npx generatesaas@latest update` themselves, then continue.
86
+ non-interactively - it never prompts you for a key. It refreshes the skill files,
87
+ downloads the new version, shapes it into this project's configuration, and writes
88
+ `.generatesaas/staging/` + `staging.json`. If it reports "Already on the latest
89
+ version," there is nothing to update - tell the user and stop. If it fails because the
90
+ API key is missing or rejected, ask the user to run `generatesaas auth` (which sets the
91
+ key once and saves it), then re-run the update. For a network error, ask them to retry.
91
92
 
92
93
  > Note: this command also rewrites the skill scripts to their latest version. That is
93
- > expected the scripts you run in the following steps are the freshly-installed ones.
94
+ > expected - the scripts you run in the following steps are the freshly-installed ones.
94
95
 
95
96
  **1b. Prepare the diff/classification data:**
96
97
 
@@ -99,30 +100,30 @@ node __SKILL_ROOT__/generatesaas-update/scripts/prepare-update.js
99
100
  ```
100
101
 
101
102
  Creates:
102
- - `references/changelog.md` what changed and why
103
- - `references/diffs/<path>.diff` one unified diff per changed file, named by the
103
+ - `references/changelog.md` - what changed and why
104
+ - `references/diffs/<path>.diff` - one unified diff per changed file, named by the
104
105
  file's project-relative path with `.diff` appended (e.g.
105
106
  `references/diffs/packages/config/src/index.ts.diff`). Diffs cover `modified` +
106
107
  `added` files; `unmodified`, `deleted`, and `removed` files have none.
107
- - `references/update-manifest.json` lists of added, modified, and removed files
108
+ - `references/update-manifest.json` - lists of added, modified, and removed files
108
109
 
109
110
  ### Step 2: Present Changelog
110
111
 
111
112
  Read `references/changelog.md`. It may be one of two shapes:
112
113
 
113
- - **Curated** contains explicit `## Breaking`, `## Migration`, `## Features`, `## Fixes` (or similar) sections. Use those sections verbatim; they are authoritative.
114
- - **Raw** auto-generated release notes (a flat list of PR titles, no sections). This is the common case. You can group PR titles into features/fixes by reading them, but you **cannot** reliably infer breaking changes or migration steps from PR titles.
114
+ - **Curated** - contains explicit `## Breaking`, `## Migration`, `## Features`, `## Fixes` (or similar) sections. Use those sections verbatim; they are authoritative.
115
+ - **Raw** - auto-generated release notes (a flat list of PR titles, no sections). This is the common case. You can group PR titles into features/fixes by reading them, but you **cannot** reliably infer breaking changes or migration steps from PR titles.
115
116
 
116
117
  Present a clear, organized summary:
117
118
 
118
119
  > ## What's New in v{targetVersion}
119
120
  >
120
- > **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"}
121
+ > **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"}
121
122
  > **New features:** {list}
122
123
  > **Bug fixes:** {list}
123
- > **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."}
124
+ > **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."}
124
125
 
125
- **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.
126
+ **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.
126
127
 
127
128
  Then ask: *"Ready to proceed with the file analysis?"*
128
129
 
@@ -136,27 +137,27 @@ node __SKILL_ROOT__/generatesaas-update/scripts/classify-files.js
136
137
 
137
138
  Read `references/classification.json`. It contains:
138
139
 
139
- - **unmodified** file matches the original template, safe to replace
140
- - **modified** user has customized this file, needs careful review
141
- - **deleted** user deleted this file, skip update for it
142
- - **new** file doesn't exist locally and its directory tree is genuinely new upstream, safe to create
143
- - **newInDeletedTree** file is new upstream BUT lives inside a directory tree the user intentionally deleted (the tree existed in the baseline and is now gone on disk). **Never auto-create these.** Ask the user whether to bring the tree back (see "For New Files In Deleted Trees" in Step 7).
144
- - **removed** file was removed upstream, user decides whether to delete
145
- - **crossDependencies** import relationships between categories (if detected)
140
+ - **unmodified** - file matches the original template, safe to replace
141
+ - **modified** - user has customized this file, needs careful review
142
+ - **deleted** - user deleted this file, skip update for it
143
+ - **new** - file doesn't exist locally and its directory tree is genuinely new upstream, safe to create
144
+ - **newInDeletedTree** - file is new upstream BUT lives inside a directory tree the user intentionally deleted (the tree existed in the baseline and is now gone on disk). **Never auto-create these.** Ask the user whether to bring the tree back (see "For New Files In Deleted Trees" in Step 7).
145
+ - **removed** - file was removed upstream, user decides whether to delete
146
+ - **crossDependencies** - import relationships between categories (if detected)
146
147
 
147
148
  ### Step 4: Analyze Dependencies
148
149
 
149
150
  This step prevents breaking the user's code through indirect effects.
150
151
 
151
152
  1. **Read `crossDependencies`** from classification.json (if present). This shows:
152
- - `modifiedImportsUnmodified` modified files that import from auto-apply files
153
- - `unmodifiedImportsModified` auto-apply files that import from modified files
154
- - `newImportsModified` new files that import from modified files
153
+ - `modifiedImportsUnmodified` - modified files that import from auto-apply files
154
+ - `unmodifiedImportsModified` - auto-apply files that import from modified files
155
+ - `newImportsModified` - new files that import from modified files
155
156
 
156
157
  2. **For each cross-dependency**, read the relevant diff at `references/diffs/<path>.diff`. Handle all three directions the classifier emits:
157
- - **`modifiedImportsUnmodified`** a modified file imports from an auto-apply (unmodified) file. If the auto-apply file's diff changes **exports, function signatures, types, or interfaces** that the modified file uses → promote it from `unmodified` to `modified` (it needs user review alongside its dependent, so the merge accounts for the new API).
158
- - **`unmodifiedImportsModified`** an auto-apply file imports from a modified file. After the modified file is handled in Step 7, re-check this auto-apply file: if the user kept their version of the modified file but the auto-apply file's new version expects the upstream API, the auto-apply file may not be compatible. Flag it and revisit in the Step 7 cascade-awareness check.
159
- - **`newImportsModified`** a new file imports from a modified file. The new file will be auto-created in Step 6. After the modified file is handled, verify the new file's imports resolve against whatever version of the modified file the user ended up with (their merge, their kept version, or upstream). If the new file expects the upstream API but the user kept their version, surface it.
158
+ - **`modifiedImportsUnmodified`** - a modified file imports from an auto-apply (unmodified) file. If the auto-apply file's diff changes **exports, function signatures, types, or interfaces** that the modified file uses → promote it from `unmodified` to `modified` (it needs user review alongside its dependent, so the merge accounts for the new API).
159
+ - **`unmodifiedImportsModified`** - an auto-apply file imports from a modified file. After the modified file is handled in Step 7, re-check this auto-apply file: if the user kept their version of the modified file but the auto-apply file's new version expects the upstream API, the auto-apply file may not be compatible. Flag it and revisit in the Step 7 cascade-awareness check.
160
+ - **`newImportsModified`** - a new file imports from a modified file. The new file will be auto-created in Step 6. After the modified file is handled, verify the new file's imports resolve against whatever version of the modified file the user ended up with (their merge, their kept version, or upstream). If the new file expects the upstream API but the user kept their version, surface it.
160
161
 
161
162
  3. **Update `references/classification.json`**: before making any changes, copy the file to `references/classification-original.json` as a backup. Then move any promoted files from the `unmodified` array to the `modified` array. This ensures `apply-auto.js` only touches truly safe files. If you need to redo the analysis, restore from the backup.
162
163
 
@@ -178,31 +179,31 @@ Date: {YYYY-MM-DD}
178
179
 
179
180
  ## Safe Auto-Updates ({count} files)
180
181
  These files haven't been customized and will be updated automatically:
181
- - [ ] `path/to/file.ts` {brief description of change}
182
+ - [ ] `path/to/file.ts` - {brief description of change}
182
183
 
183
184
  ## New Files ({count} files)
184
185
  New in this version:
185
- - [ ] `path/to/new-file.ts` {what this file does}
186
+ - [ ] `path/to/new-file.ts` - {what this file does}
186
187
 
187
188
  ## Files Needing Review ({count} files)
188
189
  These files have your customizations. Each one will be presented for your decision:
189
- - [ ] `path/to/modified.ts` Risk: {Low/Medium/High} {what changed}
190
+ - [ ] `path/to/modified.ts` - Risk: {Low/Medium/High} - {what changed}
190
191
 
191
192
  ## Removed Upstream ({count} files)
192
- Removed in the new version you decide whether to delete:
193
- - [ ] `path/to/old-file.ts` {why it was removed}
193
+ Removed in the new version - you decide whether to delete:
194
+ - [ ] `path/to/old-file.ts` - {why it was removed}
194
195
 
195
196
  ## Skipped
196
- {Empty items move here during execution with reasons}
197
+ {Empty - items move here during execution with reasons}
197
198
  ```
198
199
 
199
200
  Then ask the user to choose their interaction level:
200
201
 
201
202
  > **How would you like to handle this update?**
202
203
  >
203
- > 1. **Recommended** Auto-apply safe files, then I'll walk you through each modified file with my recommendation. You approve or adjust each one.
204
- > 2. **Careful** I'll present every single change for your approval, including auto-updates. Nothing happens without your explicit OK.
205
- > 3. **Quick** Auto-apply safe files, auto-apply my recommended merges for low-risk files, and only ask you about medium/high-risk changes.
204
+ > 1. **Recommended** - Auto-apply safe files, then I'll walk you through each modified file with my recommendation. You approve or adjust each one.
205
+ > 2. **Careful** - I'll present every single change for your approval, including auto-updates. Nothing happens without your explicit OK.
206
+ > 3. **Quick** - Auto-apply safe files, auto-apply my recommended merges for low-risk files, and only ask you about medium/high-risk changes.
206
207
 
207
208
  **Wait for the user's choice.** Default to **Recommended** if they just say "proceed" or similar.
208
209
 
@@ -233,9 +234,9 @@ This is the most important step. Work through each file in the `modified` catego
233
234
 
234
235
  For each file, you have three versions available for proper three-way merge context:
235
236
 
236
- 1. **Old template:** `.generatesaas/template/<path>` the original boilerplate version
237
- 2. **New template:** `.generatesaas/staging/<path>` the updated boilerplate version
238
- 3. **User's version:** the current file on disk their customized version
237
+ 1. **Old template:** `.generatesaas/template/<path>` - the original boilerplate version
238
+ 2. **New template:** `.generatesaas/staging/<path>` - the updated boilerplate version
239
+ 3. **User's version:** the current file on disk - their customized version
239
240
 
240
241
  The diff at `references/diffs/<path>.diff` shows **pure upstream changes** (old template → new template), making it easy to see exactly what we changed and why.
241
242
 
@@ -247,7 +248,7 @@ If `.generatesaas/template/<path>` does not exist (backward compat), fall back t
247
248
  ### `{file path}`
248
249
 
249
250
  **What changed upstream:** {describe changes from old template → new template, referencing the diff and changelog}
250
- **What you changed:** {describe changes from old template → user's file compare the two to identify customizations}
251
+ **What you changed:** {describe changes from old template → user's file - compare the two to identify customizations}
251
252
  **Overlap:** {None / Partial / Significant}
252
253
  **Risk:** {Low / Medium / High}
253
254
 
@@ -261,11 +262,11 @@ If `.generatesaas/template/<path>` does not exist (backward compat), fall back t
261
262
  **Then present the user's options:**
262
263
 
263
264
  > **What would you like to do?**
264
- > 1. **Accept merge** {brief description of the merged result}
265
- > 2. **Keep my version** Skip this upstream change entirely
266
- > 3. **Use upstream version** Replace with the new template version (your changes will be lost)
267
- > 4. **Show me the diff** Display the raw upstream diff so I can review it
268
- > 5. **Let me customize** Tell me exactly what you want
265
+ > 1. **Accept merge** - {brief description of the merged result}
266
+ > 2. **Keep my version** - Skip this upstream change entirely
267
+ > 3. **Use upstream version** - Replace with the new template version (your changes will be lost)
268
+ > 4. **Show me the diff** - Display the raw upstream diff so I can review it
269
+ > 5. **Let me customize** - Tell me exactly what you want
269
270
 
270
271
  **In Quick mode:** For **Low risk** files, apply the recommended merge automatically and report what was done. For **Medium/High risk**, always ask.
271
272
 
@@ -275,25 +276,25 @@ If `.generatesaas/template/<path>` does not exist (backward compat), fall back t
275
276
 
276
277
  #### For Removed Files
277
278
 
278
- Before presenting each removed file, **search the entire project** for imports of it. Use grep to find `import ... from` or `require(...)` statements referencing the file's path (check relative paths, aliases, and package paths). This is critical the classification script only scans files in the update, not user-created files that may also depend on the removed file.
279
+ Before presenting each removed file, **search the entire project** for imports of it. Use grep to find `import ... from` or `require(...)` statements referencing the file's path (check relative paths, aliases, and package paths). This is critical - the classification script only scans files in the update, not user-created files that may also depend on the removed file.
279
280
 
280
281
  ```
281
- ### `{file path}` Removed Upstream
282
+ ### `{file path}` - Removed Upstream
282
283
 
283
284
  **Why it was removed:** {reason from changelog}
284
- **Your modifications:** {describe if the user modified it, or "None file is unmodified"}
285
+ **Your modifications:** {describe if the user modified it, or "None - file is unmodified"}
285
286
  **Files that import this:** {list ALL files found by searching, or "None found"}
286
287
 
287
288
  > **What would you like to do?**
288
- > 1. **Delete it** It's no longer needed upstream
289
- > 2. **Keep it** I still use this file
289
+ > 1. **Delete it** - It's no longer needed upstream
290
+ > 2. **Keep it** - I still use this file
290
291
  ```
291
292
 
292
293
  If ANY files import the removed file, always warn before deletion and explain what would break.
293
294
 
294
295
  #### For Deleted-by-User Files
295
296
 
296
- Files the user intentionally deleted skip silently. Just note in the plan: "Skipped user previously deleted this file."
297
+ Files the user intentionally deleted - skip silently. Just note in the plan: "Skipped - user previously deleted this file."
297
298
 
298
299
  #### For New Files In Deleted Trees (`newInDeletedTree`)
299
300
 
@@ -302,15 +303,15 @@ These are files the new version adds inside a directory the user previously dele
302
303
  ```
303
304
  ### New upstream files under `{deleted dir}/` (which you removed)
304
305
 
305
- The new version adds {N} files under `{deleted dir}/` a directory you previously deleted:
306
- - `{path}` {what it is}
306
+ The new version adds {N} files under `{deleted dir}/` - a directory you previously deleted:
307
+ - `{path}` - {what it is}
307
308
 
308
309
  > **What would you like to do?**
309
- > 1. **Keep it deleted** Skip these files (you removed this feature on purpose)
310
- > 2. **Bring the tree back** Create these files (you want this feature after all)
310
+ > 1. **Keep it deleted** - Skip these files (you removed this feature on purpose)
311
+ > 2. **Bring the tree back** - Create these files (you want this feature after all)
311
312
  ```
312
313
 
313
- Default to **keep deleted** unless the user says otherwise. If they keep it deleted, note in the plan and do nothing (the completion script handles the hashes correctly these files simply won't exist).
314
+ Default to **keep deleted** unless the user says otherwise. If they keep it deleted, note in the plan and do nothing (the completion script handles the hashes correctly - these files simply won't exist).
314
315
 
315
316
  #### After Each File
316
317
 
@@ -329,10 +330,10 @@ After processing ALL modified files, review the results:
329
330
  >
330
331
  > **Options:**
331
332
  > 1. **Revert `{auto-file}`** to the previous version
332
- > 2. **Keep it** I'll check if it still works
333
+ > 2. **Keep it** - I'll check if it still works
333
334
  > 3. **Let me look at both files** to decide
334
335
 
335
- Similarly, if an auto-applied file changed its exports and a modified file that imports from it was merged verify the merge accounts for the new API.
336
+ Similarly, if an auto-applied file changed its exports and a modified file that imports from it was merged - verify the merge accounts for the new API.
336
337
 
337
338
  ### Step 8: Post-Update Validation
338
339
 
@@ -341,15 +342,15 @@ After all files are processed:
341
342
  1. **Check for obvious issues:**
342
343
  - Scan merged files for syntax errors or broken imports
343
344
  - If the project has TypeScript, run the type check (`pnpm check-types`)
344
- - **Run the test suite (`pnpm test`) too do not rely on the type check alone.** Most packages scope `check-types` to `src/**` and exclude `tests/**`, so a type error introduced into a **test file** during a merge will pass `check-types` silently. If any of your merges landed in a `tests/` file, the test suite is the only gate that exercises it. When in doubt, also type-check the specific merged test files directly (e.g. `npx tsc --noEmit <file>` with the package's tsconfig settings).
345
+ - **Run the test suite (`pnpm test`) too - do not rely on the type check alone.** Most packages scope `check-types` to `src/**` and exclude `tests/**`, so a type error introduced into a **test file** during a merge will pass `check-types` silently. If any of your merges landed in a `tests/` file, the test suite is the only gate that exercises it. When in doubt, also type-check the specific merged test files directly (e.g. `npx tsc --noEmit <file>` with the package's tsconfig settings).
345
346
  - Note: auto-applied files may have changed their exports/API in ways that affect **user-created files** (files not part of the template). The classification script only scans template files for cross-dependencies. A type check + test run is the best way to catch breakage in user-created files.
346
347
 
347
- > All changes have been applied. I'll run the type check **and** the test suite to verify nothing is broken the type check alone skips test files, and some auto-applied files may have changed their API in ways that affect your custom code or tests.
348
+ > All changes have been applied. I'll run the type check **and** the test suite to verify nothing is broken - the type check alone skips test files, and some auto-applied files may have changed their API in ways that affect your custom code or tests.
348
349
 
349
350
  2. If issues are found, present them:
350
351
 
351
352
  > Found {N} issues after the update:
352
- > - `{file}:{line}` {description}
353
+ > - `{file}:{line}` - {description}
353
354
  >
354
355
  > These appear to be caused by the update. Want me to fix them?
355
356
 
@@ -366,7 +367,7 @@ After all files are processed:
366
367
 
367
368
  **Do NOT include:**
368
369
  - Files where the user accepted a merge or chose "Use upstream version" (template changes were applied)
369
- - Removed files (not in staging template-hashes handles these automatically)
370
+ - Removed files (not in staging - template-hashes handles these automatically)
370
371
  - New files, whether created or rejected (handled correctly by staging presence)
371
372
  - Any file where the template's intended change was successfully applied
372
373
 
@@ -402,7 +403,7 @@ This updates `.generatesaas/manifest.json` to the new version, regenerates file
402
403
  - {any other steps from the changelog}
403
404
 
404
405
  ### Skipped changes
405
- {For each skipped item: what was skipped and why so the user can revisit later}
406
+ {For each skipped item: what was skipped and why - so the user can revisit later}
406
407
  ```
407
408
 
408
409
  ---
@@ -437,7 +438,7 @@ Config files are the most commonly customized.
437
438
 
438
439
  ### Database (schema, migrations)
439
440
 
440
- - NEVER auto-merge schema files always present for user review
441
+ - NEVER auto-merge schema files - always present for user review
441
442
  - New migration files can be added directly
442
443
  - Warn about schema changes that may require new migrations
443
444
 
@@ -446,7 +447,7 @@ Config files are the most commonly customized.
446
447
  - Add new dependencies from upstream
447
448
  - Update version ranges only for intentional upstream changes (security, breaking change requirements)
448
449
  - Preserve user-added dependencies
449
- - Never touch `pnpm-lock.yaml` user runs `pnpm install` after
450
+ - Never touch `pnpm-lock.yaml` - user runs `pnpm install` after
450
451
 
451
452
  ### Translation Files (`en.json`)
452
453
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shared helpers for update scripts.
3
- * CommonJS module used by prepare-update.js, apply-auto.js, classify-files.js, complete-update.js.
3
+ * CommonJS module - used by prepare-update.js, apply-auto.js, classify-files.js, complete-update.js.
4
4
  */
5
5
 
6
6
  const fs = require("node:fs");
@@ -36,7 +36,7 @@ function main() {
36
36
  process.exit(1);
37
37
  }
38
38
 
39
- // `new` only ever contains genuinely-new trees files inside buyer-deleted
39
+ // `new` only ever contains genuinely-new trees - files inside buyer-deleted
40
40
  // trees land in `newInDeletedTree`, which the AI handles by prompt (never here).
41
41
  const filesToCopy = [...classification.unmodified, ...classification.new];
42
42
 
@@ -49,12 +49,12 @@ function main() {
49
49
  const unmodifiedSet = new Set(classification.unmodified);
50
50
 
51
51
  // Pre-flight: every source must exist in staging. Abort BEFORE touching the
52
- // project if any are missing never leave a half-applied tree (there is no
52
+ // project if any are missing - never leave a half-applied tree (there is no
53
53
  // rollback besides git). Buyer-shaped staging should always contain them; a
54
54
  // miss means staging is stale or incomplete.
55
55
  const missing = filesToCopy.filter((f) => !fs.existsSync(path.join(stagingDir, f)));
56
56
  if (missing.length > 0) {
57
- console.error(`Error: ${missing.length} file(s) not found in staging aborting without changes:`);
57
+ console.error(`Error: ${missing.length} file(s) not found in staging - aborting without changes:`);
58
58
  for (const f of missing) console.error(` - ${f}`);
59
59
  console.error("Re-stage the update (run 'npx generatesaas@latest update') and retry.");
60
60
  process.exit(1);
@@ -15,7 +15,7 @@ const { findProjectRoot, hashFile, TEMPLATE_HASHES_FILE, HASHES_FILE, STAGING_DI
15
15
 
16
16
  /**
17
17
  * Extract local import paths from file content.
18
- * Only captures relative (./, ../), alias (~/, @/), and # imports skips packages.
18
+ * Only captures relative (./, ../), alias (~/, @/), and # imports - skips packages.
19
19
  */
20
20
  function extractImports(content, filePath) {
21
21
  // For Vue SFCs, extract all script block contents (both <script> and <script setup>)
@@ -54,7 +54,7 @@ function matchImportToFile(importPath, fromFilePath, knownFileSet) {
54
54
  if (importPath.startsWith("./") || importPath.startsWith("../")) {
55
55
  basePath = path.normalize(path.join(path.dirname(fromFilePath), importPath));
56
56
  } else if (importPath.startsWith("~/") || importPath.startsWith("@/") || importPath.startsWith("#")) {
57
- // Alias strip prefix. ~ and @ typically resolve from the app root.
57
+ // Alias - strip prefix. ~ and @ typically resolve from the app root.
58
58
  basePath = importPath.replace(/^[~@#]\//, "");
59
59
  } else {
60
60
  return null;
@@ -163,7 +163,7 @@ function buildCrossDependencies(root, classification, stagingDir) {
163
163
  if (content) scanFileContent(f, content, "modified");
164
164
  }
165
165
 
166
- // Scan unmodified files both current AND staging versions
166
+ // Scan unmodified files - both current AND staging versions
167
167
  // (staging is what will actually be placed, so its imports matter)
168
168
  for (const f of classification.unmodified) {
169
169
  const content = readSafe(path.join(root, f));
@@ -250,15 +250,15 @@ function main() {
250
250
  if (!fs.existsSync(fullPath)) {
251
251
  classification.deleted.push(filePath);
252
252
  } else if (!baselineHash) {
253
- // No baseline hash treat as modified to be safe
253
+ // No baseline hash - treat as modified to be safe
254
254
  classification.modified.push(filePath);
255
255
  } else {
256
256
  const currentHash = hashFile(fullPath);
257
257
  if (currentHash === baselineHash) {
258
- // File matches the original template safe to auto-update
258
+ // File matches the original template - safe to auto-update
259
259
  classification.unmodified.push(filePath);
260
260
  } else {
261
- // File differs from template user has customized
261
+ // File differs from template - user has customized
262
262
  classification.modified.push(filePath);
263
263
  }
264
264
  }
@@ -271,7 +271,7 @@ function main() {
271
271
  classification.modified.push(filePath);
272
272
  } else if (deletedTreeAncestor(filePath, root, baselineFiles)) {
273
273
  // Upstream added a file inside a directory tree the buyer deleted on
274
- // purpose. Don't silently recreate the tree surface for a decision.
274
+ // purpose. Don't silently recreate the tree - surface for a decision.
275
275
  classification.newInDeletedTree.push(filePath);
276
276
  } else {
277
277
  classification.new.push(filePath);
@@ -314,7 +314,7 @@ function main() {
314
314
  console.log("");
315
315
 
316
316
  if (classification.newInDeletedTree.length > 0) {
317
- console.log("New files inside trees you deleted (do NOT auto-create ask the user):");
317
+ console.log("New files inside trees you deleted (do NOT auto-create - ask the user):");
318
318
  for (const f of classification.newInDeletedTree) {
319
319
  console.log(` - ${f}`);
320
320
  }
@@ -7,7 +7,7 @@
7
7
  * .generatesaas/manifest.json, and cleans up staging + references.
8
8
  *
9
9
  * Reads .generatesaas/held-back.json (if present) to skip updating template
10
- * files that were held back their old template version is preserved so the
10
+ * files that were held back - their old template version is preserved so the
11
11
  * next update generates correct diffs that include the missed changes.
12
12
  */
13
13
 
@@ -54,6 +54,43 @@ function collectProjectFiles(dir, baseDir) {
54
54
  return files;
55
55
  }
56
56
 
57
+ /**
58
+ * Archive a finished update plan file. The skill writes its checklist to
59
+ * .generatesaas/updates/update-<from>-to-<to>.md and ticks items off as it goes;
60
+ * once the update completes we move it into updates/history/ so its presence no
61
+ * longer reads as an "incomplete previous update" on the next run, while keeping
62
+ * the record of what was skipped. Best-effort: bookkeeping must never fail a
63
+ * completed update.
64
+ */
65
+ function archivePlanFile(root) {
66
+ const updatesDir = path.join(root, INTERNAL_DIR, "updates");
67
+ if (!fs.existsSync(updatesDir)) return;
68
+
69
+ let entries;
70
+ try {
71
+ entries = fs.readdirSync(updatesDir, { withFileTypes: true });
72
+ } catch {
73
+ return;
74
+ }
75
+
76
+ const plans = entries.filter((e) => e.isFile() && /^update-.+\.md$/.test(e.name));
77
+ if (plans.length === 0) return;
78
+
79
+ const historyDir = path.join(updatesDir, "history");
80
+ for (const plan of plans) {
81
+ const src = path.join(updatesDir, plan.name);
82
+ const dest = path.join(historyDir, plan.name);
83
+ try {
84
+ ensureDir(historyDir);
85
+ fs.rmSync(dest, { force: true });
86
+ fs.renameSync(src, dest);
87
+ console.log(`Archived plan file to ${path.relative(root, dest)}`);
88
+ } catch (err) {
89
+ console.warn(` Warning: could not archive plan file "${plan.name}": ${err.message}`);
90
+ }
91
+ }
92
+ }
93
+
57
94
  function main() {
58
95
  const root = findProjectRoot();
59
96
  if (!root) {
@@ -85,7 +122,7 @@ function main() {
85
122
  );
86
123
 
87
124
  if (heldBack.size > 0) {
88
- console.log(`${heldBack.size} files were held back preserving their old template versions.`);
125
+ console.log(`${heldBack.size} files were held back - preserving their old template versions.`);
89
126
  }
90
127
 
91
128
  // ── Update template directory from staging ──
@@ -97,23 +134,23 @@ function main() {
97
134
  const stagingFiles = new Set(walkDir(stagingDir, stagingDir));
98
135
 
99
136
  // Validate held-back entries (non-fatal). A held-back file is supposed to be
100
- // a real upstream change the user chose to skip preserving its old template
137
+ // a real upstream change the user chose to skip - preserving its old template
101
138
  // version so the next update re-surfaces the missed change. Warn on entries
102
139
  // that can't be that: a path not in staging (nothing to hold back), or a file
103
140
  // identical between old template and staging (no upstream change to preserve).
104
141
  // A wrong held-back entry silently corrupts the next update's diff baseline.
105
142
  for (const rel of heldBack) {
106
143
  if (!stagingFiles.has(rel)) {
107
- console.warn(` Warning: held-back "${rel}" is not in staging nothing to hold back. Check .generatesaas/held-back.json.`);
144
+ console.warn(` Warning: held-back "${rel}" is not in staging - nothing to hold back. Check .generatesaas/held-back.json.`);
108
145
  continue;
109
146
  }
110
147
  const oldTemplateFile = path.join(templateDir, rel);
111
148
  if (fs.existsSync(oldTemplateFile) && hashFile(oldTemplateFile) === hashFile(path.join(stagingDir, rel))) {
112
- console.warn(` Warning: held-back "${rel}" is identical in the old template and staging no upstream change to preserve.`);
149
+ console.warn(` Warning: held-back "${rel}" is identical in the old template and staging - no upstream change to preserve.`);
113
150
  }
114
151
  }
115
152
 
116
- // Copy staging files to template dir (skip held-back old version stays)
153
+ // Copy staging files to template dir (skip held-back - old version stays)
117
154
  let copied = 0;
118
155
  let skipped = 0;
119
156
  for (const rel of stagingFiles) {
@@ -203,13 +240,21 @@ function main() {
203
240
  fs.writeFileSync(hashesPath, JSON.stringify(fileHashes, null, "\t") + "\n", "utf-8");
204
241
  console.log(`Tracked ${Object.keys(fileHashes).length} file hashes in ${HASHES_FILE}`);
205
242
 
243
+ // Archive the finished plan file so the next update's pre-flight check doesn't
244
+ // mistake a completed update for an interrupted one.
245
+ archivePlanFile(root);
246
+
206
247
  console.log(`\nUpdate to ${targetVersion} complete!`);
207
248
  console.log("Run 'pnpm install' if package.json was updated.");
208
249
  }
209
250
 
210
- try {
211
- main();
212
- } catch (err) {
213
- console.error("Error:", err.message);
214
- process.exit(1);
251
+ if (require.main === module) {
252
+ try {
253
+ main();
254
+ } catch (err) {
255
+ console.error("Error:", err.message);
256
+ process.exit(1);
257
+ }
215
258
  }
259
+
260
+ module.exports = { main, archivePlanFile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generatesaas",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding and managing GenerateSaaS projects",
6
6
  "bin": {