@jskit-ai/jskit-cli 0.2.25 → 0.2.27

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.
@@ -124,6 +124,18 @@ function createCommandHandlers(deps) {
124
124
  return sortStrings(dependents);
125
125
  }
126
126
 
127
+ function resolvePackageKind(packageEntry) {
128
+ const descriptor = ensureObject(packageEntry?.descriptor);
129
+ const normalizedKind = String(descriptor.kind || "").trim().toLowerCase();
130
+ if (normalizedKind === "runtime" || normalizedKind === "generator") {
131
+ return normalizedKind;
132
+ }
133
+ const packageId = String(packageEntry?.packageId || descriptor.packageId || "unknown-package").trim();
134
+ throw createCliError(
135
+ `Invalid package descriptor for ${packageId}: missing/invalid kind (expected runtime or generator).`
136
+ );
137
+ }
138
+
127
139
  function resolvePackageOptionNames(packageEntry) {
128
140
  const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
129
141
  return Object.keys(optionSchemas);
@@ -177,6 +189,155 @@ function createCommandHandlers(deps) {
177
189
  : " This bundle does not accept inline options.";
178
190
  throw createCliError(`Unknown option(s) for bundle ${bundleId}: ${sortedUnknown.join(", ")}.${suffix}`);
179
191
  }
192
+
193
+ function collectDescriptorContainerTokens({ packageId, side, values, issues }) {
194
+ const declaredTokens = new Set();
195
+ const duplicateTokens = new Set();
196
+ let invalidCount = 0;
197
+
198
+ for (const rawValue of ensureArray(values)) {
199
+ if (typeof rawValue !== "string") {
200
+ invalidCount += 1;
201
+ continue;
202
+ }
203
+ const token = rawValue.trim();
204
+ if (!token) {
205
+ invalidCount += 1;
206
+ continue;
207
+ }
208
+ if (declaredTokens.has(token)) {
209
+ duplicateTokens.add(token);
210
+ continue;
211
+ }
212
+ declaredTokens.add(token);
213
+ }
214
+
215
+ if (invalidCount > 0) {
216
+ issues.push({
217
+ packageId,
218
+ side,
219
+ code: "descriptor-token-invalid",
220
+ message: `${packageId} (${side}): metadata.apiSummary.containerTokens includes ${invalidCount} non-string or empty token value(s).`
221
+ });
222
+ }
223
+ for (const token of sortStrings([...duplicateTokens])) {
224
+ issues.push({
225
+ packageId,
226
+ side,
227
+ code: "descriptor-token-duplicate",
228
+ token,
229
+ message: `${packageId} (${side}): descriptor token is declared more than once: ${token}.`
230
+ });
231
+ }
232
+
233
+ return declaredTokens;
234
+ }
235
+
236
+ function collectUsedContainerTokens({ packageId, side, bindings, issues }) {
237
+ const usedTokens = new Set();
238
+ for (const rawBinding of ensureArray(bindings)) {
239
+ const binding = ensureObject(rawBinding);
240
+ const tokenExpression = String(binding.tokenExpression || "").trim();
241
+ const token = String(binding.token || "").trim();
242
+ const location = String(binding.location || "").trim();
243
+ if (binding.tokenResolved !== true || !token) {
244
+ const expressionLabel = tokenExpression || "<empty>";
245
+ const locationSuffix = location ? ` at ${location}` : "";
246
+ issues.push({
247
+ packageId,
248
+ side,
249
+ code: "binding-token-unresolved",
250
+ tokenExpression: expressionLabel,
251
+ location,
252
+ message: `${packageId} (${side}): unresolved DI token expression "${expressionLabel}"${locationSuffix}.`
253
+ });
254
+ continue;
255
+ }
256
+ usedTokens.add(token);
257
+ }
258
+ return usedTokens;
259
+ }
260
+
261
+ function collectProviderIntrospectionIssues({ packageId, packageInsights, issues }) {
262
+ const introspection = ensureObject(packageInsights);
263
+ if (!introspection.available) {
264
+ issues.push({
265
+ packageId,
266
+ side: "",
267
+ code: "provider-introspection-unavailable",
268
+ message: `${packageId}: provider source introspection is unavailable, so DI token parity cannot be verified.`
269
+ });
270
+ return;
271
+ }
272
+
273
+ const notes = ensureArray(introspection.notes).map((value) => String(value || "").trim()).filter(Boolean);
274
+ for (const note of notes) {
275
+ if (
276
+ note.startsWith("Skipped wildcard provider entrypoint during introspection:") ||
277
+ note.startsWith("Provider file missing during introspection:") ||
278
+ note.startsWith("Failed reading provider ")
279
+ ) {
280
+ issues.push({
281
+ packageId,
282
+ side: "",
283
+ code: "provider-introspection-incomplete",
284
+ message: `${packageId}: ${note}`
285
+ });
286
+ }
287
+ }
288
+ }
289
+
290
+ function collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }) {
291
+ const packageId = String(packageEntry?.packageId || "").trim();
292
+ const descriptor = ensureObject(packageEntry?.descriptor);
293
+ const metadataApiSummary = ensureObject(ensureObject(descriptor.metadata).apiSummary);
294
+ const descriptorTokenSummary = ensureObject(metadataApiSummary.containerTokens);
295
+ const bindingSections = ensureObject(ensureObject(packageInsights).containerBindings);
296
+ const issues = [];
297
+ const sides = ["server", "client"];
298
+
299
+ collectProviderIntrospectionIssues({ packageId, packageInsights, issues });
300
+
301
+ for (const side of sides) {
302
+ const declaredTokens = collectDescriptorContainerTokens({
303
+ packageId,
304
+ side,
305
+ values: descriptorTokenSummary[side],
306
+ issues
307
+ });
308
+ const usedTokens = collectUsedContainerTokens({
309
+ packageId,
310
+ side,
311
+ bindings: bindingSections[side],
312
+ issues
313
+ });
314
+
315
+ for (const token of sortStrings([...usedTokens])) {
316
+ if (!declaredTokens.has(token)) {
317
+ issues.push({
318
+ packageId,
319
+ side,
320
+ code: "binding-token-undeclared",
321
+ token,
322
+ message: `${packageId} (${side}): token is used by providers but missing from metadata.apiSummary.containerTokens.${side}: ${token}.`
323
+ });
324
+ }
325
+ }
326
+ for (const token of sortStrings([...declaredTokens])) {
327
+ if (!usedTokens.has(token)) {
328
+ issues.push({
329
+ packageId,
330
+ side,
331
+ code: "descriptor-token-unused",
332
+ token,
333
+ message: `${packageId} (${side}): token is declared in metadata.apiSummary.containerTokens.${side} but never bound by providers: ${token}.`
334
+ });
335
+ }
336
+ }
337
+ }
338
+
339
+ return issues;
340
+ }
180
341
 
