specweave 0.28.59 → 0.28.62

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.
Files changed (38) hide show
  1. package/bin/specweave.js +36 -0
  2. package/dist/src/cli/commands/init.d.ts.map +1 -1
  3. package/dist/src/cli/commands/init.js +5 -0
  4. package/dist/src/cli/commands/init.js.map +1 -1
  5. package/dist/src/cli/commands/jobs.d.ts +20 -0
  6. package/dist/src/cli/commands/jobs.d.ts.map +1 -0
  7. package/dist/src/cli/commands/jobs.js +448 -0
  8. package/dist/src/cli/commands/jobs.js.map +1 -0
  9. package/dist/src/cli/helpers/init/ado-repo-cloning.d.ts +32 -0
  10. package/dist/src/cli/helpers/init/ado-repo-cloning.d.ts.map +1 -0
  11. package/dist/src/cli/helpers/init/ado-repo-cloning.js +174 -0
  12. package/dist/src/cli/helpers/init/ado-repo-cloning.js.map +1 -0
  13. package/dist/src/cli/helpers/selection-strategy.d.ts +28 -0
  14. package/dist/src/cli/helpers/selection-strategy.d.ts.map +1 -1
  15. package/dist/src/cli/helpers/selection-strategy.js +48 -0
  16. package/dist/src/cli/helpers/selection-strategy.js.map +1 -1
  17. package/dist/src/cli/workers/import-worker.js +116 -2
  18. package/dist/src/cli/workers/import-worker.js.map +1 -1
  19. package/dist/src/core/repo-structure/providers/azure-devops-provider.d.ts +15 -0
  20. package/dist/src/core/repo-structure/providers/azure-devops-provider.d.ts.map +1 -1
  21. package/dist/src/core/repo-structure/providers/azure-devops-provider.js +56 -0
  22. package/dist/src/core/repo-structure/providers/azure-devops-provider.js.map +1 -1
  23. package/package.json +1 -1
  24. package/plugins/specweave/commands/specweave-jobs.md +7 -7
  25. package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +85 -0
  26. package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +148 -0
  27. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +58 -16
  28. package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +4 -0
  29. package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +4 -0
  30. package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +4 -0
  31. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +179 -0
  32. package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +165 -0
  33. package/plugins/specweave/hooks/v2/handlers/status-update.sh +7 -0
  34. package/plugins/specweave/hooks/v2/queue/dequeue.sh +4 -0
  35. package/plugins/specweave/hooks/v2/queue/enqueue.sh +50 -12
  36. package/plugins/specweave/hooks/v2/queue/processor.sh +74 -12
  37. package/plugins/specweave-ado/commands/specweave-ado-clone-repos.md +379 -0
  38. package/plugins/specweave-release/commands/specweave-release-npm.md +51 -14
