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.
- package/dist/index.js +77 -77
- package/dist/skill/content/SKILL.md +84 -83
- package/dist/skill/content/scripts/_helpers.js +1 -1
- package/dist/skill/content/scripts/apply-auto.js +3 -3
- package/dist/skill/content/scripts/classify-files.js +8 -8
- package/dist/skill/content/scripts/complete-update.js +56 -11
- package/package.json +1 -1
|
@@ -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
|
|
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**
|
|
13
|
-
2. **Never break user code**
|
|
14
|
-
3. **Full transparency**
|
|
15
|
-
4. **Track everything**
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
57
|
-
> 2. **Help me commit**
|
|
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**
|
|
71
|
-
> 2. **Stop**
|
|
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
|
|
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
|
|
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,
|
|
87
|
-
into this project's configuration, and writes
|
|
88
|
-
If it reports "Already on the latest
|
|
89
|
-
|
|
90
|
-
`
|
|
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
|
|
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`
|
|
103
|
-
- `references/diffs/<path>.diff`
|
|
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`
|
|
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**
|
|
114
|
-
- **Raw**
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
140
|
-
- **modified**
|
|
141
|
-
- **deleted**
|
|
142
|
-
- **new**
|
|
143
|
-
- **newInDeletedTree**
|
|
144
|
-
- **removed**
|
|
145
|
-
- **crossDependencies**
|
|
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`
|
|
153
|
-
- `unmodifiedImportsModified`
|
|
154
|
-
- `newImportsModified`
|
|
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`**
|
|
158
|
-
- **`unmodifiedImportsModified`**
|
|
159
|
-
- **`newImportsModified`**
|
|
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`
|
|
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`
|
|
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`
|
|
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
|
|
193
|
-
- [ ] `path/to/old-file.ts`
|
|
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
|
|
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**
|
|
204
|
-
> 2. **Careful**
|
|
205
|
-
> 3. **Quick**
|
|
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>`
|
|
237
|
-
2. **New template:** `.generatesaas/staging/<path>`
|
|
238
|
-
3. **User's version:** the current file on disk
|
|
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
|
|
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**
|
|
265
|
-
> 2. **Keep my version**
|
|
266
|
-
> 3. **Use upstream version**
|
|
267
|
-
> 4. **Show me the diff**
|
|
268
|
-
> 5. **Let me customize**
|
|
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
|
|
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}`
|
|
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
|
|
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**
|
|
289
|
-
> 2. **Keep it**
|
|
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
|
|
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}/`
|
|
306
|
-
- `{path}`
|
|
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**
|
|
310
|
-
> 2. **Bring the tree back**
|
|
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
|
|
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**
|
|
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
|
|
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
|
|
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
|
|
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}`
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
258
|
+
// File matches the original template - safe to auto-update
|
|
259
259
|
classification.unmodified.push(filePath);
|
|
260
260
|
} else {
|
|
261
|
-
// File differs from template
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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 };
|