@kosdev-code/kos-ui-cli 0.1.0-dev.5053
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 +7 -0
- package/package.json +30 -0
- package/src/index.d.ts +2 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -0
- package/src/lib/cli.mjs +803 -0
- package/src/lib/generators/cache/index.mjs +18 -0
- package/src/lib/generators/component/index.mjs +55 -0
- package/src/lib/generators/env/index.mjs +42 -0
- package/src/lib/generators/i18n/namespace.mjs +61 -0
- package/src/lib/generators/kab/index.mjs +82 -0
- package/src/lib/generators/metadata.json +341 -0
- package/src/lib/generators/model/add-future.mjs +96 -0
- package/src/lib/generators/model/companion.mjs +117 -0
- package/src/lib/generators/model/container.mjs +96 -0
- package/src/lib/generators/model/context.mjs +77 -0
- package/src/lib/generators/model/hook.mjs +77 -0
- package/src/lib/generators/model/model.mjs +79 -0
- package/src/lib/generators/plugin/index.mjs +195 -0
- package/src/lib/generators/project/app.mjs +39 -0
- package/src/lib/generators/project/content.mjs +41 -0
- package/src/lib/generators/project/i18n.mjs +38 -0
- package/src/lib/generators/project/plugin.mjs +38 -0
- package/src/lib/generators/project/splash.mjs +39 -0
- package/src/lib/generators/project/theme.mjs +38 -0
- package/src/lib/generators/serve/index.mjs +74 -0
- package/src/lib/generators/version/index.mjs +182 -0
- package/src/lib/generators/workspace/index.mjs +40 -0
- package/src/lib/generators/workspace/list-models.mjs +64 -0
- package/src/lib/generators/workspace/list-projects.mjs +167 -0
- package/src/lib/plopfile.mjs +67 -0
- package/src/lib/routing-plopfile.mjs +53 -0
- package/src/lib/scripts/generate-metadata.mjs +39 -0
- package/src/lib/utils/action-factory.mjs +9 -0
- package/src/lib/utils/cache.mjs +128 -0
- package/src/lib/utils/command-builder.mjs +94 -0
- package/src/lib/utils/exec.mjs +18 -0
- package/src/lib/utils/generator-loader.mjs +65 -0
- package/src/lib/utils/index.mjs +1 -0
- package/src/lib/utils/java-home.mjs +55 -0
- package/src/lib/utils/logger.mjs +0 -0
- package/src/lib/utils/nx-context.mjs +395 -0
- package/src/lib/utils/prompts.mjs +75 -0
- package/src/lib/utils/studio-home.mjs +12 -0
- package/src/lib/utils/utils.mjs +126 -0
- package/src/lib/utils/validators.mjs +10 -0
package/src/lib/cli.mjs
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import figlet from "figlet";
|
|
4
|
+
import minimist from "minimist";
|
|
5
|
+
import nodePlop from "node-plop";
|
|
6
|
+
import path, { dirname } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { clearCache } from "./utils/cache.mjs";
|
|
9
|
+
import { buildNonInteractiveCommand, isRunningInteractively } from "./utils/command-builder.mjs";
|
|
10
|
+
import { getGeneratorMetadata } from "./utils/generator-loader.mjs";
|
|
11
|
+
|
|
12
|
+
const originalArgs = process.argv.slice(2);
|
|
13
|
+
const parsedArgs = minimist(originalArgs);
|
|
14
|
+
const command = parsedArgs._[0] || "help";
|
|
15
|
+
|
|
16
|
+
const customFlags = ["--no-cache", "--refresh", "--quiet"];
|
|
17
|
+
const hasNoCache = originalArgs.includes("--no-cache");
|
|
18
|
+
const hasRefresh = originalArgs.includes("--refresh");
|
|
19
|
+
const hasQuiet = originalArgs.includes("--quiet");
|
|
20
|
+
|
|
21
|
+
if (hasNoCache || hasRefresh) {
|
|
22
|
+
process.env.DISABLE_CACHE = "true";
|
|
23
|
+
if (!hasQuiet) {
|
|
24
|
+
console.log("[kos-cli] Cache disabled or refresh forced. Clearing cache...");
|
|
25
|
+
}
|
|
26
|
+
clearCache();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (hasQuiet) {
|
|
30
|
+
process.env.DISABLE_CLI_BANNER = "true";
|
|
31
|
+
process.env.KOS_CLI_QUIET = "true";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
const configPath = path.join(__dirname, "plopfile.mjs");
|
|
36
|
+
|
|
37
|
+
const showBanner =
|
|
38
|
+
command !== "help" && process.env.DISABLE_CLI_BANNER !== "true";
|
|
39
|
+
|
|
40
|
+
async function launch() {
|
|
41
|
+
// Check for help modes before initializing plop to avoid expensive model parsing
|
|
42
|
+
const shouldShowHelp = parsedArgs.help;
|
|
43
|
+
|
|
44
|
+
if (!command || command === "help") {
|
|
45
|
+
// For general help, we still need plop to list generators
|
|
46
|
+
const plop = await nodePlop(configPath);
|
|
47
|
+
|
|
48
|
+
console.warn("--- KOS CLI Help ---");
|
|
49
|
+
console.log("\nAvailable Generators:");
|
|
50
|
+
plop.getGeneratorList().forEach((g) => {
|
|
51
|
+
console.log(`- ${g.name}: ${g.description}`);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log("\nUsage:");
|
|
55
|
+
console.log(" kosui <generator> [options]");
|
|
56
|
+
console.log(" kosui <generator> --help # Show generator-specific help");
|
|
57
|
+
console.log("\nGlobal Options:");
|
|
58
|
+
console.log(" --no-cache Disable cache");
|
|
59
|
+
console.log(" --refresh Clear cache and refresh");
|
|
60
|
+
console.log(" --quiet Suppress banner and debug output");
|
|
61
|
+
console.log(
|
|
62
|
+
" --interactive, -i Force interactive mode (ignore provided arguments)"
|
|
63
|
+
);
|
|
64
|
+
console.log(" --help Show this help");
|
|
65
|
+
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// For generator-specific help, check if we can show help without full plop initialization
|
|
70
|
+
if (shouldShowHelp) {
|
|
71
|
+
const earlyMeta = await getGeneratorMetadata(command);
|
|
72
|
+
|
|
73
|
+
if (earlyMeta && Object.keys(earlyMeta).length > 0) {
|
|
74
|
+
// Show help immediately for --help flag
|
|
75
|
+
console.log(`--- ${earlyMeta?.name || command} Help ---`);
|
|
76
|
+
console.log(earlyMeta.description || "No description available");
|
|
77
|
+
|
|
78
|
+
if (earlyMeta?.namedArguments) {
|
|
79
|
+
console.log("\nNamed Arguments:");
|
|
80
|
+
Object.entries(earlyMeta.namedArguments).forEach(
|
|
81
|
+
([cliArg, promptName]) => {
|
|
82
|
+
console.log(` --${cliArg} Maps to prompt: ${promptName}`);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log("\nExamples:");
|
|
88
|
+
|
|
89
|
+
// Generate examples dynamically from the generator's named arguments
|
|
90
|
+
if (earlyMeta?.namedArguments) {
|
|
91
|
+
const args = Object.keys(earlyMeta.namedArguments);
|
|
92
|
+
|
|
93
|
+
// Generate example values based on argument names
|
|
94
|
+
const generateExampleValue = (argName) => {
|
|
95
|
+
switch (argName) {
|
|
96
|
+
case "name":
|
|
97
|
+
case "componentName":
|
|
98
|
+
return command.includes("plugin") ? "MyPlugin" : "MyComponent";
|
|
99
|
+
case "modelName":
|
|
100
|
+
return "my-model";
|
|
101
|
+
case "workspaceName":
|
|
102
|
+
return "my-workspace";
|
|
103
|
+
case "project":
|
|
104
|
+
case "componentProject":
|
|
105
|
+
case "modelProject":
|
|
106
|
+
return "my-ui-lib";
|
|
107
|
+
case "registrationProject":
|
|
108
|
+
return "my-lib";
|
|
109
|
+
case "companionParent":
|
|
110
|
+
return "parent-model";
|
|
111
|
+
case "extensionPoint":
|
|
112
|
+
return "utility";
|
|
113
|
+
case "group":
|
|
114
|
+
return "appearance";
|
|
115
|
+
case "locale":
|
|
116
|
+
return "en";
|
|
117
|
+
case "container":
|
|
118
|
+
case "singleton":
|
|
119
|
+
case "parentAware":
|
|
120
|
+
case "dataServices":
|
|
121
|
+
return true;
|
|
122
|
+
case "dryRun":
|
|
123
|
+
return true;
|
|
124
|
+
default:
|
|
125
|
+
return `my-${argName}`;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Create basic example with most important arguments
|
|
130
|
+
const basicArgs = [];
|
|
131
|
+
const booleanArgs = [
|
|
132
|
+
"container",
|
|
133
|
+
"singleton",
|
|
134
|
+
"parentAware",
|
|
135
|
+
"dataServices",
|
|
136
|
+
"dryRun",
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
// Pick the most relevant arguments for each generator type
|
|
140
|
+
const getRelevantArgs = (args, command) => {
|
|
141
|
+
const relevantArgs = [];
|
|
142
|
+
|
|
143
|
+
// Always prefer name-type arguments first
|
|
144
|
+
const nameArgs = [
|
|
145
|
+
"name",
|
|
146
|
+
"componentName",
|
|
147
|
+
"modelName",
|
|
148
|
+
"workspaceName",
|
|
149
|
+
].filter((arg) => args.includes(arg));
|
|
150
|
+
if (nameArgs.length > 0) {
|
|
151
|
+
relevantArgs.push(nameArgs[0]); // Take the first name argument
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Then add project-type arguments
|
|
155
|
+
const projectArgs = [
|
|
156
|
+
"project",
|
|
157
|
+
"componentProject",
|
|
158
|
+
"modelProject",
|
|
159
|
+
"registrationProject",
|
|
160
|
+
].filter((arg) => args.includes(arg));
|
|
161
|
+
if (projectArgs.length > 0) {
|
|
162
|
+
relevantArgs.push(projectArgs[0]); // Take the first project argument
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add other specific arguments
|
|
166
|
+
const otherArgs = args.filter(
|
|
167
|
+
(arg) =>
|
|
168
|
+
!nameArgs.includes(arg) &&
|
|
169
|
+
!projectArgs.includes(arg) &&
|
|
170
|
+
!booleanArgs.includes(arg) &&
|
|
171
|
+
arg !== "interactive"
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Add up to 2 more relevant arguments
|
|
175
|
+
relevantArgs.push(...otherArgs.slice(0, 2));
|
|
176
|
+
|
|
177
|
+
return relevantArgs;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const relevantArgs = getRelevantArgs(args, command);
|
|
181
|
+
|
|
182
|
+
relevantArgs.forEach((argName) => {
|
|
183
|
+
const value = generateExampleValue(argName);
|
|
184
|
+
basicArgs.push(`--${argName} ${value}`);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Create advanced example with boolean flags
|
|
188
|
+
const advancedBooleanArgs = [];
|
|
189
|
+
booleanArgs.forEach((argName) => {
|
|
190
|
+
if (args.includes(argName)) {
|
|
191
|
+
advancedBooleanArgs.push(`--${argName}`);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Show basic example
|
|
196
|
+
if (basicArgs.length > 0) {
|
|
197
|
+
console.log(` kosui ${command} ${basicArgs.join(" ")}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Show advanced example with boolean flags if any exist
|
|
201
|
+
if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
|
|
202
|
+
console.log(
|
|
203
|
+
` kosui ${command} ${basicArgs
|
|
204
|
+
.slice(0, 2)
|
|
205
|
+
.join(" ")} ${advancedBooleanArgs.join(" ")}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// If no basic args, show a minimal example
|
|
210
|
+
if (basicArgs.length === 0) {
|
|
211
|
+
console.log(` kosui ${command} [options]`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log(` kosui ${command} --interactive # Force interactive mode`);
|
|
216
|
+
console.log(
|
|
217
|
+
` kosui ${command} -i # Force interactive mode (short form)`
|
|
218
|
+
);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Also check for no-arguments case (when only global flags like --no-cache are present)
|
|
224
|
+
const earlyMeta = await getGeneratorMetadata(command);
|
|
225
|
+
if (earlyMeta?.namedArguments) {
|
|
226
|
+
const namedArgumentsMap = earlyMeta.namedArguments;
|
|
227
|
+
const providedAnswers = {};
|
|
228
|
+
Object.entries(namedArgumentsMap).forEach(([cliArg, promptName]) => {
|
|
229
|
+
if (cliArg === "interactive") return;
|
|
230
|
+
if (parsedArgs[cliArg] !== undefined) {
|
|
231
|
+
let value = parsedArgs[cliArg];
|
|
232
|
+
if (value === "true" || value === "y" || value === "yes") {
|
|
233
|
+
value = true;
|
|
234
|
+
} else if (value === "false" || value === "n" || value === "no") {
|
|
235
|
+
value = false;
|
|
236
|
+
}
|
|
237
|
+
providedAnswers[promptName] = value;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
const hasAnyAnswers = Object.keys(providedAnswers).length > 0;
|
|
241
|
+
const forceInteractive = parsedArgs.interactive || parsedArgs.i;
|
|
242
|
+
|
|
243
|
+
// Show help for no-arguments case
|
|
244
|
+
if (!hasAnyAnswers && !forceInteractive) {
|
|
245
|
+
console.log(`--- ${earlyMeta?.name || command} Help ---`);
|
|
246
|
+
console.log(earlyMeta.description || "No description available");
|
|
247
|
+
|
|
248
|
+
if (earlyMeta?.namedArguments) {
|
|
249
|
+
console.log("\nNamed Arguments:");
|
|
250
|
+
Object.entries(earlyMeta.namedArguments).forEach(
|
|
251
|
+
([cliArg, promptName]) => {
|
|
252
|
+
console.log(` --${cliArg} Maps to prompt: ${promptName}`);
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log("\nExamples:");
|
|
258
|
+
|
|
259
|
+
// Generate examples dynamically from the generator's named arguments
|
|
260
|
+
if (earlyMeta?.namedArguments) {
|
|
261
|
+
const args = Object.keys(earlyMeta.namedArguments);
|
|
262
|
+
|
|
263
|
+
// Generate example values based on argument names
|
|
264
|
+
const generateExampleValue = (argName) => {
|
|
265
|
+
switch (argName) {
|
|
266
|
+
case "name":
|
|
267
|
+
case "componentName":
|
|
268
|
+
return command.includes("plugin") ? "MyPlugin" : "MyComponent";
|
|
269
|
+
case "modelName":
|
|
270
|
+
return "my-model";
|
|
271
|
+
case "workspaceName":
|
|
272
|
+
return "my-workspace";
|
|
273
|
+
case "project":
|
|
274
|
+
case "componentProject":
|
|
275
|
+
case "modelProject":
|
|
276
|
+
return "my-ui-lib";
|
|
277
|
+
case "registrationProject":
|
|
278
|
+
return "my-lib";
|
|
279
|
+
case "companionParent":
|
|
280
|
+
return "parent-model";
|
|
281
|
+
case "extensionPoint":
|
|
282
|
+
return "utility";
|
|
283
|
+
case "group":
|
|
284
|
+
return "appearance";
|
|
285
|
+
case "locale":
|
|
286
|
+
return "en";
|
|
287
|
+
case "container":
|
|
288
|
+
case "singleton":
|
|
289
|
+
case "parentAware":
|
|
290
|
+
case "dataServices":
|
|
291
|
+
return true;
|
|
292
|
+
case "dryRun":
|
|
293
|
+
return true;
|
|
294
|
+
default:
|
|
295
|
+
return `my-${argName}`;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Create basic example with most important arguments
|
|
300
|
+
const basicArgs = [];
|
|
301
|
+
const booleanArgs = [
|
|
302
|
+
"container",
|
|
303
|
+
"singleton",
|
|
304
|
+
"parentAware",
|
|
305
|
+
"dataServices",
|
|
306
|
+
"dryRun",
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
// Pick the most relevant arguments for each generator type
|
|
310
|
+
const getRelevantArgs = (args, command) => {
|
|
311
|
+
const relevantArgs = [];
|
|
312
|
+
|
|
313
|
+
// Always prefer name-type arguments first
|
|
314
|
+
const nameArgs = [
|
|
315
|
+
"name",
|
|
316
|
+
"componentName",
|
|
317
|
+
"modelName",
|
|
318
|
+
"workspaceName",
|
|
319
|
+
].filter((arg) => args.includes(arg));
|
|
320
|
+
if (nameArgs.length > 0) {
|
|
321
|
+
relevantArgs.push(nameArgs[0]); // Take the first name argument
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Then add project-type arguments
|
|
325
|
+
const projectArgs = [
|
|
326
|
+
"project",
|
|
327
|
+
"componentProject",
|
|
328
|
+
"modelProject",
|
|
329
|
+
"registrationProject",
|
|
330
|
+
].filter((arg) => args.includes(arg));
|
|
331
|
+
if (projectArgs.length > 0) {
|
|
332
|
+
relevantArgs.push(projectArgs[0]); // Take the first project argument
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Add other specific arguments
|
|
336
|
+
const otherArgs = args.filter(
|
|
337
|
+
(arg) =>
|
|
338
|
+
!nameArgs.includes(arg) &&
|
|
339
|
+
!projectArgs.includes(arg) &&
|
|
340
|
+
!booleanArgs.includes(arg) &&
|
|
341
|
+
arg !== "interactive"
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
// Add up to 2 more relevant arguments
|
|
345
|
+
relevantArgs.push(...otherArgs.slice(0, 2));
|
|
346
|
+
|
|
347
|
+
return relevantArgs;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const relevantArgs = getRelevantArgs(args, command);
|
|
351
|
+
|
|
352
|
+
relevantArgs.forEach((argName) => {
|
|
353
|
+
const value = generateExampleValue(argName);
|
|
354
|
+
basicArgs.push(`--${argName} ${value}`);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Create advanced example with boolean flags
|
|
358
|
+
const advancedBooleanArgs = [];
|
|
359
|
+
booleanArgs.forEach((argName) => {
|
|
360
|
+
if (args.includes(argName)) {
|
|
361
|
+
advancedBooleanArgs.push(`--${argName}`);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Show basic example
|
|
366
|
+
if (basicArgs.length > 0) {
|
|
367
|
+
console.log(` kosui ${command} ${basicArgs.join(" ")}`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Show advanced example with boolean flags if any exist
|
|
371
|
+
if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
|
|
372
|
+
console.log(
|
|
373
|
+
` kosui ${command} ${basicArgs
|
|
374
|
+
.slice(0, 2)
|
|
375
|
+
.join(" ")} ${advancedBooleanArgs.join(" ")}`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// If no basic args, show a minimal example
|
|
380
|
+
if (basicArgs.length === 0) {
|
|
381
|
+
console.log(` kosui ${command} [options]`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log(` kosui ${command} --interactive # Force interactive mode`);
|
|
386
|
+
console.log(
|
|
387
|
+
` kosui ${command} -i # Force interactive mode (short form)`
|
|
388
|
+
);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const plop = await nodePlop(configPath);
|
|
394
|
+
|
|
395
|
+
plop.setActionType("clearCache", (answers, config, plop) => {
|
|
396
|
+
clearCache();
|
|
397
|
+
return "[ok] CLI cache cleared successfully.";
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const generator = plop.getGenerator(command);
|
|
401
|
+
|
|
402
|
+
if (!generator) {
|
|
403
|
+
console.error(`[kos-cli] Generator "${command}" not found.`);
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const meta = await getGeneratorMetadata(command);
|
|
408
|
+
|
|
409
|
+
if (showBanner && !hasQuiet) {
|
|
410
|
+
console.log(`--- Running KOS Generator: ${meta?.name || command} ---`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
// Extract positional arguments for prompt bypass
|
|
415
|
+
const positionalArgs = parsedArgs._.slice(1); // Remove command from args
|
|
416
|
+
|
|
417
|
+
// Get named arguments mapping from generator metadata
|
|
418
|
+
const namedArgumentsMap = meta?.namedArguments || {};
|
|
419
|
+
|
|
420
|
+
// Convert CLI args to answer format using generator's mapping
|
|
421
|
+
const providedAnswers = {};
|
|
422
|
+
|
|
423
|
+
// Ensure we have the metadata with named arguments
|
|
424
|
+
if (!namedArgumentsMap || Object.keys(namedArgumentsMap).length === 0) {
|
|
425
|
+
console.warn(
|
|
426
|
+
`[kos-cli] Warning: No named arguments mapping found for generator '${command}'`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
Object.entries(namedArgumentsMap).forEach(([cliArg, promptName]) => {
|
|
431
|
+
// Skip 'interactive' - it's handled as a special CLI flag, not a prompt argument
|
|
432
|
+
if (cliArg === "interactive") {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (parsedArgs[cliArg] !== undefined) {
|
|
437
|
+
let value = parsedArgs[cliArg];
|
|
438
|
+
|
|
439
|
+
// Convert string boolean values to actual booleans
|
|
440
|
+
if (value === "true" || value === "y" || value === "yes") {
|
|
441
|
+
value = true;
|
|
442
|
+
} else if (value === "false" || value === "n" || value === "no") {
|
|
443
|
+
value = false;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
providedAnswers[promptName] = value;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Also handle positional arguments
|
|
451
|
+
const prompts = generator.prompts || [];
|
|
452
|
+
positionalArgs.forEach((arg, index) => {
|
|
453
|
+
if (prompts[index] && !prompts[index].when) {
|
|
454
|
+
// Only non-conditional prompts
|
|
455
|
+
const promptName = prompts[index].name;
|
|
456
|
+
if (providedAnswers[promptName] === undefined) {
|
|
457
|
+
let value = arg;
|
|
458
|
+
|
|
459
|
+
// Convert string boolean values to actual booleans
|
|
460
|
+
if (value === "true" || value === "y" || value === "yes") {
|
|
461
|
+
value = true;
|
|
462
|
+
} else if (value === "false" || value === "n" || value === "no") {
|
|
463
|
+
value = false;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
providedAnswers[promptName] = value;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Check if we have ANY provided answers
|
|
472
|
+
const hasAnyAnswers = Object.keys(providedAnswers).length > 0;
|
|
473
|
+
|
|
474
|
+
let answers;
|
|
475
|
+
|
|
476
|
+
// Check if user wants to force interactive mode
|
|
477
|
+
const forceInteractive = parsedArgs.interactive || parsedArgs.i;
|
|
478
|
+
|
|
479
|
+
// If no arguments provided and no interactive flag, show help
|
|
480
|
+
if (!hasAnyAnswers && !forceInteractive) {
|
|
481
|
+
console.log(`--- ${meta?.name || command} Help ---`);
|
|
482
|
+
console.log(generator.description || "No description available");
|
|
483
|
+
|
|
484
|
+
if (meta?.namedArguments) {
|
|
485
|
+
console.log("\nNamed Arguments:");
|
|
486
|
+
Object.entries(meta.namedArguments).forEach(([cliArg, promptName]) => {
|
|
487
|
+
console.log(` --${cliArg} Maps to prompt: ${promptName}`);
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
console.log("\nExamples:");
|
|
492
|
+
|
|
493
|
+
// Generate examples dynamically from the generator's named arguments
|
|
494
|
+
if (meta?.namedArguments) {
|
|
495
|
+
const args = Object.keys(meta.namedArguments);
|
|
496
|
+
|
|
497
|
+
// Generate example values based on argument names
|
|
498
|
+
const generateExampleValue = (argName) => {
|
|
499
|
+
switch (argName) {
|
|
500
|
+
case "name":
|
|
501
|
+
case "componentName":
|
|
502
|
+
return command.includes("plugin") ? "MyPlugin" : "MyComponent";
|
|
503
|
+
case "modelName":
|
|
504
|
+
return "my-model";
|
|
505
|
+
case "workspaceName":
|
|
506
|
+
return "my-workspace";
|
|
507
|
+
case "project":
|
|
508
|
+
case "componentProject":
|
|
509
|
+
case "modelProject":
|
|
510
|
+
return "my-ui-lib";
|
|
511
|
+
case "registrationProject":
|
|
512
|
+
return "my-lib";
|
|
513
|
+
case "companionParent":
|
|
514
|
+
return "parent-model";
|
|
515
|
+
case "extensionPoint":
|
|
516
|
+
return "utility";
|
|
517
|
+
case "group":
|
|
518
|
+
return "appearance";
|
|
519
|
+
case "locale":
|
|
520
|
+
return "en";
|
|
521
|
+
case "container":
|
|
522
|
+
case "singleton":
|
|
523
|
+
case "parentAware":
|
|
524
|
+
case "dataServices":
|
|
525
|
+
return true;
|
|
526
|
+
case "dryRun":
|
|
527
|
+
return true;
|
|
528
|
+
default:
|
|
529
|
+
return `my-${argName}`;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// Create basic example with most important arguments
|
|
534
|
+
const basicArgs = [];
|
|
535
|
+
const booleanArgs = [
|
|
536
|
+
"container",
|
|
537
|
+
"singleton",
|
|
538
|
+
"parentAware",
|
|
539
|
+
"dataServices",
|
|
540
|
+
"dryRun",
|
|
541
|
+
];
|
|
542
|
+
|
|
543
|
+
// Pick the most relevant arguments for each generator type
|
|
544
|
+
const getRelevantArgs = (args, command) => {
|
|
545
|
+
const relevantArgs = [];
|
|
546
|
+
|
|
547
|
+
// Always prefer name-type arguments first
|
|
548
|
+
const nameArgs = [
|
|
549
|
+
"name",
|
|
550
|
+
"componentName",
|
|
551
|
+
"modelName",
|
|
552
|
+
"workspaceName",
|
|
553
|
+
].filter((arg) => args.includes(arg));
|
|
554
|
+
if (nameArgs.length > 0) {
|
|
555
|
+
relevantArgs.push(nameArgs[0]); // Take the first name argument
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Then add project-type arguments
|
|
559
|
+
const projectArgs = [
|
|
560
|
+
"project",
|
|
561
|
+
"componentProject",
|
|
562
|
+
"modelProject",
|
|
563
|
+
"registrationProject",
|
|
564
|
+
].filter((arg) => args.includes(arg));
|
|
565
|
+
if (projectArgs.length > 0) {
|
|
566
|
+
relevantArgs.push(projectArgs[0]); // Take the first project argument
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Add other specific arguments
|
|
570
|
+
const otherArgs = args.filter(
|
|
571
|
+
(arg) =>
|
|
572
|
+
!nameArgs.includes(arg) &&
|
|
573
|
+
!projectArgs.includes(arg) &&
|
|
574
|
+
!booleanArgs.includes(arg) &&
|
|
575
|
+
arg !== "interactive"
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
// Add up to 2 more relevant arguments
|
|
579
|
+
relevantArgs.push(...otherArgs.slice(0, 2));
|
|
580
|
+
|
|
581
|
+
return relevantArgs;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
const relevantArgs = getRelevantArgs(args, command);
|
|
585
|
+
|
|
586
|
+
relevantArgs.forEach((argName) => {
|
|
587
|
+
const value = generateExampleValue(argName);
|
|
588
|
+
basicArgs.push(`--${argName} ${value}`);
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// Create advanced example with boolean flags
|
|
592
|
+
const advancedBooleanArgs = [];
|
|
593
|
+
booleanArgs.forEach((argName) => {
|
|
594
|
+
if (args.includes(argName)) {
|
|
595
|
+
advancedBooleanArgs.push(`--${argName}`);
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// Show basic example
|
|
600
|
+
if (basicArgs.length > 0) {
|
|
601
|
+
console.log(` kosui ${command} ${basicArgs.join(" ")}`);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Show advanced example with boolean flags if any exist
|
|
605
|
+
if (basicArgs.length > 0 && advancedBooleanArgs.length > 0) {
|
|
606
|
+
console.log(
|
|
607
|
+
` kosui ${command} ${basicArgs
|
|
608
|
+
.slice(0, 2)
|
|
609
|
+
.join(" ")} ${advancedBooleanArgs.join(" ")}`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// If no basic args, show a minimal example
|
|
614
|
+
if (basicArgs.length === 0) {
|
|
615
|
+
console.log(` kosui ${command} [options]`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
console.log(` kosui ${command} --interactive # Force interactive mode`);
|
|
620
|
+
console.log(
|
|
621
|
+
` kosui ${command} -i # Force interactive mode (short form)`
|
|
622
|
+
);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (hasAnyAnswers && !forceInteractive) {
|
|
627
|
+
// Create bypass array for all prompts
|
|
628
|
+
const bypassArgs = [];
|
|
629
|
+
|
|
630
|
+
// Fill bypass array with provided values or defaults
|
|
631
|
+
for (const prompt of prompts) {
|
|
632
|
+
if (prompt.when) {
|
|
633
|
+
// Skip conditional prompts - they can't be bypassed
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const providedValue = providedAnswers[prompt.name];
|
|
638
|
+
if (providedValue !== undefined) {
|
|
639
|
+
bypassArgs.push(providedValue);
|
|
640
|
+
} else {
|
|
641
|
+
// Check for workspace defaults for project-type prompts
|
|
642
|
+
let defaultValue = prompt.default;
|
|
643
|
+
|
|
644
|
+
// Use workspace defaults for project selection prompts
|
|
645
|
+
if (
|
|
646
|
+
prompt.name &&
|
|
647
|
+
(prompt.name.includes("Project") || prompt.name === "project")
|
|
648
|
+
) {
|
|
649
|
+
const { getDefaultProjectForType } = await import(
|
|
650
|
+
"./utils/nx-context.mjs"
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
// Determine project type based on command
|
|
654
|
+
let projectType = null;
|
|
655
|
+
if (command === "model" || command.startsWith("model:")) {
|
|
656
|
+
projectType = "model";
|
|
657
|
+
} else if (command === "component") {
|
|
658
|
+
projectType = "model-component";
|
|
659
|
+
} else if (command === "i18n:namespace") {
|
|
660
|
+
projectType = "i18n";
|
|
661
|
+
} else if (command.startsWith("plugin")) {
|
|
662
|
+
projectType = "plugin";
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (projectType) {
|
|
666
|
+
const workspaceDefault = await getDefaultProjectForType(
|
|
667
|
+
projectType
|
|
668
|
+
);
|
|
669
|
+
if (workspaceDefault) {
|
|
670
|
+
defaultValue = workspaceDefault;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// For required project prompts without a default value, we can't bypass - need to show help
|
|
676
|
+
if (
|
|
677
|
+
defaultValue === undefined &&
|
|
678
|
+
(prompt.name === "modelProject" ||
|
|
679
|
+
prompt.name === "project" ||
|
|
680
|
+
prompt.name === "componentProject" ||
|
|
681
|
+
prompt.name === "registrationProject")
|
|
682
|
+
) {
|
|
683
|
+
// Can't bypass required project prompts without a value - show help instead
|
|
684
|
+
console.log(
|
|
685
|
+
`\nError: Missing required argument '--project' (no default project configured)`
|
|
686
|
+
);
|
|
687
|
+
console.log(`\n--- ${meta?.name || command} Help ---`);
|
|
688
|
+
console.log(meta.description || "No description available");
|
|
689
|
+
|
|
690
|
+
if (meta?.namedArguments) {
|
|
691
|
+
console.log("\nNamed Arguments:");
|
|
692
|
+
Object.entries(meta.namedArguments).forEach(
|
|
693
|
+
([cliArg, promptName]) => {
|
|
694
|
+
console.log(` --${cliArg} Maps to prompt: ${promptName}`);
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
console.log("\nTo fix this, you can:");
|
|
700
|
+
console.log(
|
|
701
|
+
"1. Specify a project: kosui " +
|
|
702
|
+
command +
|
|
703
|
+
" --name MyModel --project my-model-lib"
|
|
704
|
+
);
|
|
705
|
+
console.log(
|
|
706
|
+
"2. Configure a default in .kos.json at the workspace root:"
|
|
707
|
+
);
|
|
708
|
+
console.log(
|
|
709
|
+
' { "type": "root", "generator": { "defaults": { "model": "my-default-project" } } }'
|
|
710
|
+
);
|
|
711
|
+
console.log(
|
|
712
|
+
"3. Run in interactive mode: kosui " + command + " --interactive"
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Use default value or skip with "_"
|
|
719
|
+
bypassArgs.push(defaultValue !== undefined ? defaultValue : "_");
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Only bypass if we have all non-conditional prompts
|
|
724
|
+
const nonConditionalPrompts = prompts.filter((p) => !p.when);
|
|
725
|
+
// Debug: Check if we're trying to bypass with "_" for required fields
|
|
726
|
+
const hasInvalidBypass = bypassArgs.includes("_");
|
|
727
|
+
if (hasInvalidBypass) {
|
|
728
|
+
console.log(
|
|
729
|
+
"[kos-cli] Cannot bypass prompts - missing required values"
|
|
730
|
+
);
|
|
731
|
+
// Fall back to interactive mode
|
|
732
|
+
console.log(
|
|
733
|
+
"Note: Running interactive mode. Your provided arguments will be applied automatically."
|
|
734
|
+
);
|
|
735
|
+
answers = await generator.runPrompts();
|
|
736
|
+
Object.assign(answers, providedAnswers);
|
|
737
|
+
} else if (bypassArgs.length === nonConditionalPrompts.length) {
|
|
738
|
+
// We can bypass all non-conditional prompts
|
|
739
|
+
answers = await generator.runPrompts(bypassArgs);
|
|
740
|
+
|
|
741
|
+
// Add any conditional prompt values that were provided
|
|
742
|
+
Object.assign(answers, providedAnswers);
|
|
743
|
+
} else {
|
|
744
|
+
// Fall back to interactive mode
|
|
745
|
+
console.log(
|
|
746
|
+
"Note: Running interactive mode. Your provided arguments will be applied automatically."
|
|
747
|
+
);
|
|
748
|
+
answers = await generator.runPrompts();
|
|
749
|
+
Object.assign(answers, providedAnswers);
|
|
750
|
+
}
|
|
751
|
+
} else {
|
|
752
|
+
// Run prompts interactively
|
|
753
|
+
if (forceInteractive) {
|
|
754
|
+
console.log(
|
|
755
|
+
"Note: Running in interactive mode. All prompts will be shown."
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
// In interactive mode, ignore provided arguments and run all prompts
|
|
759
|
+
answers = await generator.runPrompts();
|
|
760
|
+
} else {
|
|
761
|
+
answers = await generator.runPrompts();
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const results = await generator.runActions(answers);
|
|
766
|
+
|
|
767
|
+
if (!hasQuiet) {
|
|
768
|
+
console.log("[kos-cli] Generator completed successfully!");
|
|
769
|
+
results.changes.forEach((result) => {
|
|
770
|
+
console.log(result.path || result.message);
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
results.failures.forEach((fail) => {
|
|
775
|
+
console.error(fail.error || fail.message);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
// Output equivalent non-interactive command when running interactively
|
|
779
|
+
if (isRunningInteractively(parsedArgs, hasAnyAnswers) && results.failures.length === 0 && !hasQuiet) {
|
|
780
|
+
const commandString = buildNonInteractiveCommand(command, answers, meta);
|
|
781
|
+
if (commandString) {
|
|
782
|
+
console.log(chalk.cyan("\n[kos-cli] Equivalent non-interactive command:"));
|
|
783
|
+
console.log(chalk.green(` ${commandString}`));
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} catch (err) {
|
|
787
|
+
console.error("[kos-cli] Generator run failed:", err.message);
|
|
788
|
+
process.exit(1);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (showBanner) {
|
|
793
|
+
figlet("KOS CLI", function (err, data) {
|
|
794
|
+
if (err) {
|
|
795
|
+
console.error("Figlet failed:", err);
|
|
796
|
+
return launch(); // even if figlet fails, run!
|
|
797
|
+
}
|
|
798
|
+
console.log(data);
|
|
799
|
+
launch();
|
|
800
|
+
});
|
|
801
|
+
} else {
|
|
802
|
+
launch();
|
|
803
|
+
}
|