@@ -0,0 +1,379 @@
1
+ ---
2
+ name: specweave-ado:clone-repos
3
+ description: Clone Azure DevOps repositories to local workspace. Use after init if cloning was skipped, or to add repos later.
4
+ ---
5
+
6
+ # Clone Azure DevOps Repositories Command
7
+
8
+ You are an Azure DevOps repository cloning expert. Help users clone repositories from ADO projects to their local workspace.
9
+
10
+ ## Purpose
11
+
12
+ This command clones Azure DevOps repositories **after** initial SpecWeave setup (`specweave init`). Use when:
13
+ - User skipped cloning during init
14
+ - Adding repositories from additional projects
15
+ - Re-cloning after cleanup
16
+ - Selective cloning with pattern filtering
17
+
18
+ ## Command Syntax
19
+
20
+ ```bash
21
+ # Interactive mode (prompts for everything)
22
+ /specweave-ado:clone-repos
23
+
24
+ # With pattern filter
25
+ /specweave-ado:clone-repos --pattern "sw-*"
26
+
27
+ # Regex pattern
28
+ /specweave-ado:clone-repos --pattern "regex:^api-.*$"
29
+
30
+ # Specific project only
31
+ /specweave-ado:clone-repos --project "MyProject"
32
+
33
+ # Dry-run (preview only)
34
+ /specweave-ado:clone-repos --dry-run
35
+ ```
36
+
37
+ ## Your Task
38
+
39
+ When the user runs this command:
40
+
41
+ ### Step 1: Check Prerequisites
42
+
43
+ ```typescript
44
+ import { readEnvFile, parseEnvFile } from '../../../src/utils/env-file.js';
45
+ import chalk from 'chalk';
46
+
47
+ const projectPath = process.cwd();
48
+ const envContent = readEnvFile(projectPath);
49
+
50
+ if (!envContent) {
51
+ console.log(chalk.red('❌ No .env file found. Run `specweave init` first.'));
52
+ return;
53
+ }
54
+
55
+ const parsed = parseEnvFile(envContent);
56
+
57
+ if (!parsed.AZURE_DEVOPS_PAT || !parsed.AZURE_DEVOPS_ORG) {
58
+ console.log(chalk.red('❌ Missing Azure DevOps credentials.'));
59
+ console.log(chalk.gray(' Run `specweave init` with Azure DevOps provider.'));
60
+ return;
61
+ }
62
+
63
+ const org = parsed.AZURE_DEVOPS_ORG;
64
+ const pat = parsed.AZURE_DEVOPS_PAT;
65
+
66
+ console.log(chalk.blue('\n📦 ADO Repository Cloning\n'));
67
+ console.log(chalk.gray(` Organization: ${org}`));
68
+ ```
69
+
70
+ ### Step 2: Get Project Selection
71
+
72
+ ```typescript
73
+ import { AzureDevOpsProvider } from '../../../src/core/repo-structure/providers/azure-devops-provider.js';
74
+
75
+ const provider = new AzureDevOpsProvider();
76
+
77
+ // If project specified via CLI, use it
78
+ let selectedProjects: string[] = [];
79
+
80
+ if (args.project) {
81
+ selectedProjects = [args.project];
82
+ console.log(chalk.gray(` Project: ${args.project} (from CLI)`));
83
+ } else {
84
+ // Fetch available projects
85
+ console.log(chalk.gray('\n Fetching projects...'));
86
+
87
+ const response = await fetch(
88
+ `https://dev.azure.com/${org}/_apis/projects?api-version=7.0`,
89
+ {
90
+ headers: {
91
+ 'Authorization': `Basic ${Buffer.from(':' + pat).toString('base64')}`,
92
+ 'Accept': 'application/json'
93
+ }
94
+ }
95
+ );
96
+
97
+ if (!response.ok) {
98
+ console.log(chalk.red(`❌ Failed to fetch projects: ${response.status}`));
99
+ return;
100
+ }
101
+
102
+ const data = await response.json();
103
+ const projects = data.value || [];
104
+
105
+ if (projects.length === 0) {
106
+ console.log(chalk.yellow('⚠️ No projects found in organization.'));
107
+ return;
108
+ }
109
+
110
+ // Prompt for project selection
111
+ const { checkbox } = await import('@inquirer/prompts');
112
+
113
+ selectedProjects = await checkbox({
114
+ message: 'Select project(s) to clone repositories from:',
115
+ choices: projects.map(p => ({ name: p.name, value: p.name })),
116
+ required: true
117
+ });
118
+
119
+ console.log(chalk.green(` ✓ ${selectedProjects.length} project(s) selected`));
120
+ }
121
+ ```
122
+
123
+ ### Step 3: Fetch Repositories
124
+
125
+ ```typescript
126
+ const allRepos = [];
127
+
128
+ for (const project of selectedProjects) {
129
+ console.log(chalk.gray(`\n Fetching repos from ${project}...`));
130
+
131
+ try {
132
+ const repos = await provider.listRepositories(org, project, pat);
133
+ const reposWithProject = repos.map(r => ({ ...r, project }));
134
+ allRepos.push(...reposWithProject);
135
+ console.log(chalk.green(` ✓ Found ${repos.length} repositories`));
136
+ } catch (error) {
137
+ console.log(chalk.yellow(` ⚠️ Failed: ${error.message}`));
138
+ }
139
+ }
140
+
141
+ if (allRepos.length === 0) {
142
+ console.log(chalk.yellow('\n⚠️ No repositories found.'));
143
+ return;
144
+ }
145
+
146
+ console.log(chalk.blue(`\n📋 Total: ${allRepos.length} repositories available\n`));
147
+ ```
148
+
149
+ ### Step 4: Apply Pattern Filter
150
+
151
+ ```typescript
152
+ import { filterRepositoriesByPattern } from '../../../src/cli/helpers/selection-strategy.js';
153
+
154
+ let filteredRepos = allRepos;
155
+ let patternDescription = 'all';
156
+
157
+ if (args.pattern) {
158
+ // Determine pattern type
159
+ const isRegex = args.pattern.startsWith('regex:');
160
+ const pattern = isRegex ? args.pattern.slice(6) : args.pattern;
161
+
162
+ const clonePattern = {
163
+ strategy: isRegex ? 'pattern-regex' : 'pattern-glob',
164
+ pattern: pattern,
165
+ isRegex
166
+ };
167
+
168
+ filteredRepos = filterRepositoriesByPattern(allRepos, clonePattern);
169
+ patternDescription = `matching "${pattern}"`;
170
+
171
+ console.log(chalk.gray(` Pattern: ${args.pattern}`));
172
+ console.log(chalk.gray(` Matched: ${filteredRepos.length} of ${allRepos.length} repos\n`));
173
+ } else {
174
+ // Prompt for pattern selection
175
+ const { select, input } = await import('@inquirer/prompts');
176
+
177
+ const strategy = await select({
178
+ message: 'How do you want to select repositories?',
179
+ choices: [
180
+ { name: 'All - Clone all repositories', value: 'all' },
181
+ { name: 'Pattern (glob) - e.g., "sw-*", "*-backend"', value: 'pattern-glob' },
182
+ { name: 'Pattern (regex) - e.g., "^api-.*$"', value: 'pattern-regex' }
183
+ ]
184
+ });
185
+
186
+ if (strategy !== 'all') {
187
+ const pattern = await input({
188
+ message: 'Enter pattern:',
189
+ validate: v => v.trim() ? true : 'Pattern required'
190
+ });
191
+
192
+ const clonePattern = {
193
+ strategy,
194
+ pattern: pattern.trim(),
195
+ isRegex: strategy === 'pattern-regex'
196
+ };
197
+
198
+ filteredRepos = filterRepositoriesByPattern(allRepos, clonePattern);
199
+ patternDescription = `matching "${pattern.trim()}"`;
200
+ }
201
+ }
202
+
203
+ if (filteredRepos.length === 0) {
204
+ console.log(chalk.yellow(`⚠️ No repositories ${patternDescription}.`));
205
+ return;
206
+ }
207
+ ```
208
+
209
+ ### Step 5: Preview and Confirm
210
+
211
+ ```typescript
212
+ console.log(chalk.blue(`\n📦 Repositories to clone (${filteredRepos.length}):\n`));
213
+
214
+ // Show preview (max 20)
215
+ filteredRepos.slice(0, 20).forEach(repo => {
216
+ console.log(chalk.gray(` • ${repo.name} (${repo.project})`));
217
+ });
218
+
219
+ if (filteredRepos.length > 20) {
220
+ console.log(chalk.gray(` ... and ${filteredRepos.length - 20} more\n`));
221
+ }
222
+
223
+ if (args.dryRun) {
224
+ console.log(chalk.cyan('\n🔎 DRY RUN: No repositories will be cloned.\n'));
225
+ return;
226
+ }
227
+
228
+ const { confirm } = await import('@inquirer/prompts');
229
+
230
+ const confirmed = await confirm({
231
+ message: `Clone ${filteredRepos.length} repositories to ./repos/?`,
232
+ default: true
233
+ });
234
+
235
+ if (!confirmed) {
236
+ console.log(chalk.gray('\n⏭️ Cloning cancelled.\n'));
237
+ return;
238
+ }
239
+ ```
240
+
241
+ ### Step 6: Start Background Cloning
242
+
243
+ ```typescript
244
+ import { triggerAdoRepoCloning } from '../../../src/cli/helpers/init/ado-repo-cloning.js';
245
+
246
+ // Build adoProjectSelection
247
+ const adoProjectSelection = {
248
+ org,
249
+ pat,
250
+ projects: selectedProjects
251
+ };
252
+
253
+ // Build clonePattern
254
+ const clonePatternResult = args.pattern
255
+ ? {
256
+ strategy: args.pattern.startsWith('regex:') ? 'pattern-regex' : 'pattern-glob',
257
+ pattern: args.pattern.startsWith('regex:') ? args.pattern.slice(6) : args.pattern
258
+ }
259
+ : { strategy: 'all' };
260
+
261
+ // Trigger background cloning
262
+ await triggerAdoRepoCloning(projectPath, adoProjectSelection, clonePatternResult);
263
+ ```
264
+
265
+ ## Examples
266
+
267
+ ### Example 1: Interactive Clone
268
+ **User**: `/specweave-ado:clone-repos`
269
+
270
+ **Output**:
271
+ ```
272
+ 📦 ADO Repository Cloning
273
+
274
+ Organization: mycompany
275
+
276
+ Fetching projects...
277
+ ✓ 3 projects found
278
+
279
+ Select project(s) to clone repositories from:
280
+ > [x] Platform
281
+ [x] Shared
282
+ [ ] Legacy
283
+
284
+ ✓ 2 project(s) selected
285
+
286
+ Fetching repos from Platform...
287
+ ✓ Found 12 repositories
288
+ Fetching repos from Shared...
289
+ ✓ Found 4 repositories
290
+
291
+ 📋 Total: 16 repositories available
292
+
293
+ How do you want to select repositories?
294
+ > All - Clone all repositories
295
+
296
+ 📦 Repositories to clone (16):
297
+
298
+ • sw-frontend (Platform)
299
+ • sw-backend (Platform)
300
+ • sw-shared-lib (Shared)
301
+ ... and 13 more
302
+
303
+ Clone 16 repositories to ./repos/? (Y/n)
304
+
305
+ 📦 Fetching ADO Repositories
306
+
307
+ Fetching from mycompany/Platform...
308
+ ✓ Found 12 repositories in Platform
309
+ Fetching from mycompany/Shared...
310
+ ✓ Found 4 repositories in Shared
311
+
312
+ 🔄 Cloning 16 repositories in background...
313
+
314
+ Repositories will be cloned to: ./repos/
315
+ Job ID: abc12345
316
+
317
+ Check progress: /specweave:jobs
318
+ Resume if interrupted: /specweave:jobs --resume abc12345
319
+ ```
320
+
321
+ ### Example 2: Pattern Filter
322
+ **User**: `/specweave-ado:clone-repos --pattern "sw-*"`
323
+
324
+ **Output**:
325
+ ```
326
+ 📦 ADO Repository Cloning
327
+
328
+ Organization: mycompany
329
+ Pattern: sw-*
330
+ Matched: 8 of 16 repos
331
+
332
+ 📦 Repositories to clone (8):
333
+
334
+ • sw-frontend (Platform)
335
+ • sw-backend (Platform)
336
+ • sw-shared-lib (Shared)
337
+ ...
338
+
339
+ Clone 8 repositories to ./repos/? (Y/n)
340
+ ```
341
+
342
+ ### Example 3: Dry Run
343
+ **User**: `/specweave-ado:clone-repos --dry-run`
344
+
345
+ **Output**:
346
+ ```
347
+ 📦 Repositories to clone (16):
348
+
349
+ • sw-frontend (Platform)
350
+ • sw-backend (Platform)
351
+ ...
352
+
353
+ 🔎 DRY RUN: No repositories will be cloned.
354
+ ```
355
+
356
+ ## Important Notes
357
+
358
+ - **Background Cloning**: Repositories clone in background (non-blocking)
359
+ - **Progress Tracking**: Check progress with `/specweave:jobs`
360
+ - **Resumable**: Interrupted clones can resume with `/specweave:jobs --resume <id>`
361
+ - **Auth via PAT**: Uses HTTPS clone URLs with PAT authentication
362
+ - **Target Directory**: Repos are cloned to `./repos/<repo-name>/`
363
+
364
+ ## Related Commands
365
+
366
+ - `/specweave:init` - Initial SpecWeave setup (includes repo cloning option)
367
+ - `/specweave:jobs` - Monitor background jobs
368
+ - `/specweave-ado:import-projects` - Import ADO projects with area path mapping
369
+
370
+ ## Error Handling
371
+
372
+ - **Missing Credentials**: Prompt to run `specweave init` first
373
+ - **Auth Failures**: Check PAT permissions (vso.code_read scope required)
374
+ - **Clone Failures**: Individual repo failures logged, others continue
375
+ - **Network Errors**: Job pauses, resume with `/specweave:jobs --resume`
376
+
377
+ ---
378
+
379
+ **Multi-Repo Support**: Perfect for microservices architectures with many ADO repositories.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: specweave-release:npm
3
- description: Bump patch version, create git tag, and trigger npm publish via GitHub Actions. Use --quick for instant release (auto-commits dirty changes, publishes to npmjs.org, pushes to git). Use --only for local publish without git push. Use --only --local for version bump only.
3
+ description: Bump patch version, create git tag, and trigger npm publish via GitHub Actions. Use --quick for instant release (auto-commits, PUSHES FIRST, then publishes to npmjs.org). Use --only for local publish without git push. Use --only --local for version bump only.
4
4
  ---
5
5
 
6
6
  # /specweave-release:npm - NPM Release Automation
@@ -12,9 +12,9 @@ You are the NPM Release Assistant. Your job is to automate the patch version rel
12
12
  | Command | Flow | Use Case |
13
13
  |---------|------|----------|
14
14
  | `/specweave-release:npm` | Bump → Push → **CI publishes** | Standard release (CI handles npm) |
15
- | `/specweave-release:npm --quick` | Auto-commit → Bump → Build → **Publish** → Push | **INSTANT RELEASE** (recommended!) |
15
+ | `/specweave-release:npm --quick` | Auto-commit → **PUSH** → Bump → Build → **Publish** → Push tag | **INSTANT RELEASE** (recommended!) |
16
16
  | `/specweave-release:npm --only` | Bump → Build → **Publish locally** → NO push | Quick local release, push later |
17
- | `/specweave-release:npm --only --push` | Auto-commit → Bump → Build → **Publish** → Push | Same as --quick |
17
+ | `/specweave-release:npm --only --push` | Auto-commit → **PUSH** → Bump → Build → **Publish** → Push tag | Same as --quick |
18
18
  | `/specweave-release:npm --only --local` | **Bump ONLY** → NO build, NO publish, NO git | FASTEST: Local testing only |
19
19
 
20
20
  ## Detecting Mode
@@ -22,7 +22,7 @@ You are the NPM Release Assistant. Your job is to automate the patch version rel
22
22
  Check flags in the command invocation:
23
23
 
24
24
  ```
