@jskit-ai/jskit-cli 0.2.30 → 0.2.32
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/package.json +3 -3
- package/src/server/cliRuntime/mutationWhen.js +25 -7
- package/src/server/commandHandlers/health.js +140 -0
- package/src/server/commandHandlers/list.js +268 -1
- package/src/server/commandHandlers/packageCommands/add.js +113 -0
- package/src/server/commandHandlers/packageCommands/discoverabilityHelp.js +649 -0
- package/src/server/commandHandlers/packageCommands/generate.js +151 -10
- package/src/server/commandHandlers/packageCommands/tabLinkItemProvisioning.js +326 -0
- package/src/server/core/commandCatalog.js +4 -1
- package/src/server/core/createCommandHandlers.js +2 -1
- package/src/server/core/dispatchCli.js +3 -0
- package/src/server/core/usageHelp.js +32 -5
- package/src/server/shared/optionInterpolation.js +6 -0
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureArray,
|
|
3
|
+
ensureObject,
|
|
4
|
+
sortStrings
|
|
5
|
+
} from "../../shared/collectionUtils.js";
|
|
6
|
+
|
|
7
|
+
const JSKIT_SCOPE_PREFIX = "@jskit-ai/";
|
|
8
|
+
|
|
9
|
+
function isHelpToken(value = "") {
|
|
10
|
+
return String(value || "").trim().toLowerCase() === "help";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function toShortPackageId(packageId = "") {
|
|
14
|
+
const normalized = String(packageId || "").trim();
|
|
15
|
+
if (!normalized.startsWith(JSKIT_SCOPE_PREFIX)) {
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
18
|
+
return normalized.slice(JSKIT_SCOPE_PREFIX.length);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resolvePackageSummary(entry = {}) {
|
|
22
|
+
const descriptor = ensureObject(entry?.descriptor);
|
|
23
|
+
return String(descriptor.description || "").trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildPackageOptionRows(packageEntry = {}) {
|
|
27
|
+
const descriptor = ensureObject(packageEntry?.descriptor);
|
|
28
|
+
const optionSchemas = ensureObject(descriptor.options);
|
|
29
|
+
const rows = [];
|
|
30
|
+
|
|
31
|
+
for (const optionName of sortStrings(Object.keys(optionSchemas))) {
|
|
32
|
+
const schema = ensureObject(optionSchemas[optionName]);
|
|
33
|
+
rows.push(Object.freeze({
|
|
34
|
+
name: optionName,
|
|
35
|
+
required: schema.required === true,
|
|
36
|
+
inputType: String(schema.inputType || "text").trim() || "text",
|
|
37
|
+
defaultValue: String(schema.defaultValue || "").trim(),
|
|
38
|
+
allowEmpty: schema.allowEmpty === true,
|
|
39
|
+
promptLabel: String(schema.promptLabel || "").trim(),
|
|
40
|
+
promptHint: String(schema.promptHint || "").trim()
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
rows.sort((left, right) => {
|
|
45
|
+
if (left.required !== right.required) {
|
|
46
|
+
return left.required ? -1 : 1;
|
|
47
|
+
}
|
|
48
|
+
return String(left.name || "").localeCompare(String(right.name || ""));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return rows;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeSubcommandPositionalArgRows(rawRows = []) {
|
|
55
|
+
const rows = [];
|
|
56
|
+
for (const rawRow of ensureArray(rawRows)) {
|
|
57
|
+
if (typeof rawRow === "string") {
|
|
58
|
+
const name = String(rawRow || "").trim();
|
|
59
|
+
if (!name) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
rows.push(Object.freeze({
|
|
63
|
+
name,
|
|
64
|
+
required: true,
|
|
65
|
+
description: ""
|
|
66
|
+
}));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const row = ensureObject(rawRow);
|
|
71
|
+
const name = String(row.name || "").trim();
|
|
72
|
+
if (!name) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
rows.push(Object.freeze({
|
|
76
|
+
name,
|
|
77
|
+
required: row.required !== false,
|
|
78
|
+
description: String(row.description || "").trim()
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
return rows;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeSubcommandOptionNames(rawOptionNames = []) {
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
const rows = [];
|
|
87
|
+
for (const rawOptionName of ensureArray(rawOptionNames)) {
|
|
88
|
+
const optionName = String(rawOptionName || "").trim();
|
|
89
|
+
if (!optionName || seen.has(optionName)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
seen.add(optionName);
|
|
93
|
+
rows.push(optionName);
|
|
94
|
+
}
|
|
95
|
+
return rows;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function resolveGeneratorSubcommandMetadata(packageEntry = {}) {
|
|
99
|
+
const descriptor = ensureObject(packageEntry?.descriptor);
|
|
100
|
+
const metadata = ensureObject(descriptor.metadata);
|
|
101
|
+
const subcommands = ensureObject(metadata.generatorSubcommands || descriptor.generatorSubcommands);
|
|
102
|
+
const primarySubcommand = String(metadata.generatorPrimarySubcommand || descriptor.generatorPrimarySubcommand || "")
|
|
103
|
+
.trim()
|
|
104
|
+
.toLowerCase();
|
|
105
|
+
const subcommandNames = new Set(sortStrings(Object.keys(subcommands)));
|
|
106
|
+
if (primarySubcommand) {
|
|
107
|
+
subcommandNames.add(primarySubcommand);
|
|
108
|
+
}
|
|
109
|
+
const rows = [];
|
|
110
|
+
|
|
111
|
+
for (const subcommandName of sortStrings([...subcommandNames])) {
|
|
112
|
+
const definition = ensureObject(subcommands[subcommandName]);
|
|
113
|
+
const entrypoint = String(definition.entrypoint || "").trim();
|
|
114
|
+
const exportName = String(definition.export || "runGeneratorSubcommand").trim() || "runGeneratorSubcommand";
|
|
115
|
+
const name = String(subcommandName || "").trim();
|
|
116
|
+
if (!name) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const optionNames = normalizeSubcommandOptionNames(definition.optionNames);
|
|
120
|
+
const requiredOptionNames = normalizeSubcommandOptionNames(definition.requiredOptionNames);
|
|
121
|
+
rows.push(Object.freeze({
|
|
122
|
+
name,
|
|
123
|
+
entrypoint,
|
|
124
|
+
exportName,
|
|
125
|
+
primary: primarySubcommand === name.toLowerCase(),
|
|
126
|
+
description: String(definition.description || "").trim(),
|
|
127
|
+
positionalArgs: normalizeSubcommandPositionalArgRows(definition.positionalArgs),
|
|
128
|
+
optionNames,
|
|
129
|
+
requiredOptionNames
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
primarySubcommand,
|
|
135
|
+
subcommands: rows
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function formatOptionSummary(optionRow = {}) {
|
|
140
|
+
const status = optionRow.required ? "required" : "optional";
|
|
141
|
+
const hasDefaultValue = String(optionRow.defaultValue || "").length > 0;
|
|
142
|
+
const optionalDefaultSuffix = optionRow.required
|
|
143
|
+
? ""
|
|
144
|
+
: `; default: ${hasDefaultValue ? optionRow.defaultValue : "<empty>"}`;
|
|
145
|
+
const defaultSuffix = optionRow.required && hasDefaultValue
|
|
146
|
+
? `; default: ${optionRow.defaultValue}`
|
|
147
|
+
: optionalDefaultSuffix;
|
|
148
|
+
const allowEmptySuffix = optionRow.allowEmpty ? "; allow-empty" : "";
|
|
149
|
+
const labelParts = [optionRow.promptLabel, optionRow.promptHint].filter(Boolean);
|
|
150
|
+
const label = labelParts.join(". ");
|
|
151
|
+
const typeSuffix = optionRow.inputType ? `<${optionRow.inputType}> ` : "";
|
|
152
|
+
const baseDescription = label || "No description provided.";
|
|
153
|
+
const description = optionRow.name === "placement-component-token"
|
|
154
|
+
? `${baseDescription} Use jskit list-link-items to discover link-item tokens.`
|
|
155
|
+
: baseDescription;
|
|
156
|
+
return `- --${optionRow.name} ${typeSuffix}[${status}${defaultSuffix}${allowEmptySuffix}]: ${description}`.trim();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function formatPositionalArgUsageToken(arg = {}) {
|
|
160
|
+
const name = String(arg.name || "").trim();
|
|
161
|
+
if (!name) {
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
if (arg.required === false) {
|
|
165
|
+
return `[${name}]`;
|
|
166
|
+
}
|
|
167
|
+
return name;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function formatPositionalArgSummary(arg = {}) {
|
|
171
|
+
const name = String(arg.name || "").trim();
|
|
172
|
+
if (!name) {
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
175
|
+
const status = arg.required === false ? "optional" : "required";
|
|
176
|
+
const description = String(arg.description || "").trim() || "No description provided.";
|
|
177
|
+
return `- ${name} [${status}]: ${description}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function findGeneratorSubcommandRow(packageEntry = {}, subcommandName = "") {
|
|
181
|
+
const metadata = resolveGeneratorSubcommandMetadata(packageEntry);
|
|
182
|
+
const normalizedSubcommandName = String(subcommandName || "").trim().toLowerCase();
|
|
183
|
+
if (!normalizedSubcommandName) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
for (const row of ensureArray(metadata.subcommands)) {
|
|
187
|
+
if (String(row.name || "").trim().toLowerCase() === normalizedSubcommandName) {
|
|
188
|
+
return row;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function buildSubcommandOptionRows(optionRows = [], subcommandRow = {}) {
|
|
195
|
+
const packageOptionRows = ensureArray(optionRows);
|
|
196
|
+
const optionNames = ensureArray(subcommandRow.optionNames).map((value) => String(value || "").trim()).filter(Boolean);
|
|
197
|
+
const requiredOptionNames = new Set(
|
|
198
|
+
ensureArray(subcommandRow.requiredOptionNames).map((value) => String(value || "").trim()).filter(Boolean)
|
|
199
|
+
);
|
|
200
|
+
if (optionNames.length < 1) {
|
|
201
|
+
return packageOptionRows;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const optionRowsByName = new Map();
|
|
205
|
+
for (const optionRow of packageOptionRows) {
|
|
206
|
+
optionRowsByName.set(String(optionRow.name || "").trim(), optionRow);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const rows = [];
|
|
210
|
+
for (const optionName of optionNames) {
|
|
211
|
+
const optionRow = optionRowsByName.get(optionName);
|
|
212
|
+
if (optionRow) {
|
|
213
|
+
if (requiredOptionNames.size > 0) {
|
|
214
|
+
rows.push(Object.freeze({
|
|
215
|
+
...optionRow,
|
|
216
|
+
required: requiredOptionNames.has(optionName)
|
|
217
|
+
}));
|
|
218
|
+
} else {
|
|
219
|
+
rows.push(optionRow);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return rows;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function renderGenerateCatalogHelp({
|
|
227
|
+
io,
|
|
228
|
+
packageRegistry,
|
|
229
|
+
resolvePackageKind,
|
|
230
|
+
json = false
|
|
231
|
+
} = {}) {
|
|
232
|
+
const generators = sortStrings([...packageRegistry.keys()])
|
|
233
|
+
.map((packageId) => packageRegistry.get(packageId))
|
|
234
|
+
.filter((entry) => resolvePackageKind(entry) === "generator")
|
|
235
|
+
.map((entry) => {
|
|
236
|
+
const packageId = String(entry?.packageId || "").trim();
|
|
237
|
+
return Object.freeze({
|
|
238
|
+
packageId,
|
|
239
|
+
shortId: toShortPackageId(packageId),
|
|
240
|
+
version: String(entry?.version || "").trim(),
|
|
241
|
+
description: resolvePackageSummary(entry)
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (json) {
|
|
246
|
+
io.stdout.write(`${JSON.stringify({
|
|
247
|
+
command: "generate",
|
|
248
|
+
generators,
|
|
249
|
+
usage: [
|
|
250
|
+
"jskit generate <generatorId> help",
|
|
251
|
+
"jskit generate <generatorId> [subcommand] [subcommand args...] [--<option> <value>...]",
|
|
252
|
+
"jskit list generators"
|
|
253
|
+
]
|
|
254
|
+
}, null, 2)}\n`);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const lines = [];
|
|
259
|
+
lines.push("Generate command");
|
|
260
|
+
lines.push("");
|
|
261
|
+
lines.push(`Available generators (${generators.length}):`);
|
|
262
|
+
for (const generator of generators) {
|
|
263
|
+
const shortIdSuffix =
|
|
264
|
+
generator.shortId && generator.shortId !== generator.packageId ? `${generator.shortId} ` : "";
|
|
265
|
+
const versionSuffix = generator.version ? ` (${generator.version})` : "";
|
|
266
|
+
const descriptionSuffix = generator.description ? `: ${generator.description}` : "";
|
|
267
|
+
lines.push(`- ${shortIdSuffix}${generator.packageId}${versionSuffix}${descriptionSuffix}`.trim());
|
|
268
|
+
}
|
|
269
|
+
lines.push("");
|
|
270
|
+
lines.push("Use:");
|
|
271
|
+
lines.push("- jskit generate <generatorId> help");
|
|
272
|
+
lines.push("- jskit generate <generatorId> [subcommand] [subcommand args...] [--<option> <value>...]");
|
|
273
|
+
lines.push("- jskit list generators");
|
|
274
|
+
io.stdout.write(`${lines.join("\n")}\n`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function renderAddCatalogHelp({
|
|
278
|
+
io,
|
|
279
|
+
packageRegistry,
|
|
280
|
+
bundleRegistry,
|
|
281
|
+
resolvePackageKind,
|
|
282
|
+
json = false
|
|
283
|
+
} = {}) {
|
|
284
|
+
const bundles = sortStrings([...bundleRegistry.keys()]).map((bundleId) => {
|
|
285
|
+
const bundle = ensureObject(bundleRegistry.get(bundleId));
|
|
286
|
+
return Object.freeze({
|
|
287
|
+
bundleId,
|
|
288
|
+
version: String(bundle.version || "").trim(),
|
|
289
|
+
description: String(bundle.description || "").trim(),
|
|
290
|
+
packageCount: ensureArray(bundle.packages).length
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const runtimePackages = sortStrings([...packageRegistry.keys()])
|
|
295
|
+
.map((packageId) => packageRegistry.get(packageId))
|
|
296
|
+
.filter((entry) => resolvePackageKind(entry) === "runtime")
|
|
297
|
+
.map((entry) => {
|
|
298
|
+
const packageId = String(entry?.packageId || "").trim();
|
|
299
|
+
return Object.freeze({
|
|
300
|
+
packageId,
|
|
301
|
+
shortId: toShortPackageId(packageId),
|
|
302
|
+
version: String(entry?.version || "").trim(),
|
|
303
|
+
description: resolvePackageSummary(entry)
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
if (json) {
|
|
308
|
+
io.stdout.write(`${JSON.stringify({
|
|
309
|
+
command: "add",
|
|
310
|
+
bundles,
|
|
311
|
+
runtimePackages,
|
|
312
|
+
usage: [
|
|
313
|
+
"jskit add package <packageId> help",
|
|
314
|
+
"jskit add bundle <bundleId> help",
|
|
315
|
+
"jskit add package <packageId> [--<option> <value>...]",
|
|
316
|
+
"jskit add bundle <bundleId> [--<option> <value>...]"
|
|
317
|
+
]
|
|
318
|
+
}, null, 2)}\n`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const lines = [];
|
|
323
|
+
lines.push("Add command");
|
|
324
|
+
lines.push("");
|
|
325
|
+
lines.push(`Available bundles (${bundles.length}):`);
|
|
326
|
+
for (const bundle of bundles) {
|
|
327
|
+
const versionSuffix = bundle.version ? ` (${bundle.version})` : "";
|
|
328
|
+
const countSuffix = ` [packages:${bundle.packageCount}]`;
|
|
329
|
+
const descriptionSuffix = bundle.description ? `: ${bundle.description}` : "";
|
|
330
|
+
lines.push(`- ${bundle.bundleId}${versionSuffix}${countSuffix}${descriptionSuffix}`);
|
|
331
|
+
}
|
|
332
|
+
lines.push("");
|
|
333
|
+
lines.push(`Available runtime packages (${runtimePackages.length}):`);
|
|
334
|
+
for (const runtimePackage of runtimePackages) {
|
|
335
|
+
const shortIdSuffix =
|
|
336
|
+
runtimePackage.shortId && runtimePackage.shortId !== runtimePackage.packageId
|
|
337
|
+
? `${runtimePackage.shortId} `
|
|
338
|
+
: "";
|
|
339
|
+
const versionSuffix = runtimePackage.version ? ` (${runtimePackage.version})` : "";
|
|
340
|
+
const descriptionSuffix = runtimePackage.description ? `: ${runtimePackage.description}` : "";
|
|
341
|
+
lines.push(`- ${shortIdSuffix}${runtimePackage.packageId}${versionSuffix}${descriptionSuffix}`.trim());
|
|
342
|
+
}
|
|
343
|
+
lines.push("");
|
|
344
|
+
lines.push("Use:");
|
|
345
|
+
lines.push("- jskit add package <packageId> help");
|
|
346
|
+
lines.push("- jskit add bundle <bundleId> help");
|
|
347
|
+
lines.push("- jskit add package <packageId> [--<option> <value>...]");
|
|
348
|
+
lines.push("- jskit add bundle <bundleId> [--<option> <value>...]");
|
|
349
|
+
io.stdout.write(`${lines.join("\n")}\n`);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function renderGeneratePackageHelp({
|
|
353
|
+
io,
|
|
354
|
+
packageEntry,
|
|
355
|
+
packageIdInput = "",
|
|
356
|
+
json = false
|
|
357
|
+
} = {}) {
|
|
358
|
+
const packageId = String(packageEntry?.packageId || "").trim();
|
|
359
|
+
const summary = resolvePackageSummary(packageEntry);
|
|
360
|
+
const optionRows = buildPackageOptionRows(packageEntry);
|
|
361
|
+
const generatorMetadata = resolveGeneratorSubcommandMetadata(packageEntry);
|
|
362
|
+
const preferredId = toShortPackageId(packageId) || packageId;
|
|
363
|
+
const usage = Object.freeze([
|
|
364
|
+
`jskit generate ${preferredId} help`,
|
|
365
|
+
`jskit generate ${preferredId} help <subcommand>`,
|
|
366
|
+
`jskit generate ${preferredId} <subcommand> help`,
|
|
367
|
+
`jskit generate ${preferredId} [subcommand] [subcommand args...] [--<option> <value>...]`
|
|
368
|
+
]);
|
|
369
|
+
const resolvedFrom = String(packageIdInput || "").trim();
|
|
370
|
+
const resolvedFromLabel = resolvedFrom && resolvedFrom !== packageId ? resolvedFrom : "";
|
|
371
|
+
const hasRequiredWithDefaults = optionRows.some((row) => row.required && row.defaultValue);
|
|
372
|
+
|
|
373
|
+
if (json) {
|
|
374
|
+
io.stdout.write(`${JSON.stringify({
|
|
375
|
+
command: "generate",
|
|
376
|
+
targetType: "generator",
|
|
377
|
+
packageId,
|
|
378
|
+
resolvedFrom: resolvedFromLabel,
|
|
379
|
+
description: summary,
|
|
380
|
+
usage,
|
|
381
|
+
primarySubcommand: generatorMetadata.primarySubcommand || "",
|
|
382
|
+
subcommands: generatorMetadata.subcommands,
|
|
383
|
+
options: optionRows
|
|
384
|
+
}, null, 2)}\n`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const lines = [];
|
|
389
|
+
lines.push(`Generator help: ${packageId}`);
|
|
390
|
+
if (resolvedFromLabel) {
|
|
391
|
+
lines.push(`Resolved from: ${resolvedFromLabel}`);
|
|
392
|
+
}
|
|
393
|
+
if (summary) {
|
|
394
|
+
lines.push(`Description: ${summary}`);
|
|
395
|
+
}
|
|
396
|
+
lines.push("");
|
|
397
|
+
lines.push("Use:");
|
|
398
|
+
for (const usageLine of usage) {
|
|
399
|
+
lines.push(`- ${usageLine}`);
|
|
400
|
+
}
|
|
401
|
+
lines.push("");
|
|
402
|
+
|
|
403
|
+
const subcommands = ensureArray(generatorMetadata.subcommands);
|
|
404
|
+
if (subcommands.length > 0) {
|
|
405
|
+
lines.push(`Subcommands (${subcommands.length}):`);
|
|
406
|
+
for (const subcommand of subcommands) {
|
|
407
|
+
const primarySuffix = subcommand.primary ? " [primary]" : "";
|
|
408
|
+
const descriptionSuffix = subcommand.description ? `: ${subcommand.description}` : "";
|
|
409
|
+
lines.push(`- ${subcommand.name}${primarySuffix}${descriptionSuffix}`);
|
|
410
|
+
}
|
|
411
|
+
lines.push("- Use subcommand help for details: jskit generate <generatorId> <subcommand> help");
|
|
412
|
+
lines.push("");
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
lines.push(`Options (${optionRows.length}):`);
|
|
416
|
+
if (optionRows.length > 0) {
|
|
417
|
+
for (const optionRow of optionRows) {
|
|
418
|
+
lines.push(formatOptionSummary(optionRow));
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
lines.push("- No inline options.");
|
|
422
|
+
}
|
|
423
|
+
if (hasRequiredWithDefaults) {
|
|
424
|
+
lines.push("");
|
|
425
|
+
lines.push("Note: required options with defaults are auto-filled when omitted.");
|
|
426
|
+
}
|
|
427
|
+
io.stdout.write(`${lines.join("\n")}\n`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function renderGenerateSubcommandHelp({
|
|
431
|
+
io,
|
|
432
|
+
packageEntry,
|
|
433
|
+
packageIdInput = "",
|
|
434
|
+
subcommandName = "",
|
|
435
|
+
json = false
|
|
436
|
+
} = {}) {
|
|
437
|
+
const packageId = String(packageEntry?.packageId || "").trim();
|
|
438
|
+
const summary = resolvePackageSummary(packageEntry);
|
|
439
|
+
const optionRows = buildPackageOptionRows(packageEntry);
|
|
440
|
+
const preferredId = toShortPackageId(packageId) || packageId;
|
|
441
|
+
const normalizedSubcommandName = String(subcommandName || "").trim();
|
|
442
|
+
const subcommandRow = findGeneratorSubcommandRow(packageEntry, normalizedSubcommandName);
|
|
443
|
+
if (!subcommandRow) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const resolvedFrom = String(packageIdInput || "").trim();
|
|
448
|
+
const resolvedFromLabel = resolvedFrom && resolvedFrom !== packageId ? resolvedFrom : "";
|
|
449
|
+
const description = String(subcommandRow.description || "").trim();
|
|
450
|
+
const effectiveDescription = description || (
|
|
451
|
+
subcommandRow.primary
|
|
452
|
+
? "Primary generator command."
|
|
453
|
+
: "No description provided."
|
|
454
|
+
);
|
|
455
|
+
const positionalArgs = ensureArray(subcommandRow.positionalArgs);
|
|
456
|
+
const positionalArgTokens = positionalArgs
|
|
457
|
+
.map((arg) => formatPositionalArgUsageToken(arg))
|
|
458
|
+
.filter(Boolean);
|
|
459
|
+
const invocationLine = `jskit generate ${preferredId} ${subcommandRow.name}${positionalArgTokens.length > 0
|
|
460
|
+
? ` ${positionalArgTokens.join(" ")}`
|
|
461
|
+
: ""} [--<option> <value>...]`;
|
|
462
|
+
const usage = Object.freeze([
|
|
463
|
+
invocationLine,
|
|
464
|
+
`jskit generate ${preferredId} ${subcommandRow.name} help`
|
|
465
|
+
]);
|
|
466
|
+
const subcommandOptionRows = buildSubcommandOptionRows(optionRows, subcommandRow);
|
|
467
|
+
const hasRequiredWithDefaults = subcommandOptionRows.some((row) => row.required && row.defaultValue);
|
|
468
|
+
|
|
469
|
+
if (json) {
|
|
470
|
+
io.stdout.write(`${JSON.stringify({
|
|
471
|
+
command: "generate",
|
|
472
|
+
targetType: "generator-subcommand-help",
|
|
473
|
+
packageId,
|
|
474
|
+
resolvedFrom: resolvedFromLabel,
|
|
475
|
+
generatorDescription: summary,
|
|
476
|
+
subcommand: {
|
|
477
|
+
name: subcommandRow.name,
|
|
478
|
+
primary: subcommandRow.primary,
|
|
479
|
+
description: effectiveDescription,
|
|
480
|
+
positionalArgs,
|
|
481
|
+
options: subcommandOptionRows
|
|
482
|
+
},
|
|
483
|
+
usage
|
|
484
|
+
}, null, 2)}\n`);
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const lines = [];
|
|
489
|
+
lines.push(`Generator subcommand help: ${packageId} ${subcommandRow.name}`);
|
|
490
|
+
if (resolvedFromLabel) {
|
|
491
|
+
lines.push(`Resolved from: ${resolvedFromLabel}`);
|
|
492
|
+
}
|
|
493
|
+
if (summary) {
|
|
494
|
+
lines.push(`Generator: ${summary}`);
|
|
495
|
+
}
|
|
496
|
+
lines.push(`Description: ${effectiveDescription}`);
|
|
497
|
+
if (subcommandRow.primary) {
|
|
498
|
+
lines.push("Note: this is the primary generator command (same behavior as running without a subcommand).");
|
|
499
|
+
}
|
|
500
|
+
lines.push("");
|
|
501
|
+
lines.push("Use:");
|
|
502
|
+
for (const usageLine of usage) {
|
|
503
|
+
lines.push(`- ${usageLine}`);
|
|
504
|
+
}
|
|
505
|
+
lines.push("");
|
|
506
|
+
lines.push(`Positional args (${positionalArgs.length}):`);
|
|
507
|
+
if (positionalArgs.length > 0) {
|
|
508
|
+
for (const positionalArg of positionalArgs) {
|
|
509
|
+
lines.push(formatPositionalArgSummary(positionalArg));
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
lines.push("- No positional arguments.");
|
|
513
|
+
}
|
|
514
|
+
lines.push("");
|
|
515
|
+
lines.push(`Options (${subcommandOptionRows.length}):`);
|
|
516
|
+
if (subcommandOptionRows.length > 0) {
|
|
517
|
+
for (const optionRow of subcommandOptionRows) {
|
|
518
|
+
lines.push(formatOptionSummary(optionRow));
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
lines.push("- No inline options.");
|
|
522
|
+
}
|
|
523
|
+
if (hasRequiredWithDefaults) {
|
|
524
|
+
lines.push("");
|
|
525
|
+
lines.push("Note: required options with defaults are auto-filled when omitted.");
|
|
526
|
+
}
|
|
527
|
+
io.stdout.write(`${lines.join("\n")}\n`);
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function renderAddPackageHelp({
|
|
532
|
+
io,
|
|
533
|
+
packageEntry,
|
|
534
|
+
packageIdInput = "",
|
|
535
|
+
json = false
|
|
536
|
+
} = {}) {
|
|
537
|
+
const packageId = String(packageEntry?.packageId || "").trim();
|
|
538
|
+
const summary = resolvePackageSummary(packageEntry);
|
|
539
|
+
const optionRows = buildPackageOptionRows(packageEntry);
|
|
540
|
+
const preferredId = toShortPackageId(packageId) || packageId;
|
|
541
|
+
const usage = Object.freeze([
|
|
542
|
+
`jskit add package ${preferredId} help`,
|
|
543
|
+
`jskit add package ${preferredId} [--<option> <value>...]`
|
|
544
|
+
]);
|
|
545
|
+
const resolvedFrom = String(packageIdInput || "").trim();
|
|
546
|
+
const resolvedFromLabel = resolvedFrom && resolvedFrom !== packageId ? resolvedFrom : "";
|
|
547
|
+
const hasRequiredWithDefaults = optionRows.some((row) => row.required && row.defaultValue);
|
|
548
|
+
|
|
549
|
+
if (json) {
|
|
550
|
+
io.stdout.write(`${JSON.stringify({
|
|
551
|
+
command: "add",
|
|
552
|
+
targetType: "package",
|
|
553
|
+
packageId,
|
|
554
|
+
resolvedFrom: resolvedFromLabel,
|
|
555
|
+
description: summary,
|
|
556
|
+
usage,
|
|
557
|
+
options: optionRows
|
|
558
|
+
}, null, 2)}\n`);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const lines = [];
|
|
563
|
+
lines.push(`Package help: ${packageId}`);
|
|
564
|
+
if (resolvedFromLabel) {
|
|
565
|
+
lines.push(`Resolved from: ${resolvedFromLabel}`);
|
|
566
|
+
}
|
|
567
|
+
if (summary) {
|
|
568
|
+
lines.push(`Description: ${summary}`);
|
|
569
|
+
}
|
|
570
|
+
lines.push("");
|
|
571
|
+
lines.push("Use:");
|
|
572
|
+
for (const usageLine of usage) {
|
|
573
|
+
lines.push(`- ${usageLine}`);
|
|
574
|
+
}
|
|
575
|
+
lines.push("");
|
|
576
|
+
lines.push(`Options (${optionRows.length}):`);
|
|
577
|
+
if (optionRows.length > 0) {
|
|
578
|
+
for (const optionRow of optionRows) {
|
|
579
|
+
lines.push(formatOptionSummary(optionRow));
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
lines.push("- No inline options.");
|
|
583
|
+
}
|
|
584
|
+
if (hasRequiredWithDefaults) {
|
|
585
|
+
lines.push("");
|
|
586
|
+
lines.push("Note: required options with defaults are auto-filled when omitted.");
|
|
587
|
+
}
|
|
588
|
+
io.stdout.write(`${lines.join("\n")}\n`);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function renderAddBundleHelp({
|
|
592
|
+
io,
|
|
593
|
+
bundleId = "",
|
|
594
|
+
bundle = {},
|
|
595
|
+
json = false
|
|
596
|
+
} = {}) {
|
|
597
|
+
const normalizedBundle = ensureObject(bundle);
|
|
598
|
+
const packageIds = ensureArray(normalizedBundle.packages).map((value) => String(value || "").trim()).filter(Boolean);
|
|
599
|
+
const description = String(normalizedBundle.description || "").trim();
|
|
600
|
+
const usage = Object.freeze([
|
|
601
|
+
`jskit add bundle ${bundleId} help`,
|
|
602
|
+
`jskit add bundle ${bundleId} [--<option> <value>...]`,
|
|
603
|
+
"jskit add package <packageId> help"
|
|
604
|
+
]);
|
|
605
|
+
|
|
606
|
+
if (json) {
|
|
607
|
+
io.stdout.write(`${JSON.stringify({
|
|
608
|
+
command: "add",
|
|
609
|
+
targetType: "bundle",
|
|
610
|
+
bundleId,
|
|
611
|
+
description,
|
|
612
|
+
packages: packageIds,
|
|
613
|
+
usage
|
|
614
|
+
}, null, 2)}\n`);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const lines = [];
|
|
619
|
+
lines.push(`Bundle help: ${bundleId}`);
|
|
620
|
+
if (description) {
|
|
621
|
+
lines.push(`Description: ${description}`);
|
|
622
|
+
}
|
|
623
|
+
lines.push("");
|
|
624
|
+
lines.push(`Included packages (${packageIds.length}):`);
|
|
625
|
+
for (const packageId of packageIds) {
|
|
626
|
+
lines.push(`- ${packageId}`);
|
|
627
|
+
}
|
|
628
|
+
lines.push("");
|
|
629
|
+
lines.push("Inline options:");
|
|
630
|
+
lines.push("- Bundles do not define direct options.");
|
|
631
|
+
lines.push("- Pass package options through using --<option> <value>.");
|
|
632
|
+
lines.push("- Use package help to inspect option contracts for included packages.");
|
|
633
|
+
lines.push("");
|
|
634
|
+
lines.push("Use:");
|
|
635
|
+
for (const usageLine of usage) {
|
|
636
|
+
lines.push(`- ${usageLine}`);
|
|
637
|
+
}
|
|
638
|
+
io.stdout.write(`${lines.join("\n")}\n`);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export {
|
|
642
|
+
isHelpToken,
|
|
643
|
+
renderGenerateCatalogHelp,
|
|
644
|
+
renderAddCatalogHelp,
|
|
645
|
+
renderGeneratePackageHelp,
|
|
646
|
+
renderGenerateSubcommandHelp,
|
|
647
|
+
renderAddPackageHelp,
|
|
648
|
+
renderAddBundleHelp
|
|
649
|
+
};
|