181
342
  async function commandList({ positional, options, cwd, stdout }) {
182
343
  const packageRegistry = await loadPackageRegistry();
@@ -207,8 +368,9 @@ function createCommandHandlers(deps) {
207
368
  const mode = String(positional[0] || "").trim();
208
369
  const shouldListBundles = !mode || mode === "bundles";
209
370
  const shouldListPackages = !mode || mode === "packages";
210
-
211
- if (!shouldListBundles && !shouldListPackages) {
371
+ const shouldListGenerators = !mode || mode === "generators";
372
+
373
+ if (!shouldListBundles && !shouldListPackages && !shouldListGenerators) {
212
374
  throw createCliError(`Unknown list mode: ${mode}`, { showUsage: true });
213
375
  }
214
376
 
@@ -238,8 +400,11 @@ function createCommandHandlers(deps) {
238
400
  if (lines.length > 0) {
239
401
  lines.push("");
240
402
  }
241
- lines.push(color.heading("Available packages:"));
242
- const packageIds = sortStrings([...packageRegistry.keys()]);
403
+ lines.push(color.heading("Available runtime packages:"));
404
+ const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
405
+ const packageEntry = packageRegistry.get(packageId);
406
+ return resolvePackageKind(packageEntry) === "runtime";
407
+ }));
243
408
  for (const packageId of packageIds) {
244
409
  const packageEntry = packageRegistry.get(packageId);
245
410
  const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
@@ -281,6 +446,24 @@ function createCommandHandlers(deps) {
281
446
  }
282
447
  }
283
448
  }
449
+
450
+ if (shouldListGenerators) {
451
+ if (lines.length > 0) {
452
+ lines.push("");
453
+ }
454
+ lines.push(color.heading("Available generators:"));
455
+ const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
456
+ const packageEntry = packageRegistry.get(packageId);
457
+ return resolvePackageKind(packageEntry) === "generator";
458
+ }));
459
+ for (const packageId of packageIds) {
460
+ const packageEntry = packageRegistry.get(packageId);
461
+ const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
462
+ lines.push(
463
+ `- ${color.item(packageId)} ${color.version(`(${packageEntry.version})`)}${installedLabel ? color.installed(installedLabel) : ""}`
464
+ );
465
+ }
466
+ }
284
467
 