25
- --quick → INSTANT RELEASE: auto-commit dirty, publish to npmjs.org, push git
25
+ --quick → INSTANT RELEASE: auto-commit, PUSH FIRST, then bump+build+publish+push tag
26
26
  --only --local → Version bump ONLY (no build, no publish, no git) - FASTEST
27
27
  --only --push → Same as --quick (legacy alias)
28
28
  --only → Direct publish to npm (bypass CI), no git push
@@ -47,10 +47,15 @@ Check flags in the command invocation:
47
47
 
48
48
  ## QUICK MODE WORKFLOW (--quick flag) - RECOMMENDED!
49
49
 
50
- Use this workflow when `--quick` flag is detected. This is the **instant release** mode - auto-commits any dirty changes, publishes directly to npmjs.org, and pushes to git. One command does everything!
50
+ Use this workflow when `--quick` flag is detected. This is the **instant release** mode - auto-commits any dirty changes, syncs git FIRST, then publishes to npmjs.org. One command does everything!
51
51
 
52
52
  **Use case**: You made changes and want to release immediately. No manual steps needed.
53
53
 
54
+ **CRITICAL ORDER**: Git sync FIRST, then release. This ensures:
55
+ - Your code is safe on remote before any release operations
56
+ - If npm publish fails, git is already synced (clean state)
57
+ - No risk of local-only commits that could be lost
58
+
54
59
  ### 1. Pre-flight Check (Minimal)
