gsd-opencode 1.9.2 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/gsd-debugger.md +5 -5
- package/bin/gsd-install.js +105 -0
- package/bin/gsd.js +352 -0
- package/{command → commands}/gsd/add-phase.md +1 -1
- package/{command → commands}/gsd/audit-milestone.md +1 -1
- package/{command → commands}/gsd/debug.md +3 -3
- package/{command → commands}/gsd/discuss-phase.md +1 -1
- package/{command → commands}/gsd/execute-phase.md +1 -1
- package/{command → commands}/gsd/list-phase-assumptions.md +1 -1
- package/{command → commands}/gsd/map-codebase.md +1 -1
- package/{command → commands}/gsd/new-milestone.md +1 -1
- package/{command → commands}/gsd/new-project.md +3 -3
- package/{command → commands}/gsd/plan-phase.md +2 -2
- package/{command → commands}/gsd/research-phase.md +1 -1
- package/{command → commands}/gsd/verify-work.md +1 -1
- package/get-shit-done/workflows/list-phase-assumptions.md +1 -1
- package/get-shit-done/workflows/verify-work.md +5 -5
- package/lib/constants.js +193 -0
- package/package.json +34 -20
- package/src/commands/check.js +329 -0
- package/src/commands/config.js +337 -0
- package/src/commands/install.js +608 -0
- package/src/commands/list.js +256 -0
- package/src/commands/repair.js +519 -0
- package/src/commands/uninstall.js +732 -0
- package/src/commands/update.js +444 -0
- package/src/services/backup-manager.js +585 -0
- package/src/services/config.js +262 -0
- package/src/services/file-ops.js +830 -0
- package/src/services/health-checker.js +475 -0
- package/src/services/manifest-manager.js +301 -0
- package/src/services/migration-service.js +831 -0
- package/src/services/repair-service.js +846 -0
- package/src/services/scope-manager.js +303 -0
- package/src/services/settings.js +553 -0
- package/src/services/structure-detector.js +240 -0
- package/src/services/update-service.js +863 -0
- package/src/utils/hash.js +71 -0
- package/src/utils/interactive.js +222 -0
- package/src/utils/logger.js +128 -0
- package/src/utils/npm-registry.js +255 -0
- package/src/utils/path-resolver.js +226 -0
- /package/{command → commands}/gsd/add-todo.md +0 -0
- /package/{command → commands}/gsd/check-todos.md +0 -0
- /package/{command → commands}/gsd/complete-milestone.md +0 -0
- /package/{command → commands}/gsd/help.md +0 -0
- /package/{command → commands}/gsd/insert-phase.md +0 -0
- /package/{command → commands}/gsd/pause-work.md +0 -0
- /package/{command → commands}/gsd/plan-milestone-gaps.md +0 -0
- /package/{command → commands}/gsd/progress.md +0 -0
- /package/{command → commands}/gsd/quick.md +0 -0
- /package/{command → commands}/gsd/remove-phase.md +0 -0
- /package/{command → commands}/gsd/resume-work.md +0 -0
- /package/{command → commands}/gsd/set-model.md +0 -0
- /package/{command → commands}/gsd/set-profile.md +0 -0
- /package/{command → commands}/gsd/settings.md +0 -0
- /package/{command → commands}/gsd/update.md +0 -0
- /package/{command → commands}/gsd/whats-new.md +0 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command for GSD-OpenCode CLI.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the update functionality to check for and install
|
|
5
|
+
* new versions of GSD-OpenCode, showing version comparisons, requiring
|
|
6
|
+
* interactive confirmation, and displaying progress during installation.
|
|
7
|
+
*
|
|
8
|
+
* Implements requirements:
|
|
9
|
+
* - CLI-05: User can run gsd-opencode update to update to latest version
|
|
10
|
+
* - UPDATE-01: Update checks npm registry for latest version
|
|
11
|
+
* - UPDATE-02: Update supports --beta flag for private registry
|
|
12
|
+
* - UPDATE-03: Update supports specifying exact version to install
|
|
13
|
+
* - UPDATE-04: Update shows current and target versions before proceeding
|
|
14
|
+
* - UPDATE-05: Update preserves existing installation scope
|
|
15
|
+
* - UPDATE-06: Update performs full install procedure including path replacement
|
|
16
|
+
* - ERROR-01: All commands handle permission errors (EACCES) with exit code 2
|
|
17
|
+
* - ERROR-02: All commands handle signal interrupts (SIGINT/SIGTERM) gracefully
|
|
18
|
+
* - ERROR-03: All commands support --verbose flag for detailed debugging output
|
|
19
|
+
*
|
|
20
|
+
* @module commands/update
|
|
21
|
+
* @description Update command for managing GSD-OpenCode versions
|
|
22
|
+
* @example
|
|
23
|
+
* // Update to latest stable version
|
|
24
|
+
* await updateCommand({});
|
|
25
|
+
*
|
|
26
|
+
* // Update to latest beta
|
|
27
|
+
* await updateCommand({ beta: true });
|
|
28
|
+
*
|
|
29
|
+
* // Update to specific version
|
|
30
|
+
* await updateCommand({ version: '2.0.0' });
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { ScopeManager } from '../services/scope-manager.js';
|
|
34
|
+
import { BackupManager } from '../services/backup-manager.js';
|
|
35
|
+
import { FileOperations } from '../services/file-ops.js';
|
|
36
|
+
import { UpdateService } from '../services/update-service.js';
|
|
37
|
+
import { NpmRegistry } from '../utils/npm-registry.js';
|
|
38
|
+
import { promptConfirmation } from '../utils/interactive.js';
|
|
39
|
+
import { logger, setVerbose } from '../utils/logger.js';
|
|
40
|
+
import { ERROR_CODES } from '../../lib/constants.js';
|
|
41
|
+
import fs from 'fs/promises';
|
|
42
|
+
import path from 'path';
|
|
43
|
+
import { fileURLToPath } from 'url';
|
|
44
|
+
import ora from 'ora';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets the package version from package.json.
|
|
48
|
+
*
|
|
49
|
+
* @returns {Promise<string>} The package version
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
async function getPackageVersion() {
|
|
53
|
+
try {
|
|
54
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
55
|
+
const __dirname = path.dirname(__filename);
|
|
56
|
+
const packageRoot = path.resolve(__dirname, '../..');
|
|
57
|
+
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
58
|
+
|
|
59
|
+
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
60
|
+
const pkg = JSON.parse(content);
|
|
61
|
+
return pkg.version || '1.0.0';
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.warning('Could not read package version, using 1.0.0');
|
|
64
|
+
return '1.0.0';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Displays formatted update information.
|
|
70
|
+
*
|
|
71
|
+
* Shows current version, target version, and installation scope
|
|
72
|
+
* in a clear, formatted manner.
|
|
73
|
+
*
|
|
74
|
+
* @param {string|null} current - Current installed version
|
|
75
|
+
* @param {string} target - Target version to install
|
|
76
|
+
* @param {string} scopeLabel - Label for scope (Global/Local)
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
function displayUpdateInfo(current, target, scopeLabel) {
|
|
80
|
+
logger.heading(`${scopeLabel} Installation Update`);
|
|
81
|
+
logger.dim('========================');
|
|
82
|
+
logger.dim('');
|
|
83
|
+
|
|
84
|
+
logger.info(`Current version: ${current || 'Not installed'}`);
|
|
85
|
+
logger.info(`Target version: ${target}`);
|
|
86
|
+
logger.info(`Scope: ${scopeLabel}`);
|
|
87
|
+
logger.dim('');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Displays update results after completion.
|
|
92
|
+
*
|
|
93
|
+
* Shows success/failure status, backup location, migration status, and next steps.
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} result - Update result from performUpdate()
|
|
96
|
+
* @param {string} scopeLabel - Label for scope (Global/Local)
|
|
97
|
+
* @param {boolean} isDryRun - Whether this was a dry run
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
function displayUpdateResults(result, scopeLabel, isDryRun = false) {
|
|
101
|
+
if (isDryRun) {
|
|
102
|
+
logger.heading(`Dry Run Results for ${scopeLabel} Installation`);
|
|
103
|
+
logger.dim('=====================================');
|
|
104
|
+
logger.dim('');
|
|
105
|
+
logger.info('No changes were made (dry run mode)');
|
|
106
|
+
} else {
|
|
107
|
+
logger.heading(`Update Results for ${scopeLabel} Installation`);
|
|
108
|
+
logger.dim('=====================================');
|
|
109
|
+
logger.dim('');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (result.success) {
|
|
113
|
+
if (isDryRun) {
|
|
114
|
+
logger.success(`Would update to version ${result.version}`);
|
|
115
|
+
} else {
|
|
116
|
+
logger.success(`Updated to version ${result.version}`);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
logger.error('Update failed');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Display migration status if applicable
|
|
123
|
+
if (result.stats.structureMigrated) {
|
|
124
|
+
logger.dim('');
|
|
125
|
+
logger.success('Structure migrated: Old (command/gsd/) → New (commands/gsd/)');
|
|
126
|
+
if (result.stats.migrationBackup) {
|
|
127
|
+
logger.dim(` Migration backup: ${result.stats.migrationBackup}`);
|
|
128
|
+
}
|
|
129
|
+
} else if (result.stats.migrationSkipped) {
|
|
130
|
+
logger.dim('');
|
|
131
|
+
logger.warning('Structure migration was skipped (--skip-migration flag)');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (result.errors && result.errors.length > 0) {
|
|
135
|
+
logger.dim('');
|
|
136
|
+
logger.info('Errors:');
|
|
137
|
+
for (const error of result.errors) {
|
|
138
|
+
logger.dim(` ✗ ${error}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
logger.dim('');
|
|
143
|
+
if (isDryRun) {
|
|
144
|
+
logger.dim('To perform the actual update, run without --dry-run');
|
|
145
|
+
} else {
|
|
146
|
+
logger.dim('Next steps:');
|
|
147
|
+
logger.dim(" Run 'gsd-opencode list' to verify the installation");
|
|
148
|
+
logger.dim(" Run 'gsd-opencode check' to verify installation health");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Main update command function.
|
|
154
|
+
*
|
|
155
|
+
* Orchestrates the update process:
|
|
156
|
+
* 1. Parse options and set verbose mode
|
|
157
|
+
* 2. Determine scopes to check (global, local, or both)
|
|
158
|
+
* 3. For each scope:
|
|
159
|
+
* - Check if installed
|
|
160
|
+
* - Create service instances
|
|
161
|
+
* - Check for available updates
|
|
162
|
+
* - Display version comparison
|
|
163
|
+
* - Prompt for confirmation (unless --force)
|
|
164
|
+
* - Perform update with progress indication
|
|
165
|
+
* - Display results
|
|
166
|
+
* 4. Return appropriate exit code
|
|
167
|
+
*
|
|
168
|
+
* @param {Object} options - Command options
|
|
169
|
+
* @param {boolean} [options.global] - Update global installation only
|
|
170
|
+
* @param {boolean} [options.local] - Update local installation only
|
|
171
|
+
* @param {boolean} [options.beta] - Update to beta version from @rokicool/gsd-opencode
|
|
172
|
+
* @param {boolean} [options.verbose] - Enable verbose output for debugging
|
|
173
|
+
* @param {string} [options.version] - Specific version to install
|
|
174
|
+
* @param {boolean} [options.force] - Skip confirmation prompt
|
|
175
|
+
* @param {boolean} [options.dryRun] - Show what would be done without making changes
|
|
176
|
+
* @param {boolean} [options.skipMigration] - Skip structure migration (not recommended)
|
|
177
|
+
* @returns {Promise<number>} Exit code (0=success, 1=error, 2=permission, 130=interrupted)
|
|
178
|
+
* @async
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* // Update to latest stable
|
|
182
|
+
* const exitCode = await updateCommand({});
|
|
183
|
+
*
|
|
184
|
+
* // Update to latest beta
|
|
185
|
+
* const exitCode = await updateCommand({ beta: true });
|
|
186
|
+
*
|
|
187
|
+
* // Update to specific version
|
|
188
|
+
* const exitCode = await updateCommand({ version: '2.0.0' });
|
|
189
|
+
*
|
|
190
|
+
* // Update global only with force
|
|
191
|
+
* const exitCode = await updateCommand({ global: true, force: true });
|
|
192
|
+
*/
|
|
193
|
+
export async function updateCommand(options = {}) {
|
|
194
|
+
const verbose = options.verbose || false;
|
|
195
|
+
setVerbose(verbose);
|
|
196
|
+
|
|
197
|
+
logger.debug('Starting update command');
|
|
198
|
+
logger.debug(`Options: global=${options.global}, local=${options.local}, beta=${options.beta}, version=${options.version}, force=${options.force}, verbose=${verbose}, dryRun=${options.dryRun}, skipMigration=${options.skipMigration}`);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
logger.heading('GSD-OpenCode Update');
|
|
202
|
+
logger.dim('===================');
|
|
203
|
+
logger.dim('');
|
|
204
|
+
|
|
205
|
+
// Determine scopes to check
|
|
206
|
+
const scopesToCheck = [];
|
|
207
|
+
if (options.global) {
|
|
208
|
+
scopesToCheck.push('global');
|
|
209
|
+
} else if (options.local) {
|
|
210
|
+
scopesToCheck.push('local');
|
|
211
|
+
} else {
|
|
212
|
+
scopesToCheck.push('global', 'local');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let anyInstalled = false;
|
|
216
|
+
let anyUpdated = false;
|
|
217
|
+
let anyFailed = false;
|
|
218
|
+
|
|
219
|
+
for (const scope of scopesToCheck) {
|
|
220
|
+
try {
|
|
221
|
+
const scopeManager = new ScopeManager({ scope });
|
|
222
|
+
const scopeLabel = scope.charAt(0).toUpperCase() + scope.slice(1);
|
|
223
|
+
|
|
224
|
+
logger.debug(`Checking ${scope} installation...`);
|
|
225
|
+
|
|
226
|
+
// Check if installed (unless --force which allows fresh install)
|
|
227
|
+
const isInstalled = await scopeManager.isInstalled();
|
|
228
|
+
if (!isInstalled && !options.force) {
|
|
229
|
+
logger.info(`No installation found at ${scopeLabel.toLowerCase()} scope`);
|
|
230
|
+
logger.dim('');
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
anyInstalled = true;
|
|
235
|
+
|
|
236
|
+
// Create service instances
|
|
237
|
+
const backupManager = new BackupManager(scopeManager, logger);
|
|
238
|
+
const fileOps = new FileOperations(scopeManager, logger);
|
|
239
|
+
const npmRegistry = new NpmRegistry({ logger });
|
|
240
|
+
|
|
241
|
+
// Determine package name
|
|
242
|
+
const packageName = options.beta ? '@rokicool/gsd-opencode' : 'gsd-opencode';
|
|
243
|
+
|
|
244
|
+
// Create UpdateService
|
|
245
|
+
const updateService = new UpdateService({
|
|
246
|
+
scopeManager,
|
|
247
|
+
backupManager,
|
|
248
|
+
fileOps,
|
|
249
|
+
npmRegistry,
|
|
250
|
+
logger,
|
|
251
|
+
packageName
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Check for updates
|
|
255
|
+
logger.debug('Checking for available updates...');
|
|
256
|
+
const checkResult = await updateService.checkForUpdate();
|
|
257
|
+
|
|
258
|
+
if (checkResult.error) {
|
|
259
|
+
logger.error(`Failed to check for updates: ${checkResult.error}`);
|
|
260
|
+
anyFailed = true;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Handle already up to date
|
|
265
|
+
if (!checkResult.updateAvailable && !options.version) {
|
|
266
|
+
logger.success(`${scopeLabel} installation is up to date (${checkResult.currentVersion})`);
|
|
267
|
+
logger.dim('');
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Determine target version
|
|
272
|
+
const targetVersion = options.version || checkResult.latestVersion;
|
|
273
|
+
|
|
274
|
+
if (!targetVersion) {
|
|
275
|
+
logger.error('Could not determine target version');
|
|
276
|
+
anyFailed = true;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Display update info
|
|
281
|
+
displayUpdateInfo(checkResult.currentVersion, targetVersion, scopeLabel);
|
|
282
|
+
|
|
283
|
+
// If specific version requested, validate it exists
|
|
284
|
+
if (options.version) {
|
|
285
|
+
const versionExists = await npmRegistry.versionExists(packageName, options.version);
|
|
286
|
+
if (!versionExists) {
|
|
287
|
+
logger.error(`Version ${options.version} does not exist for ${packageName}`);
|
|
288
|
+
anyFailed = true;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Prompt for confirmation (unless --force)
|
|
294
|
+
if (!options.force) {
|
|
295
|
+
logger.debug('Requesting user confirmation...');
|
|
296
|
+
const confirmed = await promptConfirmation(
|
|
297
|
+
`Proceed with update to ${targetVersion}?`,
|
|
298
|
+
true
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
// Handle SIGINT (Ctrl+C) - user cancelled with interrupt
|
|
302
|
+
if (confirmed === null) {
|
|
303
|
+
logger.info('Update cancelled');
|
|
304
|
+
return ERROR_CODES.INTERRUPTED;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Handle explicit "no" response
|
|
308
|
+
if (!confirmed) {
|
|
309
|
+
logger.info('Update cancelled');
|
|
310
|
+
return ERROR_CODES.SUCCESS;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
logger.debug('User confirmed update');
|
|
314
|
+
} else {
|
|
315
|
+
logger.debug('Skipping confirmation (--force flag)');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Perform update with progress indication
|
|
319
|
+
const spinner = ora({
|
|
320
|
+
text: 'Starting update...',
|
|
321
|
+
spinner: 'dots',
|
|
322
|
+
color: 'cyan'
|
|
323
|
+
}).start();
|
|
324
|
+
|
|
325
|
+
const updateResult = await updateService.performUpdate(targetVersion, {
|
|
326
|
+
onProgress: ({ phase, current, total, message, overallProgress }) => {
|
|
327
|
+
spinner.text = `${phase}: ${message} (${overallProgress}%)`;
|
|
328
|
+
},
|
|
329
|
+
dryRun: options.dryRun,
|
|
330
|
+
skipMigration: options.skipMigration
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (updateResult.success) {
|
|
334
|
+
spinner.succeed(`Updated to ${targetVersion}`);
|
|
335
|
+
} else {
|
|
336
|
+
spinner.fail('Update failed');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
logger.dim('');
|
|
340
|
+
|
|
341
|
+
// Display results
|
|
342
|
+
displayUpdateResults(updateResult, scopeLabel, options.dryRun);
|
|
343
|
+
|
|
344
|
+
// Show backup location if created
|
|
345
|
+
if (updateResult.stats.backupCreated && !options.dryRun) {
|
|
346
|
+
const backupDir = backupManager.getBackupDir();
|
|
347
|
+
logger.dim(`Backup saved to: ${backupDir}`);
|
|
348
|
+
logger.dim('');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Show warning if skipMigration was used
|
|
352
|
+
if (options.skipMigration) {
|
|
353
|
+
logger.warning('Structure migration was skipped. Old structure may cause issues.');
|
|
354
|
+
logger.dim(' Run "gsd-opencode update" without --skip-migration to migrate.');
|
|
355
|
+
logger.dim('');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Track overall status
|
|
359
|
+
if (updateResult.success) {
|
|
360
|
+
anyUpdated = true;
|
|
361
|
+
} else {
|
|
362
|
+
anyFailed = true;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
} catch (error) {
|
|
366
|
+
logger.error(`Failed to update ${scope} installation: ${error.message}`);
|
|
367
|
+
anyFailed = true;
|
|
368
|
+
|
|
369
|
+
if (verbose) {
|
|
370
|
+
logger.debug(error.stack);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Overall status message
|
|
376
|
+
logger.dim('');
|
|
377
|
+
|
|
378
|
+
if (!anyInstalled && !options.force) {
|
|
379
|
+
logger.info('No GSD-OpenCode installation found to update');
|
|
380
|
+
logger.dim('');
|
|
381
|
+
logger.info("Run 'gsd-opencode install' to install");
|
|
382
|
+
logger.dim('');
|
|
383
|
+
return ERROR_CODES.SUCCESS;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (anyFailed) {
|
|
387
|
+
logger.error('Some updates failed. Run gsd-opencode check for details.');
|
|
388
|
+
return ERROR_CODES.GENERAL_ERROR;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (anyUpdated) {
|
|
392
|
+
logger.success('All updates completed successfully');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return ERROR_CODES.SUCCESS;
|
|
396
|
+
|
|
397
|
+
} catch (error) {
|
|
398
|
+
// Handle Ctrl+C during async operations (AbortPromptError from @inquirer/prompts)
|
|
399
|
+
if (error.name === 'AbortPromptError' || error.message?.includes('cancel')) {
|
|
400
|
+
logger.info('Update cancelled by user');
|
|
401
|
+
return ERROR_CODES.INTERRUPTED;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Handle permission errors (EACCES)
|
|
405
|
+
if (error.code === 'EACCES') {
|
|
406
|
+
logger.error('Permission denied: Cannot access installation directory');
|
|
407
|
+
logger.dim('');
|
|
408
|
+
logger.dim('Suggestion: Check directory permissions or run with appropriate privileges');
|
|
409
|
+
return ERROR_CODES.PERMISSION_ERROR;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Handle network errors
|
|
413
|
+
if (error.message?.includes('ENOTFOUND') || error.message?.includes('ECONNREFUSED')) {
|
|
414
|
+
logger.error('Network error: Unable to reach npm registry');
|
|
415
|
+
logger.dim('');
|
|
416
|
+
logger.dim('Suggestion: Check your internet connection and try again');
|
|
417
|
+
return ERROR_CODES.GENERAL_ERROR;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Handle version not found
|
|
421
|
+
if (error.message?.includes('not found') || error.message?.includes('E404')) {
|
|
422
|
+
logger.error(`Package or version not found: ${error.message}`);
|
|
423
|
+
return ERROR_CODES.GENERAL_ERROR;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Handle all other errors
|
|
427
|
+
logger.error(`Update failed: ${error.message}`);
|
|
428
|
+
|
|
429
|
+
if (verbose && error.stack) {
|
|
430
|
+
logger.dim(error.stack);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return ERROR_CODES.GENERAL_ERROR;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Default export for the update command.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* import updateCommand from './commands/update.js';
|
|
442
|
+
* const exitCode = await updateCommand({ beta: true });
|
|
443
|
+
*/
|
|
444
|
+
export default updateCommand;
|