285
468
  if (options.json) {
286
469
  const payload = {
@@ -299,14 +482,42 @@ function createCommandHandlers(deps) {
299
482
  })
300
483
  : [],
301
484
  packages: shouldListPackages
485
+ ? sortStrings([...packageRegistry.keys()])
486
+ .filter((packageId) => resolvePackageKind(packageRegistry.get(packageId)) === "runtime")
487
+ .map((packageId) => {
488
+ const packageEntry = packageRegistry.get(packageId);
489
+ return {
490
+ packageId,
491
+ version: packageEntry.version,
492
+ installed: installedPackages.has(packageId)
493
+ };
494
+ })
495
+ : [],
496
+ runtimePackages: shouldListPackages
302
497
  ? sortStrings([...packageRegistry.keys()]).map((packageId) => {
303
498
  const packageEntry = packageRegistry.get(packageId);
499
+ if (resolvePackageKind(packageEntry) !== "runtime") {
500
+ return null;
501
+ }
304
502
  return {
305
503
  packageId,
306
504
  version: packageEntry.version,
307
505
  installed: installedPackages.has(packageId)
308
506
  };
309
- })
507
+ }).filter(Boolean)
508
+ : [],
509
+ generators: shouldListGenerators
510
+ ? sortStrings([...packageRegistry.keys()]).map((packageId) => {
511
+ const packageEntry = packageRegistry.get(packageId);
512
+ if (resolvePackageKind(packageEntry) !== "generator") {
513
+ return null;
514
+ }
515
+ return {
516
+ packageId,
517
+ version: packageEntry.version,
518
+ installed: installedPackages.has(packageId)
519
+ };
520
+ }).filter(Boolean)
310
521
  : [],
311
522
  installedLocalPackages: shouldListPackages
