pnpm-catalog-updates 1.0.3 → 1.1.0
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/README.md +15 -0
- package/dist/index.js +22031 -10684
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cli/__tests__/commandRegistrar.test.ts +248 -0
- package/src/cli/commandRegistrar.ts +785 -0
- package/src/cli/commands/__tests__/aiCommand.test.ts +161 -0
- package/src/cli/commands/__tests__/analyzeCommand.test.ts +283 -0
- package/src/cli/commands/__tests__/checkCommand.test.ts +435 -0
- package/src/cli/commands/__tests__/graphCommand.test.ts +312 -0
- package/src/cli/commands/__tests__/initCommand.test.ts +317 -0
- package/src/cli/commands/__tests__/rollbackCommand.test.ts +400 -0
- package/src/cli/commands/__tests__/securityCommand.test.ts +467 -0
- package/src/cli/commands/__tests__/themeCommand.test.ts +166 -0
- package/src/cli/commands/__tests__/updateCommand.test.ts +720 -0
- package/src/cli/commands/__tests__/workspaceCommand.test.ts +286 -0
- package/src/cli/commands/aiCommand.ts +163 -0
- package/src/cli/commands/analyzeCommand.ts +219 -0
- package/src/cli/commands/checkCommand.ts +91 -98
- package/src/cli/commands/graphCommand.ts +475 -0
- package/src/cli/commands/initCommand.ts +64 -54
- package/src/cli/commands/rollbackCommand.ts +334 -0
- package/src/cli/commands/securityCommand.ts +165 -100
- package/src/cli/commands/themeCommand.ts +148 -0
- package/src/cli/commands/updateCommand.ts +215 -263
- package/src/cli/commands/workspaceCommand.ts +73 -0
- package/src/cli/constants/cliChoices.ts +93 -0
- package/src/cli/formatters/__tests__/__snapshots__/outputFormatter.test.ts.snap +557 -0
- package/src/cli/formatters/__tests__/ciFormatter.test.ts +526 -0
- package/src/cli/formatters/__tests__/outputFormatter.test.ts +448 -0
- package/src/cli/formatters/__tests__/progressBar.test.ts +709 -0
- package/src/cli/formatters/ciFormatter.ts +964 -0
- package/src/cli/formatters/colorUtils.ts +145 -0
- package/src/cli/formatters/outputFormatter.ts +615 -332
- package/src/cli/formatters/progressBar.ts +43 -52
- package/src/cli/formatters/versionFormatter.ts +132 -0
- package/src/cli/handlers/aiAnalysisHandler.ts +205 -0
- package/src/cli/handlers/changelogHandler.ts +113 -0
- package/src/cli/handlers/index.ts +9 -0
- package/src/cli/handlers/installHandler.ts +130 -0
- package/src/cli/index.ts +175 -726
- package/src/cli/interactive/InteractiveOptionsCollector.ts +387 -0
- package/src/cli/interactive/interactivePrompts.ts +189 -83
- package/src/cli/interactive/optionUtils.ts +89 -0
- package/src/cli/themes/colorTheme.ts +43 -16
- package/src/cli/utils/cliOutput.ts +118 -0
- package/src/cli/utils/commandHelpers.ts +249 -0
- package/src/cli/validators/commandValidator.ts +321 -336
- package/src/cli/validators/index.ts +37 -2
- package/src/cli/options/globalOptions.ts +0 -437
- package/src/cli/options/index.ts +0 -5
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Provides smart prompts and auto-completion for CLI commands
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { FileSystemService } from '@pcu/core'
|
|
7
|
+
import { FileSystemService, FileWorkspaceRepository, WorkspaceService } from '@pcu/core'
|
|
8
|
+
import { logger, t } from '@pcu/utils'
|
|
8
9
|
import chalk from 'chalk'
|
|
9
10
|
import inquirer from 'inquirer'
|
|
10
11
|
import { StyledText } from '../themes/colorTheme.js'
|
|
12
|
+
import { cliOutput } from '../utils/cliOutput.js'
|
|
11
13
|
|
|
12
14
|
export interface AutoCompleteOption {
|
|
13
15
|
name: string
|
|
@@ -15,6 +17,37 @@ export interface AutoCompleteOption {
|
|
|
15
17
|
description?: string
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Package choice for selection prompts
|
|
22
|
+
*/
|
|
23
|
+
interface PackageChoice {
|
|
24
|
+
name: string
|
|
25
|
+
current: string
|
|
26
|
+
latest: string
|
|
27
|
+
type: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Configuration wizard result
|
|
32
|
+
*/
|
|
33
|
+
interface ConfigurationWizardResult {
|
|
34
|
+
theme: string
|
|
35
|
+
interactive: boolean
|
|
36
|
+
backup: boolean
|
|
37
|
+
updateStrategy: string
|
|
38
|
+
timeout: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Update impact preview
|
|
43
|
+
*/
|
|
44
|
+
interface UpdateImpact {
|
|
45
|
+
totalUpdates: number
|
|
46
|
+
riskLevel: string
|
|
47
|
+
affectedCount: number
|
|
48
|
+
securityUpdates: number
|
|
49
|
+
}
|
|
50
|
+
|
|
18
51
|
export class InteractivePrompts {
|
|
19
52
|
private fsService: FileSystemService
|
|
20
53
|
|
|
@@ -25,9 +58,7 @@ export class InteractivePrompts {
|
|
|
25
58
|
/**
|
|
26
59
|
* Interactive package selection with search
|
|
27
60
|
*/
|
|
28
|
-
async selectPackages(
|
|
29
|
-
packages: Array<{ name: string; current: string; latest: string; type: string }>
|
|
30
|
-
): Promise<string[]> {
|
|
61
|
+
async selectPackages(packages: PackageChoice[]): Promise<string[]> {
|
|
31
62
|
if (packages.length === 0) {
|
|
32
63
|
return []
|
|
33
64
|
}
|
|
@@ -41,12 +72,12 @@ export class InteractivePrompts {
|
|
|
41
72
|
const answers = await inquirer.prompt({
|
|
42
73
|
type: 'checkbox',
|
|
43
74
|
name: 'selectedPackages',
|
|
44
|
-
message: StyledText.iconPackage('
|
|
75
|
+
message: StyledText.iconPackage(t('prompt.selectPackages')),
|
|
45
76
|
choices,
|
|
46
77
|
pageSize: 15,
|
|
47
78
|
validate: (input: unknown) => {
|
|
48
79
|
const selected = input as string[]
|
|
49
|
-
return selected.length > 0 || '
|
|
80
|
+
return selected.length > 0 || t('prompt.selectAtLeastOne')
|
|
50
81
|
},
|
|
51
82
|
})
|
|
52
83
|
|
|
@@ -66,7 +97,7 @@ export class InteractivePrompts {
|
|
|
66
97
|
}
|
|
67
98
|
|
|
68
99
|
const choices = [
|
|
69
|
-
{ name: '
|
|
100
|
+
{ name: t('prompt.allCatalogs'), value: 'all' },
|
|
70
101
|
...catalogs.map((name) => ({ name, value: name })),
|
|
71
102
|
]
|
|
72
103
|
|
|
@@ -74,7 +105,7 @@ export class InteractivePrompts {
|
|
|
74
105
|
{
|
|
75
106
|
type: 'list',
|
|
76
107
|
name: 'catalog',
|
|
77
|
-
message: StyledText.iconCatalog('
|
|
108
|
+
message: StyledText.iconCatalog(t('prompt.selectCatalog')),
|
|
78
109
|
choices,
|
|
79
110
|
pageSize: 10,
|
|
80
111
|
},
|
|
@@ -88,18 +119,18 @@ export class InteractivePrompts {
|
|
|
88
119
|
*/
|
|
89
120
|
async selectUpdateStrategy(): Promise<string> {
|
|
90
121
|
const strategies = [
|
|
91
|
-
{ name: '
|
|
92
|
-
{ name: '
|
|
93
|
-
{ name: '
|
|
94
|
-
{ name: '
|
|
95
|
-
{ name: '
|
|
122
|
+
{ name: t('prompt.strategyLatest'), value: 'latest' },
|
|
123
|
+
{ name: t('prompt.strategyGreatest'), value: 'greatest' },
|
|
124
|
+
{ name: t('prompt.strategyMinor'), value: 'minor' },
|
|
125
|
+
{ name: t('prompt.strategyPatch'), value: 'patch' },
|
|
126
|
+
{ name: t('prompt.strategyNewest'), value: 'newest' },
|
|
96
127
|
]
|
|
97
128
|
|
|
98
129
|
const answers = await inquirer.prompt([
|
|
99
130
|
{
|
|
100
131
|
type: 'list',
|
|
101
132
|
name: 'strategy',
|
|
102
|
-
message: StyledText.iconUpdate('
|
|
133
|
+
message: StyledText.iconUpdate(t('prompt.selectUpdateStrategy')),
|
|
103
134
|
choices: strategies,
|
|
104
135
|
},
|
|
105
136
|
])
|
|
@@ -111,16 +142,16 @@ export class InteractivePrompts {
|
|
|
111
142
|
* Confirm dangerous operations
|
|
112
143
|
*/
|
|
113
144
|
async confirmDangerousOperation(operation: string, details?: string): Promise<boolean> {
|
|
114
|
-
|
|
145
|
+
cliOutput.print('')
|
|
115
146
|
if (details) {
|
|
116
|
-
|
|
147
|
+
cliOutput.print(chalk.yellow(`⚠️ ${t('prompt.warning')}`), details)
|
|
117
148
|
}
|
|
118
149
|
|
|
119
150
|
const answers = await inquirer.prompt([
|
|
120
151
|
{
|
|
121
152
|
type: 'confirm',
|
|
122
153
|
name: 'confirmed',
|
|
123
|
-
message: StyledText.warning(
|
|
154
|
+
message: StyledText.warning(t('prompt.confirmOperation', { operation })),
|
|
124
155
|
default: false,
|
|
125
156
|
},
|
|
126
157
|
])
|
|
@@ -133,7 +164,7 @@ export class InteractivePrompts {
|
|
|
133
164
|
*/
|
|
134
165
|
async autoCompletePackage(
|
|
135
166
|
packages: string[],
|
|
136
|
-
message: string = '
|
|
167
|
+
message: string = t('prompt.selectPackage')
|
|
137
168
|
): Promise<string> {
|
|
138
169
|
const answers = await inquirer.prompt([
|
|
139
170
|
{
|
|
@@ -154,15 +185,15 @@ export class InteractivePrompts {
|
|
|
154
185
|
async selectWorkspacePath(): Promise<string> {
|
|
155
186
|
const currentDir = process.cwd()
|
|
156
187
|
const choices = [
|
|
157
|
-
{ name:
|
|
158
|
-
{ name: '
|
|
188
|
+
{ name: t('prompt.currentDirectory', { path: currentDir }), value: currentDir },
|
|
189
|
+
{ name: t('prompt.browseDirectory'), value: 'browse' },
|
|
159
190
|
]
|
|
160
191
|
|
|
161
192
|
const answers = await inquirer.prompt([
|
|
162
193
|
{
|
|
163
194
|
type: 'list',
|
|
164
195
|
name: 'path',
|
|
165
|
-
message:
|
|
196
|
+
message: t('prompt.selectWorkspace'),
|
|
166
197
|
choices,
|
|
167
198
|
},
|
|
168
199
|
])
|
|
@@ -180,10 +211,10 @@ export class InteractivePrompts {
|
|
|
180
211
|
private async browseDirectory(currentPath = process.cwd()): Promise<string> {
|
|
181
212
|
const directoryNames = await this.fsService.listDirectories(currentPath)
|
|
182
213
|
const choices = [
|
|
183
|
-
{ name: '
|
|
184
|
-
{ name:
|
|
214
|
+
{ name: t('prompt.parentDirectory'), value: '..' },
|
|
215
|
+
{ name: t('prompt.currentDirectory', { path: currentPath }), value: '.' },
|
|
185
216
|
...directoryNames.map((name: string) => ({
|
|
186
|
-
name:
|
|
217
|
+
name: `[dir] ${name}`,
|
|
187
218
|
value: `${currentPath}/${name}`,
|
|
188
219
|
})),
|
|
189
220
|
]
|
|
@@ -192,7 +223,7 @@ export class InteractivePrompts {
|
|
|
192
223
|
{
|
|
193
224
|
type: 'list',
|
|
194
225
|
name: 'selected',
|
|
195
|
-
message:
|
|
226
|
+
message: t('prompt.browsePath', { path: currentPath }),
|
|
196
227
|
choices,
|
|
197
228
|
pageSize: 15,
|
|
198
229
|
},
|
|
@@ -222,7 +253,7 @@ export class InteractivePrompts {
|
|
|
222
253
|
{
|
|
223
254
|
type: 'confirm',
|
|
224
255
|
name: 'useThis',
|
|
225
|
-
message:
|
|
256
|
+
message: t('prompt.useAsWorkspace', { path: answers.selected }),
|
|
226
257
|
default: true,
|
|
227
258
|
},
|
|
228
259
|
])
|
|
@@ -235,21 +266,42 @@ export class InteractivePrompts {
|
|
|
235
266
|
return this.browseDirectory(answers.selected)
|
|
236
267
|
}
|
|
237
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Simple theme selection with cancel option
|
|
271
|
+
*/
|
|
272
|
+
async selectTheme(): Promise<string | null> {
|
|
273
|
+
const answers = await inquirer.prompt({
|
|
274
|
+
type: 'list',
|
|
275
|
+
name: 'theme',
|
|
276
|
+
message: t('prompt.selectTheme'),
|
|
277
|
+
choices: [
|
|
278
|
+
{ name: t('prompt.themeDefault'), value: 'default' },
|
|
279
|
+
{ name: t('prompt.themeModern'), value: 'modern' },
|
|
280
|
+
{ name: t('prompt.themeMinimal'), value: 'minimal' },
|
|
281
|
+
{ name: t('prompt.themeNeon'), value: 'neon' },
|
|
282
|
+
{ name: t('prompt.cancel'), value: null },
|
|
283
|
+
],
|
|
284
|
+
default: 'default',
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
return answers.theme
|
|
288
|
+
}
|
|
289
|
+
|
|
238
290
|
/**
|
|
239
291
|
* Multi-step configuration wizard
|
|
240
292
|
*/
|
|
241
|
-
async configurationWizard(): Promise<
|
|
242
|
-
|
|
293
|
+
async configurationWizard(): Promise<ConfigurationWizardResult> {
|
|
294
|
+
cliOutput.print(chalk.bold.blue(`\n${t('prompt.configWizard')}\n`))
|
|
243
295
|
|
|
244
296
|
const themeAnswer = await inquirer.prompt({
|
|
245
297
|
type: 'list',
|
|
246
298
|
name: 'theme',
|
|
247
|
-
message: '
|
|
299
|
+
message: t('prompt.selectTheme'),
|
|
248
300
|
choices: [
|
|
249
|
-
{ name: '
|
|
250
|
-
{ name: '
|
|
251
|
-
{ name: '
|
|
252
|
-
{ name: '
|
|
301
|
+
{ name: t('prompt.themeDefault'), value: 'default' },
|
|
302
|
+
{ name: t('prompt.themeModern'), value: 'modern' },
|
|
303
|
+
{ name: t('prompt.themeMinimal'), value: 'minimal' },
|
|
304
|
+
{ name: t('prompt.themeNeon'), value: 'neon' },
|
|
253
305
|
],
|
|
254
306
|
default: 'default',
|
|
255
307
|
})
|
|
@@ -257,25 +309,25 @@ export class InteractivePrompts {
|
|
|
257
309
|
const interactiveAnswer = await inquirer.prompt({
|
|
258
310
|
type: 'confirm',
|
|
259
311
|
name: 'interactive',
|
|
260
|
-
message: '
|
|
312
|
+
message: t('prompt.enableInteractive'),
|
|
261
313
|
default: true,
|
|
262
314
|
})
|
|
263
315
|
|
|
264
316
|
const backupAnswer = await inquirer.prompt({
|
|
265
317
|
type: 'confirm',
|
|
266
318
|
name: 'backup',
|
|
267
|
-
message: '
|
|
319
|
+
message: t('prompt.createBackups'),
|
|
268
320
|
default: true,
|
|
269
321
|
})
|
|
270
322
|
|
|
271
323
|
const strategyAnswer = await inquirer.prompt({
|
|
272
324
|
type: 'list',
|
|
273
325
|
name: 'updateStrategy',
|
|
274
|
-
message: '
|
|
326
|
+
message: t('prompt.defaultStrategy'),
|
|
275
327
|
choices: [
|
|
276
|
-
{ name: '
|
|
277
|
-
{ name: '
|
|
278
|
-
{ name: '
|
|
328
|
+
{ name: t('prompt.strategyLatestStable'), value: 'latest' },
|
|
329
|
+
{ name: t('prompt.strategyMinorUpdates'), value: 'minor' },
|
|
330
|
+
{ name: t('prompt.strategyPatchUpdates'), value: 'patch' },
|
|
279
331
|
],
|
|
280
332
|
default: 'latest',
|
|
281
333
|
})
|
|
@@ -283,20 +335,20 @@ export class InteractivePrompts {
|
|
|
283
335
|
const timeoutAnswer = await inquirer.prompt({
|
|
284
336
|
type: 'number',
|
|
285
337
|
name: 'timeout',
|
|
286
|
-
message: '
|
|
338
|
+
message: t('prompt.networkTimeout'),
|
|
287
339
|
default: 30,
|
|
288
340
|
validate: (input: number | undefined) => {
|
|
289
|
-
if (input === undefined) return '
|
|
290
|
-
return input > 0 || '
|
|
341
|
+
if (input === undefined) return t('prompt.timeoutRequired')
|
|
342
|
+
return input > 0 || t('prompt.timeoutPositive')
|
|
291
343
|
},
|
|
292
344
|
})
|
|
293
345
|
|
|
294
|
-
const answers = {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
346
|
+
const answers: ConfigurationWizardResult = {
|
|
347
|
+
theme: themeAnswer.theme as string,
|
|
348
|
+
interactive: interactiveAnswer.interactive as boolean,
|
|
349
|
+
backup: backupAnswer.backup as boolean,
|
|
350
|
+
updateStrategy: strategyAnswer.updateStrategy as string,
|
|
351
|
+
timeout: timeoutAnswer.timeout as number,
|
|
300
352
|
}
|
|
301
353
|
|
|
302
354
|
return answers
|
|
@@ -305,25 +357,27 @@ export class InteractivePrompts {
|
|
|
305
357
|
/**
|
|
306
358
|
* Impact preview before update
|
|
307
359
|
*/
|
|
308
|
-
async previewImpact(impact:
|
|
309
|
-
|
|
360
|
+
async previewImpact(impact: UpdateImpact): Promise<boolean> {
|
|
361
|
+
cliOutput.print(chalk.bold.blue(`\n${t('prompt.impactPreview')}\n`))
|
|
310
362
|
|
|
311
363
|
// Display impact summary
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
364
|
+
cliOutput.print(t('prompt.packagesToUpdate', { count: impact.totalUpdates }))
|
|
365
|
+
cliOutput.print(t('prompt.riskLevel', { level: impact.riskLevel }))
|
|
366
|
+
cliOutput.print(t('prompt.affectedPackages', { count: impact.affectedCount }))
|
|
315
367
|
|
|
316
368
|
if (impact.securityUpdates > 0) {
|
|
317
|
-
|
|
369
|
+
cliOutput.print(
|
|
370
|
+
StyledText.iconSecurity(t('prompt.securityUpdatesCount', { count: impact.securityUpdates }))
|
|
371
|
+
)
|
|
318
372
|
}
|
|
319
373
|
|
|
320
|
-
|
|
374
|
+
cliOutput.print('')
|
|
321
375
|
|
|
322
376
|
const answers = await inquirer.prompt([
|
|
323
377
|
{
|
|
324
378
|
type: 'confirm',
|
|
325
379
|
name: 'proceed',
|
|
326
|
-
message: '
|
|
380
|
+
message: t('prompt.proceedWithUpdate'),
|
|
327
381
|
default: true,
|
|
328
382
|
},
|
|
329
383
|
])
|
|
@@ -336,17 +390,17 @@ export class InteractivePrompts {
|
|
|
336
390
|
*/
|
|
337
391
|
async errorRecoveryOptions(error: string): Promise<string> {
|
|
338
392
|
const options = [
|
|
339
|
-
{ name: '
|
|
340
|
-
{ name: '
|
|
341
|
-
{ name: '
|
|
342
|
-
{ name: '
|
|
393
|
+
{ name: t('prompt.retryOperation'), value: 'retry' },
|
|
394
|
+
{ name: t('prompt.skipPackage'), value: 'skip' },
|
|
395
|
+
{ name: t('prompt.continueRemaining'), value: 'continue' },
|
|
396
|
+
{ name: t('prompt.abortOperation'), value: 'abort' },
|
|
343
397
|
]
|
|
344
398
|
|
|
345
399
|
const answers = await inquirer.prompt([
|
|
346
400
|
{
|
|
347
401
|
type: 'list',
|
|
348
402
|
name: 'action',
|
|
349
|
-
message: StyledText.iconError(
|
|
403
|
+
message: StyledText.iconError(t('prompt.errorMessage', { error })),
|
|
350
404
|
choices: options,
|
|
351
405
|
},
|
|
352
406
|
])
|
|
@@ -373,8 +427,8 @@ export class InteractivePrompts {
|
|
|
373
427
|
/**
|
|
374
428
|
* Format package choice for display
|
|
375
429
|
*/
|
|
376
|
-
private formatPackageChoice(pkg:
|
|
377
|
-
const updateTypeColor: Record<string,
|
|
430
|
+
private formatPackageChoice(pkg: PackageChoice): string {
|
|
431
|
+
const updateTypeColor: Record<string, (text: string) => string> = {
|
|
378
432
|
major: chalk.red,
|
|
379
433
|
minor: chalk.yellow,
|
|
380
434
|
patch: chalk.green,
|
|
@@ -390,6 +444,15 @@ export class InteractivePrompts {
|
|
|
390
444
|
* Auto-completion utilities
|
|
391
445
|
*/
|
|
392
446
|
export class AutoCompleteManager {
|
|
447
|
+
/**
|
|
448
|
+
* Get workspace service instance for auto-completion
|
|
449
|
+
*/
|
|
450
|
+
private static getWorkspaceService(): WorkspaceService {
|
|
451
|
+
const fsService = new FileSystemService()
|
|
452
|
+
const repository = new FileWorkspaceRepository(fsService)
|
|
453
|
+
return new WorkspaceService(repository)
|
|
454
|
+
}
|
|
455
|
+
|
|
393
456
|
static async suggestWorkspaces(current: string): Promise<string[]> {
|
|
394
457
|
const suggestions: string[] = []
|
|
395
458
|
|
|
@@ -411,58 +474,101 @@ export class AutoCompleteManager {
|
|
|
411
474
|
suggestions.push(dir)
|
|
412
475
|
}
|
|
413
476
|
})
|
|
414
|
-
} catch {
|
|
415
|
-
//
|
|
477
|
+
} catch (error) {
|
|
478
|
+
// ERR-002: Log glob errors for debugging instead of silently ignoring
|
|
479
|
+
logger.debug('Failed to glob workspace pattern', { pattern, error })
|
|
416
480
|
}
|
|
417
481
|
}
|
|
418
482
|
|
|
419
483
|
return suggestions.filter((s) => s.toLowerCase().includes(current.toLowerCase()))
|
|
420
484
|
}
|
|
421
485
|
|
|
486
|
+
/**
|
|
487
|
+
* STUB-001: Suggest catalog names from current workspace
|
|
488
|
+
*/
|
|
422
489
|
static async suggestCatalogs(): Promise<string[]> {
|
|
423
|
-
|
|
490
|
+
try {
|
|
491
|
+
const workspaceService = AutoCompleteManager.getWorkspaceService()
|
|
492
|
+
const catalogs = await workspaceService.getCatalogs()
|
|
493
|
+
return catalogs.map((catalog) => catalog.name)
|
|
494
|
+
} catch (error) {
|
|
495
|
+
// Workspace not found or other error - return empty for auto-complete
|
|
496
|
+
logger.debug('Failed to get catalogs for auto-complete', { error })
|
|
497
|
+
return []
|
|
498
|
+
}
|
|
424
499
|
}
|
|
425
500
|
|
|
501
|
+
/**
|
|
502
|
+
* STUB-001: Suggest package names from current workspace catalogs
|
|
503
|
+
*/
|
|
426
504
|
static async suggestPackages(): Promise<string[]> {
|
|
427
|
-
|
|
505
|
+
try {
|
|
506
|
+
const workspaceService = AutoCompleteManager.getWorkspaceService()
|
|
507
|
+
const catalogs = await workspaceService.getCatalogs()
|
|
508
|
+
|
|
509
|
+
// Collect all unique package names from all catalogs
|
|
510
|
+
const packageNames = new Set<string>()
|
|
511
|
+
for (const catalog of catalogs) {
|
|
512
|
+
for (const pkgName of catalog.packages) {
|
|
513
|
+
packageNames.add(pkgName)
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return Array.from(packageNames).sort()
|
|
518
|
+
} catch (error) {
|
|
519
|
+
// Workspace not found or other error - return empty for auto-complete
|
|
520
|
+
logger.debug('Failed to get packages for auto-complete', { error })
|
|
521
|
+
return []
|
|
522
|
+
}
|
|
428
523
|
}
|
|
429
524
|
}
|
|
430
525
|
|
|
526
|
+
/**
|
|
527
|
+
* Command builder options type
|
|
528
|
+
*/
|
|
529
|
+
interface CommandBuilderOptions {
|
|
530
|
+
format?: string
|
|
531
|
+
interactive?: boolean
|
|
532
|
+
dryRun?: boolean
|
|
533
|
+
backup?: boolean
|
|
534
|
+
prerelease?: boolean
|
|
535
|
+
}
|
|
536
|
+
|
|
431
537
|
/**
|
|
432
538
|
* Interactive command builder
|
|
433
539
|
*/
|
|
434
540
|
export class InteractiveCommandBuilder {
|
|
435
541
|
static async buildCommand(): Promise<{
|
|
436
542
|
command: string
|
|
437
|
-
options:
|
|
543
|
+
options: CommandBuilderOptions
|
|
438
544
|
}> {
|
|
439
545
|
const baseCommand = await inquirer.prompt([
|
|
440
546
|
{
|
|
441
547
|
type: 'list',
|
|
442
548
|
name: 'command',
|
|
443
|
-
message: '
|
|
549
|
+
message: t('prompt.whatToDo'),
|
|
444
550
|
choices: [
|
|
445
|
-
{ name: '
|
|
446
|
-
{ name: '
|
|
447
|
-
{ name: '
|
|
448
|
-
{ name: '
|
|
551
|
+
{ name: t('prompt.checkForUpdates'), value: 'check' },
|
|
552
|
+
{ name: t('prompt.updateDependencies'), value: 'update' },
|
|
553
|
+
{ name: t('prompt.analyzeImpact'), value: 'analyze' },
|
|
554
|
+
{ name: t('prompt.showWorkspaceInfo'), value: 'workspace' },
|
|
449
555
|
],
|
|
450
556
|
},
|
|
451
557
|
])
|
|
452
558
|
|
|
453
|
-
const options:
|
|
559
|
+
const options: CommandBuilderOptions = {}
|
|
454
560
|
|
|
455
561
|
// Common options
|
|
456
562
|
const common = await inquirer.prompt([
|
|
457
563
|
{
|
|
458
564
|
type: 'list',
|
|
459
565
|
name: 'format',
|
|
460
|
-
message: '
|
|
566
|
+
message: t('prompt.outputFormat'),
|
|
461
567
|
choices: [
|
|
462
|
-
{ name: '
|
|
463
|
-
{ name: '
|
|
464
|
-
{ name: '
|
|
465
|
-
{ name: '
|
|
568
|
+
{ name: t('prompt.formatTable'), value: 'table' },
|
|
569
|
+
{ name: t('prompt.formatJson'), value: 'json' },
|
|
570
|
+
{ name: t('prompt.formatYaml'), value: 'yaml' },
|
|
571
|
+
{ name: t('prompt.formatMinimal'), value: 'minimal' },
|
|
466
572
|
],
|
|
467
573
|
default: 'table',
|
|
468
574
|
},
|
|
@@ -477,19 +583,19 @@ export class InteractiveCommandBuilder {
|
|
|
477
583
|
{
|
|
478
584
|
type: 'confirm',
|
|
479
585
|
name: 'interactive',
|
|
480
|
-
message: '
|
|
586
|
+
message: t('prompt.interactiveMode'),
|
|
481
587
|
default: true,
|
|
482
588
|
},
|
|
483
589
|
{
|
|
484
590
|
type: 'confirm',
|
|
485
591
|
name: 'dryRun',
|
|
486
|
-
message: '
|
|
592
|
+
message: t('prompt.dryRunMode'),
|
|
487
593
|
default: false,
|
|
488
594
|
},
|
|
489
595
|
{
|
|
490
596
|
type: 'confirm',
|
|
491
597
|
name: 'backup',
|
|
492
|
-
message: '
|
|
598
|
+
message: t('prompt.createBackup'),
|
|
493
599
|
default: true,
|
|
494
600
|
},
|
|
495
601
|
])
|
|
@@ -502,7 +608,7 @@ export class InteractiveCommandBuilder {
|
|
|
502
608
|
{
|
|
503
609
|
type: 'confirm',
|
|
504
610
|
name: 'includePrerelease',
|
|
505
|
-
message: '
|
|
611
|
+
message: t('prompt.includePrerelease'),
|
|
506
612
|
default: false,
|
|
507
613
|
},
|
|
508
614
|
])
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Option Utilities
|
|
3
|
+
*
|
|
4
|
+
* Common utilities for merging CLI options with defaults.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Option types supported by the merge utility
|
|
9
|
+
*/
|
|
10
|
+
type OptionType = 'string' | 'boolean' | 'number' | 'array' | 'optional-string'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Schema definition for a single option
|
|
14
|
+
*/
|
|
15
|
+
interface OptionSchema<T = unknown> {
|
|
16
|
+
type: OptionType
|
|
17
|
+
default: T
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Schema for a complete set of options
|
|
22
|
+
*/
|
|
23
|
+
export type OptionsSchema<T> = {
|
|
24
|
+
[K in keyof T]: OptionSchema<T[K]>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Merge a single value with its default based on type
|
|
29
|
+
*/
|
|
30
|
+
function mergeValue<T>(value: unknown, schema: OptionSchema<T>): T {
|
|
31
|
+
switch (schema.type) {
|
|
32
|
+
case 'string':
|
|
33
|
+
return ((value as string) || schema.default) as T
|
|
34
|
+
|
|
35
|
+
case 'optional-string':
|
|
36
|
+
return ((value as string) || undefined) as T
|
|
37
|
+
|
|
38
|
+
case 'boolean':
|
|
39
|
+
return ((value as boolean | undefined) ?? schema.default) as T
|
|
40
|
+
|
|
41
|
+
case 'number':
|
|
42
|
+
return ((value as number | undefined) ?? schema.default) as T
|
|
43
|
+
|
|
44
|
+
case 'array':
|
|
45
|
+
return ((value as T) || schema.default) as T
|
|
46
|
+
|
|
47
|
+
default:
|
|
48
|
+
return (value ?? schema.default) as T
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Merge existing options with defaults based on schema
|
|
54
|
+
*
|
|
55
|
+
* @param existing - Options provided by user/CLI
|
|
56
|
+
* @param schema - Schema defining types and defaults
|
|
57
|
+
* @returns Merged options with proper types
|
|
58
|
+
*/
|
|
59
|
+
export function mergeOptions<T extends Record<string, unknown>>(
|
|
60
|
+
existing: Record<string, unknown>,
|
|
61
|
+
schema: OptionsSchema<T>
|
|
62
|
+
): T {
|
|
63
|
+
const result = {} as T
|
|
64
|
+
|
|
65
|
+
for (const key of Object.keys(schema) as (keyof T)[]) {
|
|
66
|
+
const optionSchema = schema[key]
|
|
67
|
+
const value = existing[key as string]
|
|
68
|
+
result[key] = mergeValue(value, optionSchema)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Common option schemas reused across commands
|
|
76
|
+
*/
|
|
77
|
+
export const commonSchemas = {
|
|
78
|
+
catalog: { type: 'optional-string' as const, default: undefined as string | undefined },
|
|
79
|
+
format: { type: 'string' as const, default: 'table' as string },
|
|
80
|
+
target: { type: 'string' as const, default: 'latest' as string },
|
|
81
|
+
prerelease: { type: 'boolean' as const, default: false as boolean },
|
|
82
|
+
include: { type: 'array' as const, default: [] as string[] },
|
|
83
|
+
exclude: { type: 'array' as const, default: [] as string[] },
|
|
84
|
+
force: { type: 'boolean' as const, default: false as boolean },
|
|
85
|
+
dryRun: { type: 'boolean' as const, default: false as boolean },
|
|
86
|
+
ai: { type: 'boolean' as const, default: false as boolean },
|
|
87
|
+
provider: { type: 'string' as const, default: 'auto' as string },
|
|
88
|
+
analysisType: { type: 'string' as const, default: 'impact' as string },
|
|
89
|
+
}
|