55
60
 
56
61
  ```bash
@@ -63,8 +68,6 @@ node -p "require('./package.json').version"
63
68
 
64
69
  **STOP if**: Not on `develop` branch (ask user to switch)
65
70
 
66
- **If uncommitted changes exist → AUTO-COMMIT (Step 2)**
67
-
68
71
  ### 2. Auto-Commit Dirty Changes (if any)
69
72
 
70
73
  ```bash
@@ -86,32 +89,50 @@ git commit -m "[auto-generated message based on changed files]"
86
89
  - `*.md` changes → `docs: update documentation`
87
90
  - Mixed → `chore: update code and documentation`
88
91
 
89
- ### 3. Bump Patch Version
92
+ ### 3. PUSH DIRTY COMMIT TO REMOTE FIRST! (CRITICAL!)
93
+
94
+ **BEFORE any release operations, sync git:**
95
+
96
+ ```bash
97
+ # Push dirty commit to remote FIRST - ensures code is safe before release
98
+ git push origin develop
99
+ ```
100
+
101
+ **Why this order?**
102
+ - ✅ Your changes are safely on GitHub BEFORE release starts
103
+ - ✅ If npm publish fails later, git is already synced
104
+ - ✅ No risk of "released but not pushed" state
105
+ - ✅ Clean recovery if anything fails mid-release
106
+
107
+ ### 4. Bump Patch Version
90
108
 