312
523
  ? installedLocalPackageIds.map((packageId) => {
@@ -1103,10 +1314,16 @@ function createCommandHandlers(deps) {
1103
1314
  }
1104
1315
 
1105
1316
  async function commandAdd({ positional, options, cwd, io }) {
1317
+ const invocationMode = options?.commandMode === "generate" ? "generate" : "add";
1106
1318
  const targetType = String(positional[0] || "").trim();
1107
1319
  const targetId = String(positional[1] || "").trim();
1108
1320
 
1109
1321
  if (!targetType || !targetId) {
1322
+ if (invocationMode === "generate") {
1323
+ throw createCliError("generate requires a package id (generate <packageId>).", {
1324
+ showUsage: true
1325
+ });
1326
+ }
1110
1327
  throw createCliError("add requires target type and id (add bundle <id> | add package <id>).", {
1111
1328
  showUsage: true
1112
1329
  });
@@ -1114,6 +1331,11 @@ function createCommandHandlers(deps) {
1114
1331
  if (targetType !== "bundle" && targetType !== "package") {
1115
1332
  throw createCliError(`Unsupported add target type: ${targetType}`, { showUsage: true });
1116
1333
  }
1334
+ if (invocationMode === "generate" && targetType !== "package") {
1335
+ throw createCliError("generate requires a package id (generate <packageId>).", {
1336
+ showUsage: true
1337
+ });
1338
+ }
1117
1339
 
1118
1340
  const appRoot = await resolveAppRootFromCwd(cwd);
1119
1341
  const packageRegistry = await loadPackageRegistry();
@@ -1123,10 +1345,11 @@ function createCommandHandlers(deps) {
1123
1345
  const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
1124
1346
  const { lockPath, lock } = await loadLockFile(appRoot);
1125
1347
  let resolvedTargetPackageId = targetType === "package" ? resolvePackageIdInput(targetId, combinedPackageRegistry) : "";
1126
- if (targetType === "package" && !resolvedTargetPackageId) {
1348
+ if (targetType === "package") {
1349
+ const packageIdForNodeModulesLookup = resolvedTargetPackageId || targetId;
1127
1350
  const installedNodeModuleEntry = await resolveInstalledNodeModulePackageEntry({
1128
1351
  appRoot,
1129
- packageId: targetId
1352
+ packageId: packageIdForNodeModulesLookup
1130
1353
  });
1131
1354
  if (installedNodeModuleEntry) {
1132
1355
  combinedPackageRegistry.set(installedNodeModuleEntry.packageId, installedNodeModuleEntry);
@@ -1157,6 +1380,17 @@ function createCommandHandlers(deps) {
1157
1380
  if (!targetPackageEntry) {
1158
1381
  throw createCliError(`Unknown package: ${targetId}`);
1159
1382
  }
1383
+ const packageKind = resolvePackageKind(targetPackageEntry);
1384
+ if (invocationMode === "add" && packageKind === "generator") {
1385
+ throw createCliError(
1386
+ `Package ${resolvedTargetPackageId} is a generator. Use: jskit generate ${resolvedTargetPackageId}`
1387
+ );
1388
+ }
1389
+ if (invocationMode === "generate" && packageKind !== "generator") {
1390
+ throw createCliError(
1391
+ `Package ${resolvedTargetPackageId} is a runtime package. Use: jskit add package ${resolvedTargetPackageId}`
1392
+ );
1393
+ }
1160
1394
  validateInlineOptionsForPackage(targetPackageEntry, options.inlineOptions);
1161
1395
  }
1162
1396
 
@@ -1164,6 +1398,17 @@ function createCommandHandlers(deps) {
1164
1398
  targetPackageIds,
1165
1399
  combinedPackageRegistry
1166
1400
  );
1401
+ if (invocationMode === "add" && targetType === "bundle") {
1402
+ const bundledGenerators = resolvedPackageIds.filter((packageId) => {
1403
+ const packageEntry = combinedPackageRegistry.get(packageId);
1404
+ return resolvePackageKind(packageEntry) === "generator";
1405
+ });
1406
+ if (bundledGenerators.length > 0) {
1407
+ throw createCliError(
1408
+ `Bundle ${targetId} includes generator package(s): ${bundledGenerators.join(", ")}. Use: jskit generate <packageId>`
1409
+ );
1410
+ }
1411
+ }
1167
1412
  const plannedInstalledPackageIds = sortStrings([
1168
1413
  ...new Set([
1169
1414
  ...Object.keys(ensureObject(lock.installedPackages)).map((value) => String(value || "").trim()).filter(Boolean),
@@ -1173,7 +1418,7 @@ function createCommandHandlers(deps) {
1173
1418
  validatePlannedCapabilityClosure(
1174
1419
  plannedInstalledPackageIds,
1175
1420
  combinedPackageRegistry,
1176
- `add ${targetType} ${targetId}`
1421
+ `${invocationMode} ${targetType} ${targetId}`
1177
1422
  );
1178
1423
 
1179
1424
  if (targetType === "bundle") {
@@ -1252,14 +1497,18 @@ function createCommandHandlers(deps) {
1252
1497
  validatePlannedCapabilityClosure(
1253
1498
  postInstallPackageIds,
1254
1499
  combinedPackageRegistry,
1255
- `add ${targetType} ${targetId}`
1500
+ `${invocationMode} ${targetType} ${targetId}`
1256
1501
  );
1257
1502
  }
1258
1503
 
1259
1504
  const finalResolvedPackageIds = sortStrings([...resolvedPackageIds, ...adoptedPackageIds]);
1260
1505
 
1261
1506
  const touchedFileList = sortStrings([...touchedFiles]);
1262
- const successLabel = targetType === "bundle" ? "Added bundle" : "Added package";
1507
+ const successLabel = invocationMode === "generate"
1508
+ ? "Generated with"
1509
+ : targetType === "bundle"
1510
+ ? "Added bundle"
1511
+ : "Added package";
1263
1512
  const installWarnings = installedPackageRecords
1264
1513
  .flatMap((record) => ensureArray(ensureObject(record).warnings))
1265
1514
  .map((value) => String(value || "").trim())
@@ -1275,7 +1524,7 @@ function createCommandHandlers(deps) {
1275
1524
 
1276
1525
  if (options.json) {
1277
1526
  io.stdout.write(`${JSON.stringify({
1278
- targetType,
1527
+ targetType: invocationMode === "generate" ? "generator" : targetType,
1279
1528
  targetId,
1280
1529
  resolvedPackages: finalResolvedPackageIds,
1281
1530
  touchedFiles: touchedFileList,
@@ -1310,6 +1559,32 @@ function createCommandHandlers(deps) {
1310
1559
 
1311
1560
  return 0;
1312
1561
  }
1562
+
1563
+ async function commandGenerate({ positional, options, cwd, io }) {
1564
+ const firstToken = String(positional[0] || "").trim();
1565
+ const secondToken = String(positional[1] || "").trim();
1566
+ if (firstToken === "bundle") {
1567
+ throw createCliError("generate supports packages only (generate <packageId>).", {
1568
+ showUsage: true
1569
+ });
1570
+ }
1571
+ const targetId = firstToken === "package" ? secondToken : firstToken;
1572
+ if (!targetId) {
1573
+ throw createCliError("generate requires a package id (generate <packageId>).", {
1574
+ showUsage: true
1575
+ });
1576
+ }
1577
+
1578
+ return commandAdd({
1579
+ positional: ["package", targetId],
1580
+ options: {
1581
+ ...options,
1582
+ commandMode: "generate"
1583
+ },
1584
+ cwd,
1585
+ io
1586
+ });
1587
+ }
1313
1588
 
1314
1589
  async function commandUpdate({ positional, options, cwd, io }) {
1315
1590
  const targetType = String(positional[0] || "").trim();
@@ -1455,7 +1730,7 @@ function createCommandHandlers(deps) {
1455
1730
  io.stdout.write(`- ${touchedFile}\n`);
1456
1731
  }
1457
1732
  io.stdout.write(`Lock file: ${normalizeRelativePath(appRoot, lockPath)}\n`);
1458
- if (migrationWarnings.length > 0) {
1733
+ if (options.verbose && migrationWarnings.length > 0) {
1459
1734
  io.stdout.write(`Warnings (${migrationWarnings.length}):\n`);
1460
1735
  for (const warning of migrationWarnings) {
1461
1736
  io.stdout.write(`- ${warning}\n`);
@@ -1760,19 +2035,58 @@ function createCommandHandlers(deps) {
1760
2035
  async function commandLintDescriptors({ options, stdout }) {
1761
2036
  const packageRegistry = await loadPackageRegistry();
1762
2037
  const bundleRegistry = await loadBundleRegistry();
2038
+ const shouldCheckDiLabels = options.checkDiLabels === true;
2039
+ let diLabelIssues = [];
2040
+ if (shouldCheckDiLabels) {
2041
+ const issues = [];
2042
+ for (const packageId of sortStrings([...packageRegistry.keys()])) {
2043
+ const packageEntry = packageRegistry.get(packageId);
2044
+ if (!packageEntry) {
2045
+ continue;
2046
+ }
2047
+ const packageInsights = await inspectPackageOfferings({ packageEntry });
2048
+ issues.push(...collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }));
2049
+ }
2050
+ diLabelIssues = issues;
2051
+ }
1763
2052
  const payload = {
1764
2053
  packageCount: packageRegistry.size,
1765
2054
  bundleCount: bundleRegistry.size,
1766
2055
  packages: sortStrings([...packageRegistry.keys()]),
1767
- bundles: sortStrings([...bundleRegistry.keys()])
2056
+ bundles: sortStrings([...bundleRegistry.keys()]),
2057
+ diLabelCheck: shouldCheckDiLabels
2058
+ ? {
2059
+ enabled: true,
2060
+ issueCount: diLabelIssues.length,
2061
+ issues: diLabelIssues
2062
+ }
2063
+ : {
2064
+ enabled: false
2065
+ }
1768
2066
  };
1769
2067
 
1770
2068
  if (options.json) {
1771
2069
  stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
1772
2070
  } else {
1773
- stdout.write(`Descriptor lint passed.\n`);
2071
+ const descriptorStatus = shouldCheckDiLabels && diLabelIssues.length > 0 ? "failed" : "passed";
2072
+ stdout.write(`Descriptor lint ${descriptorStatus}.\n`);
1774
2073
  stdout.write(`Packages: ${payload.packageCount}\n`);
1775
2074
  stdout.write(`Bundles: ${payload.bundleCount}\n`);
2075
+ if (shouldCheckDiLabels) {
2076
+ if (diLabelIssues.length === 0) {
2077
+ stdout.write("DI label parity check passed.\n");
2078
+ } else {
2079
+ stdout.write(`DI label parity check failed (${diLabelIssues.length} issue(s)).\n`);
2080
+ for (const issue of diLabelIssues) {
2081
+ const code = String(issue?.code || "").trim();
2082
+ const codeLabel = code ? `[${code}] ` : "";
2083
+ stdout.write(`- ${codeLabel}${String(issue?.message || "").trim()}\n`);
2084
+ }
2085
+ }
2086
+ }
2087
+ }
2088
+ if (shouldCheckDiLabels && diLabelIssues.length > 0) {
2089
+ return 1;
1776
2090
  }
1777
2091
  return 0;
1778
2092
  }
@@ -1782,6 +2096,7 @@ function createCommandHandlers(deps) {
1782
2096
  commandShow,
1783
2097
  commandCreate,
1784
2098
  commandAdd,
2099
+ commandGenerate,
1785
2100
  commandMigrations,
1786
2101
  commandPosition,
1787
2102
  commandUpdate,
@@ -8,66 +8,6 @@ import { createCliError } from "./cliError.js";
8
8
  const CLI_PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
9
9
  const require = createRequire(import.meta.url);
10
10
 
11
- function isWorkspaceRoot(candidateRoot) {
12
- if (!candidateRoot) {
13
- return false;
14
- }
15
- return (
16
- existsSync(path.join(candidateRoot, "packages")) &&
17
- existsSync(path.join(candidateRoot, "packages", "kernel")) &&
18
- existsSync(path.join(candidateRoot, "tooling", "jskit-cli"))
19
- );
20
- }
21
-
22
- function collectAncestorDirectories(startDirectory) {
23
- const ancestors = [];
24
- let current = path.resolve(startDirectory);
25
- while (true) {
26
- ancestors.push(current);
27
- const parent = path.dirname(current);
28
- if (parent === current) {
29
- break;
30
- }
31
- current = parent;
32
- }
33
- return ancestors;
34
- }
35
-
36
- function resolveWorkspaceRoot() {
37
- const candidates = [];
38
- const seen = new Set();
39
- const appendCandidate = (candidatePath) => {
40
- const raw = String(candidatePath || "").trim();
41
- if (!raw) {
42
- return;
43
- }
44
- const absolute = path.resolve(raw);
45
- if (seen.has(absolute)) {
46
- return;
47
- }
48
- seen.add(absolute);
49
- candidates.push(absolute);
50
- };
51
-
52
- appendCandidate(process.env.JSKIT_REPO_ROOT);
53
- appendCandidate(path.resolve(CLI_PACKAGE_ROOT, "../.."));
54
- appendCandidate(CLI_PACKAGE_ROOT);
55
-
56
- const cwdAncestors = collectAncestorDirectories(process.cwd());
57
- for (const ancestor of cwdAncestors) {
58
- appendCandidate(ancestor);
59
- appendCandidate(path.join(ancestor, "jskit-ai"));
60
- }
61
-
62
- for (const candidate of candidates) {
63
- if (isWorkspaceRoot(candidate)) {
64
- return candidate;
65
- }
66
- }
67
-
68
- return "";
69
- }
70
-
71
11
  function resolveCatalogPackagesPath() {
72
12
  const explicitPath = String(process.env.JSKIT_CATALOG_PACKAGES_PATH || "").trim();
73
13
  if (explicitPath) {
@@ -92,15 +32,11 @@ function resolveCatalogPackagesPath() {
92
32
  );
93
33
  }
94
34
 
95
- const WORKSPACE_ROOT = resolveWorkspaceRoot();
96
- const MODULES_ROOT = WORKSPACE_ROOT ? path.join(WORKSPACE_ROOT, "packages") : "";
97
35
  const BUNDLES_ROOT = path.join(CLI_PACKAGE_ROOT, "bundles");
98
36
  const CATALOG_PACKAGES_PATH = resolveCatalogPackagesPath();
99
37
 
100
38
  export {
101
39
  CLI_PACKAGE_ROOT,
102
- WORKSPACE_ROOT,
103
- MODULES_ROOT,
104
40
  BUNDLES_ROOT,
105
41
  CATALOG_PACKAGES_PATH
106
42
  };
@@ -64,6 +64,14 @@ function createRunCli({
64
64
  io: { stdin, stdout, stderr }
65
65
  });
66
66
  }
67
+ if (command === "generate") {
68
+ return await commandHandlers.commandGenerate({
69
+ positional,
70
+ options,
71
+ cwd,
72
+ io: { stdin, stdout, stderr }
73
+ });
74
+ }
67
75
  if (command === "position") {
68
76
  return await commandHandlers.commandPosition({
69
77
  positional,