genbox 1.0.3 → 1.0.4
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/commands/create.js +369 -130
- package/dist/commands/db-sync.js +364 -0
- package/dist/commands/init.js +669 -402
- package/dist/commands/profiles.js +333 -0
- package/dist/commands/push.js +140 -47
- package/dist/config-loader.js +529 -0
- package/dist/index.js +5 -1
- package/dist/profile-resolver.js +547 -0
- package/dist/scanner/compose-parser.js +441 -0
- package/dist/scanner/config-generator.js +620 -0
- package/dist/scanner/env-analyzer.js +503 -0
- package/dist/scanner/framework-detector.js +621 -0
- package/dist/scanner/index.js +424 -0
- package/dist/scanner/runtime-detector.js +330 -0
- package/dist/scanner/structure-detector.js +412 -0
- package/dist/scanner/types.js +7 -0
- package/dist/schema-v3.js +12 -0
- package/package.json +2 -1
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Structure Detector
|
|
4
|
+
*
|
|
5
|
+
* Detects project structure type:
|
|
6
|
+
* - Single app
|
|
7
|
+
* - Monorepo (npm/pnpm/yarn/nx/turborepo/lerna)
|
|
8
|
+
* - Microservices (docker-compose based)
|
|
9
|
+
* - Go workspaces
|
|
10
|
+
* - Python monorepos
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.StructureDetector = void 0;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
class StructureDetector {
|
|
50
|
+
/**
|
|
51
|
+
* Detect project structure
|
|
52
|
+
*/
|
|
53
|
+
async detect(root, exclude) {
|
|
54
|
+
// Run detectors in order of specificity (most specific first)
|
|
55
|
+
const detectors = [
|
|
56
|
+
() => this.detectNx(root),
|
|
57
|
+
() => this.detectTurborepo(root),
|
|
58
|
+
() => this.detectLerna(root),
|
|
59
|
+
() => this.detectPnpmWorkspace(root),
|
|
60
|
+
() => this.detectNpmWorkspace(root),
|
|
61
|
+
() => this.detectGoWorkspace(root),
|
|
62
|
+
() => this.detectPythonWorkspace(root),
|
|
63
|
+
() => this.detectMultiRepoWorkspace(root, exclude), // Multi-repo before microservices
|
|
64
|
+
() => this.detectMicroservices(root),
|
|
65
|
+
() => this.detectSingleApp(root),
|
|
66
|
+
];
|
|
67
|
+
for (const detector of detectors) {
|
|
68
|
+
const result = detector();
|
|
69
|
+
if (result) {
|
|
70
|
+
return {
|
|
71
|
+
type: result.type,
|
|
72
|
+
confidence: result.confidence,
|
|
73
|
+
indicators: result.indicators,
|
|
74
|
+
workspaceRoot: root,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Fallback
|
|
79
|
+
return {
|
|
80
|
+
type: 'single-app',
|
|
81
|
+
confidence: 'low',
|
|
82
|
+
indicators: ['No specific structure detected'],
|
|
83
|
+
workspaceRoot: root,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
detectNx(root) {
|
|
87
|
+
const nxJsonPath = path.join(root, 'nx.json');
|
|
88
|
+
if (!fs.existsSync(nxJsonPath))
|
|
89
|
+
return null;
|
|
90
|
+
const indicators = ['nx.json'];
|
|
91
|
+
// Check for additional Nx files
|
|
92
|
+
const workspaceJson = path.join(root, 'workspace.json');
|
|
93
|
+
if (fs.existsSync(workspaceJson)) {
|
|
94
|
+
indicators.push('workspace.json');
|
|
95
|
+
}
|
|
96
|
+
// Check for project.json files in subdirectories
|
|
97
|
+
const projectDirs = ['apps', 'libs', 'packages'];
|
|
98
|
+
for (const dir of projectDirs) {
|
|
99
|
+
const dirPath = path.join(root, dir);
|
|
100
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
101
|
+
indicators.push(`${dir}/ directory`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
type: 'monorepo-nx',
|
|
106
|
+
confidence: 'high',
|
|
107
|
+
indicators,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
detectTurborepo(root) {
|
|
111
|
+
const turboJsonPath = path.join(root, 'turbo.json');
|
|
112
|
+
if (!fs.existsSync(turboJsonPath))
|
|
113
|
+
return null;
|
|
114
|
+
const indicators = ['turbo.json'];
|
|
115
|
+
// Verify it has pipeline configuration
|
|
116
|
+
try {
|
|
117
|
+
const turboConfig = JSON.parse(fs.readFileSync(turboJsonPath, 'utf8'));
|
|
118
|
+
if (turboConfig.pipeline || turboConfig.tasks) {
|
|
119
|
+
indicators.push('Has pipeline/tasks configuration');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch { }
|
|
123
|
+
return {
|
|
124
|
+
type: 'monorepo-turborepo',
|
|
125
|
+
confidence: 'high',
|
|
126
|
+
indicators,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
detectLerna(root) {
|
|
130
|
+
const lernaJsonPath = path.join(root, 'lerna.json');
|
|
131
|
+
if (!fs.existsSync(lernaJsonPath))
|
|
132
|
+
return null;
|
|
133
|
+
const indicators = ['lerna.json'];
|
|
134
|
+
try {
|
|
135
|
+
const lernaConfig = JSON.parse(fs.readFileSync(lernaJsonPath, 'utf8'));
|
|
136
|
+
if (lernaConfig.packages) {
|
|
137
|
+
indicators.push(`Packages: ${lernaConfig.packages.join(', ')}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch { }
|
|
141
|
+
return {
|
|
142
|
+
type: 'monorepo-lerna',
|
|
143
|
+
confidence: 'high',
|
|
144
|
+
indicators,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
detectPnpmWorkspace(root) {
|
|
148
|
+
const pnpmWorkspacePath = path.join(root, 'pnpm-workspace.yaml');
|
|
149
|
+
if (!fs.existsSync(pnpmWorkspacePath))
|
|
150
|
+
return null;
|
|
151
|
+
const indicators = ['pnpm-workspace.yaml'];
|
|
152
|
+
// Check for pnpm-lock.yaml
|
|
153
|
+
if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) {
|
|
154
|
+
indicators.push('pnpm-lock.yaml');
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
type: 'monorepo-pnpm',
|
|
158
|
+
confidence: 'high',
|
|
159
|
+
indicators,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
detectNpmWorkspace(root) {
|
|
163
|
+
const packageJsonPath = path.join(root, 'package.json');
|
|
164
|
+
if (!fs.existsSync(packageJsonPath))
|
|
165
|
+
return null;
|
|
166
|
+
try {
|
|
167
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
168
|
+
// Check for workspaces field
|
|
169
|
+
if (!pkg.workspaces)
|
|
170
|
+
return null;
|
|
171
|
+
const workspaces = Array.isArray(pkg.workspaces)
|
|
172
|
+
? pkg.workspaces
|
|
173
|
+
: pkg.workspaces.packages || [];
|
|
174
|
+
if (workspaces.length === 0)
|
|
175
|
+
return null;
|
|
176
|
+
const indicators = [`package.json workspaces: ${workspaces.join(', ')}`];
|
|
177
|
+
// Determine if it's yarn or npm based on lockfile
|
|
178
|
+
const hasYarnLock = fs.existsSync(path.join(root, 'yarn.lock'));
|
|
179
|
+
return {
|
|
180
|
+
type: hasYarnLock ? 'monorepo-yarn' : 'monorepo-npm',
|
|
181
|
+
confidence: 'high',
|
|
182
|
+
indicators,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
detectGoWorkspace(root) {
|
|
190
|
+
const goWorkPath = path.join(root, 'go.work');
|
|
191
|
+
if (!fs.existsSync(goWorkPath))
|
|
192
|
+
return null;
|
|
193
|
+
const indicators = ['go.work'];
|
|
194
|
+
try {
|
|
195
|
+
const content = fs.readFileSync(goWorkPath, 'utf8');
|
|
196
|
+
const useMatches = content.match(/use\s+\./g);
|
|
197
|
+
if (useMatches) {
|
|
198
|
+
indicators.push(`${useMatches.length} use directives`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch { }
|
|
202
|
+
return {
|
|
203
|
+
type: 'monorepo-go',
|
|
204
|
+
confidence: 'high',
|
|
205
|
+
indicators,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
detectPythonWorkspace(root) {
|
|
209
|
+
const pyprojectPath = path.join(root, 'pyproject.toml');
|
|
210
|
+
if (!fs.existsSync(pyprojectPath))
|
|
211
|
+
return null;
|
|
212
|
+
try {
|
|
213
|
+
const content = fs.readFileSync(pyprojectPath, 'utf8');
|
|
214
|
+
// Check for Poetry workspace or PDM workspace
|
|
215
|
+
if (content.includes('[tool.poetry.packages]') ||
|
|
216
|
+
content.includes('[tool.pdm.packages]')) {
|
|
217
|
+
return {
|
|
218
|
+
type: 'monorepo-python',
|
|
219
|
+
confidence: 'high',
|
|
220
|
+
indicators: ['pyproject.toml with workspace packages'],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// Check for multiple Python projects in subdirectories
|
|
224
|
+
const projectDirs = ['packages', 'libs', 'services'];
|
|
225
|
+
const foundDirs = [];
|
|
226
|
+
for (const dir of projectDirs) {
|
|
227
|
+
const dirPath = path.join(root, dir);
|
|
228
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
229
|
+
const subDirs = fs.readdirSync(dirPath);
|
|
230
|
+
const pythonProjects = subDirs.filter(sub => {
|
|
231
|
+
const subPath = path.join(dirPath, sub);
|
|
232
|
+
return fs.existsSync(path.join(subPath, 'pyproject.toml')) ||
|
|
233
|
+
fs.existsSync(path.join(subPath, 'setup.py'));
|
|
234
|
+
});
|
|
235
|
+
if (pythonProjects.length > 0) {
|
|
236
|
+
foundDirs.push(...pythonProjects.map(p => `${dir}/${p}`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (foundDirs.length > 1) {
|
|
241
|
+
return {
|
|
242
|
+
type: 'monorepo-python',
|
|
243
|
+
confidence: 'medium',
|
|
244
|
+
indicators: foundDirs,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch { }
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Detect multi-repo workspace (multiple separate projects/repos in subdirectories)
|
|
253
|
+
* This is common when a directory contains multiple related but independent projects
|
|
254
|
+
*/
|
|
255
|
+
detectMultiRepoWorkspace(root, exclude) {
|
|
256
|
+
// Skip if there's a root package.json (would be detected as npm/yarn workspace)
|
|
257
|
+
if (fs.existsSync(path.join(root, 'package.json')))
|
|
258
|
+
return null;
|
|
259
|
+
if (fs.existsSync(path.join(root, 'pnpm-workspace.yaml')))
|
|
260
|
+
return null;
|
|
261
|
+
const indicators = [];
|
|
262
|
+
const projectDirs = [];
|
|
263
|
+
// Default exclusions
|
|
264
|
+
const defaultExclusions = ['node_modules', 'dist', 'build', '.git', 'backup', 'backups', 'logs'];
|
|
265
|
+
const exclusions = new Set([...defaultExclusions, ...(exclude || [])]);
|
|
266
|
+
// Look for subdirectories that are independent projects
|
|
267
|
+
try {
|
|
268
|
+
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
269
|
+
for (const entry of entries) {
|
|
270
|
+
if (!entry.isDirectory())
|
|
271
|
+
continue;
|
|
272
|
+
if (entry.name.startsWith('.'))
|
|
273
|
+
continue; // Skip hidden dirs
|
|
274
|
+
if (exclusions.has(entry.name))
|
|
275
|
+
continue; // Skip excluded dirs
|
|
276
|
+
const subPath = path.join(root, entry.name);
|
|
277
|
+
// Check if this subdir is a project (has package.json, go.mod, etc.)
|
|
278
|
+
const isProject = fs.existsSync(path.join(subPath, 'package.json')) ||
|
|
279
|
+
fs.existsSync(path.join(subPath, 'go.mod')) ||
|
|
280
|
+
fs.existsSync(path.join(subPath, 'pyproject.toml')) ||
|
|
281
|
+
fs.existsSync(path.join(subPath, 'Cargo.toml')) ||
|
|
282
|
+
fs.existsSync(path.join(subPath, 'Gemfile'));
|
|
283
|
+
if (isProject) {
|
|
284
|
+
// Check if it's a separate git repo
|
|
285
|
+
const hasOwnGit = fs.existsSync(path.join(subPath, '.git'));
|
|
286
|
+
projectDirs.push(entry.name);
|
|
287
|
+
indicators.push(`${entry.name}/${hasOwnGit ? ' (git repo)' : ''}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
// Need at least 2 project subdirectories to be a workspace
|
|
295
|
+
if (projectDirs.length >= 2) {
|
|
296
|
+
return {
|
|
297
|
+
type: 'hybrid', // Use 'hybrid' for multi-repo workspaces
|
|
298
|
+
confidence: projectDirs.length >= 3 ? 'high' : 'medium',
|
|
299
|
+
indicators: [
|
|
300
|
+
`${projectDirs.length} independent projects`,
|
|
301
|
+
...indicators.slice(0, 8), // Limit indicator list
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
detectMicroservices(root) {
|
|
308
|
+
// Look for docker-compose files
|
|
309
|
+
const composeFiles = this.findComposeFiles(root);
|
|
310
|
+
if (composeFiles.length === 0)
|
|
311
|
+
return null;
|
|
312
|
+
// Parse docker-compose to count services
|
|
313
|
+
const indicators = [];
|
|
314
|
+
let totalServices = 0;
|
|
315
|
+
let buildServices = 0;
|
|
316
|
+
for (const file of composeFiles) {
|
|
317
|
+
try {
|
|
318
|
+
const yaml = require('js-yaml');
|
|
319
|
+
const content = yaml.load(fs.readFileSync(file, 'utf8'));
|
|
320
|
+
if (content && content.services) {
|
|
321
|
+
const services = Object.entries(content.services);
|
|
322
|
+
totalServices += services.length;
|
|
323
|
+
// Count services with build context (actual application code)
|
|
324
|
+
for (const [name, config] of services) {
|
|
325
|
+
const svc = config;
|
|
326
|
+
if (svc.build) {
|
|
327
|
+
buildServices++;
|
|
328
|
+
indicators.push(`${name} (build)`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch { }
|
|
334
|
+
}
|
|
335
|
+
// Consider it microservices if there are multiple buildable services
|
|
336
|
+
if (buildServices >= 2) {
|
|
337
|
+
return {
|
|
338
|
+
type: 'microservices',
|
|
339
|
+
confidence: 'high',
|
|
340
|
+
indicators: [
|
|
341
|
+
`${composeFiles.length} docker-compose file(s)`,
|
|
342
|
+
`${totalServices} total services`,
|
|
343
|
+
`${buildServices} buildable services`,
|
|
344
|
+
...indicators.slice(0, 5), // Limit indicators
|
|
345
|
+
],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
detectSingleApp(root) {
|
|
351
|
+
// Check for common single-app indicators
|
|
352
|
+
const indicators = [];
|
|
353
|
+
// Node.js
|
|
354
|
+
if (fs.existsSync(path.join(root, 'package.json'))) {
|
|
355
|
+
indicators.push('package.json');
|
|
356
|
+
}
|
|
357
|
+
// Python
|
|
358
|
+
if (fs.existsSync(path.join(root, 'pyproject.toml'))) {
|
|
359
|
+
indicators.push('pyproject.toml');
|
|
360
|
+
}
|
|
361
|
+
else if (fs.existsSync(path.join(root, 'requirements.txt'))) {
|
|
362
|
+
indicators.push('requirements.txt');
|
|
363
|
+
}
|
|
364
|
+
// Go
|
|
365
|
+
if (fs.existsSync(path.join(root, 'go.mod'))) {
|
|
366
|
+
indicators.push('go.mod');
|
|
367
|
+
}
|
|
368
|
+
// Ruby
|
|
369
|
+
if (fs.existsSync(path.join(root, 'Gemfile'))) {
|
|
370
|
+
indicators.push('Gemfile');
|
|
371
|
+
}
|
|
372
|
+
// Rust
|
|
373
|
+
if (fs.existsSync(path.join(root, 'Cargo.toml'))) {
|
|
374
|
+
indicators.push('Cargo.toml');
|
|
375
|
+
}
|
|
376
|
+
if (indicators.length > 0) {
|
|
377
|
+
return {
|
|
378
|
+
type: 'single-app',
|
|
379
|
+
confidence: indicators.length >= 2 ? 'high' : 'medium',
|
|
380
|
+
indicators,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
findComposeFiles(root) {
|
|
386
|
+
const files = [];
|
|
387
|
+
const patterns = [
|
|
388
|
+
'docker-compose.yml',
|
|
389
|
+
'docker-compose.yaml',
|
|
390
|
+
'docker-compose.dev.yml',
|
|
391
|
+
'docker-compose.dev.yaml',
|
|
392
|
+
'docker-compose.local.yml',
|
|
393
|
+
'docker-compose.local.yaml',
|
|
394
|
+
'docker-compose.override.yml',
|
|
395
|
+
'docker-compose.override.yaml',
|
|
396
|
+
'docker-compose.prod.yml',
|
|
397
|
+
'docker-compose.prod.yaml',
|
|
398
|
+
'docker-compose.production.yml',
|
|
399
|
+
'docker-compose.production.yaml',
|
|
400
|
+
'compose.yml',
|
|
401
|
+
'compose.yaml',
|
|
402
|
+
];
|
|
403
|
+
for (const pattern of patterns) {
|
|
404
|
+
const filePath = path.join(root, pattern);
|
|
405
|
+
if (fs.existsSync(filePath)) {
|
|
406
|
+
files.push(filePath);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return files;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
exports.StructureDetector = StructureDetector;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genbox Configuration Schema v3
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - Multi-level configs (workspace/project/user)
|
|
7
|
+
* - Flexible app selection (profiles, CLI, interactive)
|
|
8
|
+
* - External environment connections
|
|
9
|
+
* - Database modes (none/local/copy/remote)
|
|
10
|
+
* - Dependency resolution
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genbox",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Genbox CLI - AI-Powered Development Environments",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@inquirer/confirm": "^6.0.2",
|
|
57
|
+
"@inquirer/prompts": "^8.0.2",
|
|
57
58
|
"@inquirer/select": "^5.0.2",
|
|
58
59
|
"chalk": "^5.6.2",
|
|
59
60
|
"commander": "^14.0.2",
|