91
109
  ```bash
92
110
  npm version patch -m "chore: bump version to %s"
93
111
  ```
94
112
 
95
- ### 4. Build Package
113
+ This creates a NEW commit + tag locally.
114
+
115
+ ### 5. Build Package
96
116
 
97
117
  ```bash
98
118
  npm run rebuild
99
119
  ```
100
120
 
101
- ### 5. Publish to NPM (with explicit registry!)
121
+ ### 6. Publish to NPM (with explicit registry!)
102
122
 
103
123
  ```bash
104
124
  # CRITICAL: Always specify registry to avoid ~/.npmrc redirecting to private feeds!
105
125
  npm publish --registry https://registry.npmjs.org
106
126
  ```
107
127
 
108
- ### 6. Push to Git
128
+ ### 7. Push Version Commit + Tag
109
129
 
110
130
  ```bash
131
+ # Push the version bump commit and tag
111
132
  git push origin develop --follow-tags
112
133
  ```
113
134
 
114
- ### 7. Report Results
135
+ ### 8. Report Results
115
136
 
116
137
  ```markdown
117
138
  ✅ **Instant release complete!**
@@ -122,10 +143,11 @@ git push origin develop --follow-tags
122
143
 
123
144
  **What happened**:
124
145
  - ✅ Dirty changes auto-committed
146
+ - ✅ Pushed to GitHub (code safe!)
125
147
  - ✅ Version bumped to X.Y.Z
126
148
  - ✅ Package built
127
149
  - ✅ Published to npmjs.org
128
- - ✅ Pushed to GitHub
150
+ - ✅ Version tag pushed to GitHub
129
151
 
130
152
  **Verify**: `npm view specweave version --registry https://registry.npmjs.org`
131
153
  ```
@@ -133,11 +155,12 @@ git push origin develop --follow-tags
133
155
  ## Quick Mode Success Criteria
134
156
 
135
157
  ✅ Any dirty changes auto-committed
158
+ ✅ **Dirty commit pushed to remote FIRST**
136
159
  ✅ Version bumped in package.json
137
160
  ✅ Git commit and tag created
138
161
  ✅ Package rebuilt
139
162
  ✅ Published to npmjs.org (explicit registry!)
140
- Pushed to GitHub
163
+ Version commit + tag pushed to GitHub
141
164
 
142
165
  ---
143
166
 
@@ -408,6 +431,20 @@ git add -A
408
431
  git commit -m "[generated message]"
409
432
  ```
410
433
 
434
+ ### 1c. PUSH DIRTY COMMIT TO REMOTE FIRST! (CRITICAL!)
435
+
436
+ **BEFORE any release operations, sync git:**
437
+
438
+ ```bash
439
+ # Push dirty commit to remote FIRST - ensures code is safe before release
440
+ git push origin develop
441
+ ```
442
+
443
+ **Why this order?**
444
+ - ✅ Your changes are safely on GitHub BEFORE release starts
445
+ - ✅ If npm publish fails later, git is already synced
446
+ - ✅ No risk of "released but not pushed" state
447
+
411
448
  Then continue with version bump.
412
449
 
413
450
  ### 2. Bump Patch Version