@jskit-ai/jskit-cli 0.2.27 → 0.2.28

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.
Files changed (59) hide show
  1. package/package.json +3 -2
  2. package/src/server/cliRuntime/appState.js +226 -0
  3. package/src/server/cliRuntime/capabilitySupport.js +194 -0
  4. package/src/server/cliRuntime/descriptorValidation.js +150 -0
  5. package/src/server/cliRuntime/ioAndMigrations.js +381 -0
  6. package/src/server/cliRuntime/localPackageSupport.js +390 -0
  7. package/src/server/cliRuntime/mutationApplication.js +9 -0
  8. package/src/server/cliRuntime/mutationWhen.js +285 -0
  9. package/src/server/cliRuntime/mutations/fileMutations.js +247 -0
  10. package/src/server/cliRuntime/mutations/installMigrationMutation.js +213 -0
  11. package/src/server/cliRuntime/mutations/mutationPathUtils.js +12 -0
  12. package/src/server/cliRuntime/mutations/surfaceTargets.js +155 -0
  13. package/src/server/cliRuntime/mutations/templateContext.js +171 -0
  14. package/src/server/cliRuntime/mutations/textMutations.js +250 -0
  15. package/src/server/cliRuntime/packageInstallFlow.js +489 -0
  16. package/src/server/cliRuntime/packageIntrospection/exportEntries.js +259 -0
  17. package/src/server/cliRuntime/packageIntrospection/exportedSymbols.js +216 -0
  18. package/src/server/cliRuntime/packageIntrospection/placementNormalization.js +98 -0
  19. package/src/server/cliRuntime/packageIntrospection/providerBindingIntrospection.js +377 -0
  20. package/src/server/cliRuntime/packageIntrospection.js +137 -0
  21. package/src/server/cliRuntime/packageOptions.js +299 -0
  22. package/src/server/cliRuntime/packageRegistries.js +343 -0
  23. package/src/server/cliRuntime/packageTemplateResolution.js +131 -0
  24. package/src/server/cliRuntime/viteProxy.js +356 -0
  25. package/src/server/commandHandlers/health.js +292 -0
  26. package/src/server/commandHandlers/list.js +292 -0
  27. package/src/server/commandHandlers/package.js +23 -0
  28. package/src/server/commandHandlers/packageCommands/add.js +282 -0
  29. package/src/server/commandHandlers/packageCommands/create.js +155 -0
  30. package/src/server/commandHandlers/packageCommands/generate.js +116 -0
  31. package/src/server/commandHandlers/packageCommands/migrations.js +155 -0
  32. package/src/server/commandHandlers/packageCommands/position.js +103 -0
  33. package/src/server/commandHandlers/packageCommands/remove.js +181 -0
  34. package/src/server/commandHandlers/packageCommands/update.js +40 -0
  35. package/src/server/commandHandlers/shared.js +314 -0
  36. package/src/server/commandHandlers/show/payloads.js +92 -0
  37. package/src/server/commandHandlers/show/renderBundleText.js +16 -0
  38. package/src/server/commandHandlers/show/renderHelpers.js +82 -0
  39. package/src/server/commandHandlers/show/renderPackageCapabilities.js +124 -0
  40. package/src/server/commandHandlers/show/renderPackageExports.js +203 -0
  41. package/src/server/commandHandlers/show/renderPackageText.js +332 -0
  42. package/src/server/commandHandlers/show.js +114 -0
  43. package/src/server/core/argParser.js +144 -0
  44. package/src/server/{runtimeDeps.js → core/buildCommandDeps.js} +2 -1
  45. package/src/server/core/commandCatalog.js +47 -0
  46. package/src/server/core/createCliRunner.js +150 -0
  47. package/src/server/core/createCommandHandlers.js +43 -0
  48. package/src/server/{runCli.js → core/dispatchCli.js} +14 -1
  49. package/src/server/core/usageHelp.js +344 -0
  50. package/src/server/index.js +1 -1
  51. package/src/server/{optionInterpolation.js → shared/optionInterpolation.js} +12 -1
  52. package/src/server/{pathResolution.js → shared/pathResolution.js} +1 -1
  53. package/src/server/argParser.js +0 -206
  54. package/src/server/cliRuntime.js +0 -4956
  55. package/src/server/commandHandlers.js +0 -2109
  56. /package/src/server/{cliError.js → shared/cliError.js} +0 -0
  57. /package/src/server/{collectionUtils.js → shared/collectionUtils.js} +0 -0
  58. /package/src/server/{outputFormatting.js → shared/outputFormatting.js} +0 -0
  59. /package/src/server/{packageIdHelpers.js → shared/packageIdHelpers.js} +0 -0
@@ -0,0 +1,150 @@
1
+ import {
2
+ mkdir,
3
+ rm,
4
+ writeFile
5
+ } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { discoverShellOutletTargetsFromApp } from "@jskit-ai/kernel/server/support";
8
+ import { createCliError } from "../shared/cliError.js";
9
+ import {
10
+ createColorFormatter,
11
+ resolveWrapWidth,
12
+ writeWrappedItems
13
+ } from "../shared/outputFormatting.js";
14
+ import { createCommandHandlers } from "./createCommandHandlers.js";
15
+ import { parseArgs } from "./argParser.js";
16
+ import { printUsage, shouldShowCommandHelpOnBareInvocation } from "./usageHelp.js";
17
+ import { createCommandHandlerDeps } from "./buildCommandDeps.js";
18
+ import { createRunCli } from "./dispatchCli.js";
19
+ import {
20
+ resolvePackageIdInput,
21
+ resolveInstalledPackageIdInput
22
+ } from "../shared/packageIdHelpers.js";
23
+ import {
24
+ buildFileWriteGroups,
25
+ fileExists,
26
+ hashBuffer,
27
+ normalizeRelativePath,
28
+ readFileBufferIfExists
29
+ } from "../cliRuntime/ioAndMigrations.js";
30
+ import {
31
+ resolveAppRootFromCwd,
32
+ loadAppPackageJson,
33
+ loadLockFile,
34
+ applyPackageJsonField,
35
+ restorePackageJsonField,
36
+ removeEnvValue,
37
+ writeJsonFile
38
+ } from "../cliRuntime/appState.js";
39
+ import {
40
+ mergePackageRegistries,
41
+ loadAppLocalPackageRegistry,
42
+ loadPackageRegistry,
43
+ resolveInstalledNodeModulePackageEntry,
44
+ hydratePackageRegistryFromInstalledNodeModules,
45
+ loadBundleRegistry
46
+ } from "../cliRuntime/packageRegistries.js";
47
+ import {
48
+ normalizeRelativePosixPath,
49
+ toFileDependencySpecifier,
50
+ resolveLocalPackageId,
51
+ createLocalPackageScaffoldFiles,
52
+ resolveLocalDependencyOrder
53
+ } from "../cliRuntime/localPackageSupport.js";
54
+ import {
55
+ listDeclaredCapabilities,
56
+ buildCapabilityDetailsForPackage,
57
+ validatePlannedCapabilityClosure
58
+ } from "../cliRuntime/capabilitySupport.js";
59
+ import {
60
+ classifyExportedSymbols,
61
+ deriveProviderDisplayName,
62
+ formatPackageSubpathImport,
63
+ inspectPackageOfferings,
64
+ normalizePlacementContributions,
65
+ normalizePlacementOutlets,
66
+ shouldShowPackageExportTarget
67
+ } from "../cliRuntime/packageIntrospection.js";
68
+ import {
69
+ resolvePackageOptions,
70
+ validateInlineOptionsForPackage
71
+ } from "../cliRuntime/packageOptions.js";
72
+ import {
73
+ cleanupMaterializedPackageRoots
74
+ } from "../cliRuntime/packageTemplateResolution.js";
75
+ import {
76
+ applyPackageInstall,
77
+ applyPackageMigrationsOnly,
78
+ applyPackagePositioning,
79
+ adoptAppLocalPackageDependencies
80
+ } from "../cliRuntime/packageInstallFlow.js";
81
+ import {
82
+ removeManagedViteProxyEntries
83
+ } from "../cliRuntime/viteProxy.js";
84
+
85
+ const commandHandlers = createCommandHandlers(
86
+ createCommandHandlerDeps({
87
+ createCliError,
88
+ createColorFormatter,
89
+ resolveWrapWidth,
90
+ writeWrappedItems,
91
+ normalizeRelativePath,
92
+ normalizeRelativePosixPath,
93
+ resolveAppRootFromCwd,
94
+ loadLockFile,
95
+ loadPackageRegistry,
96
+ loadBundleRegistry,
97
+ loadAppLocalPackageRegistry,
98
+ mergePackageRegistries,
99
+ resolvePackageIdInput,
100
+ resolveInstalledPackageIdInput,
101
+ resolveInstalledNodeModulePackageEntry,
102
+ hydratePackageRegistryFromInstalledNodeModules,
103
+ validateInlineOptionsForPackage,
104
+ resolveLocalDependencyOrder,
105
+ validatePlannedCapabilityClosure,
106
+ resolvePackageOptions,
107
+ applyPackageInstall,
108
+ applyPackageMigrationsOnly,
109
+ applyPackagePositioning,
110
+ adoptAppLocalPackageDependencies,
111
+ loadAppPackageJson,
112
+ resolveLocalPackageId,
113
+ createLocalPackageScaffoldFiles,
114
+ fileExists,
115
+ applyPackageJsonField,
116
+ toFileDependencySpecifier,
117
+ writeJsonFile,
118
+ writeFile,
119
+ mkdir,
120
+ path,
121
+ inspectPackageOfferings,
122
+ buildFileWriteGroups,
123
+ listDeclaredCapabilities,
124
+ buildCapabilityDetailsForPackage,
125
+ formatPackageSubpathImport,
126
+ normalizePlacementOutlets,
127
+ normalizePlacementContributions,
128
+ shouldShowPackageExportTarget,
129
+ classifyExportedSymbols,
130
+ deriveProviderDisplayName,
131
+ restorePackageJsonField,
132
+ readFileBufferIfExists,
133
+ removeEnvValue,
134
+ removeManagedViteProxyEntries,
135
+ hashBuffer,
136
+ rm,
137
+ discoverShellOutletTargetsFromApp
138
+ })
139
+ );
140
+
141
+ const runCli = createRunCli({
142
+ parseArgs,
143
+ printUsage,
144
+ shouldShowCommandHelpOnBareInvocation,
145
+ commandHandlers,
146
+ cleanupMaterializedPackageRoots,
147
+ createCliError
148
+ });
149
+
150
+ export { runCli };
@@ -0,0 +1,43 @@
1
+ import { createCommandHandlerShared } from "../commandHandlers/shared.js";
2
+ import { createListCommands } from "../commandHandlers/list.js";
3
+ import { createShowCommand } from "../commandHandlers/show.js";
4
+ import { createPackageCommands } from "../commandHandlers/package.js";
5
+ import { createHealthCommands } from "../commandHandlers/health.js";
6
+
7
+ function createCommandHandlers(deps = {}) {
8
+ const shared = createCommandHandlerShared(deps);
9
+ const commandContext = {
10
+ ...deps,
11
+ ...shared
12
+ };
13
+
14
+ const { commandList, commandListPlacements } = createListCommands(commandContext);
15
+ const { commandShow } = createShowCommand(commandContext);
16
+ const {
17
+ commandCreate,
18
+ commandAdd,
19
+ commandGenerate,
20
+ commandUpdate,
21
+ commandMigrations,
22
+ commandPosition,
23
+ commandRemove
24
+ } = createPackageCommands(commandContext);
25
+ const { commandDoctor, commandLintDescriptors } = createHealthCommands(commandContext);
26
+
27
+ return {
28
+ commandList,
29
+ commandListPlacements,
30
+ commandShow,
31
+ commandCreate,
32
+ commandAdd,
33
+ commandGenerate,
34
+ commandMigrations,
35
+ commandPosition,
36
+ commandUpdate,
37
+ commandRemove,
38
+ commandDoctor,
39
+ commandLintDescriptors
40
+ };
41
+ }
42
+
43
+ export { createCommandHandlers };
@@ -1,6 +1,7 @@
1
1
  function createRunCli({
2
2
  parseArgs,
3
3
  printUsage,
4
+ shouldShowCommandHelpOnBareInvocation,
4
5
  commandHandlers,
5
6
  cleanupMaterializedPackageRoots,
6
7
  createCliError
@@ -11,6 +12,9 @@ function createRunCli({
11
12
  if (typeof printUsage !== "function") {
12
13
  throw new TypeError("createRunCli requires printUsage.");
13
14
  }
15
+ if (typeof shouldShowCommandHelpOnBareInvocation !== "function") {
16
+ throw new TypeError("createRunCli requires shouldShowCommandHelpOnBareInvocation.");
17
+ }
14
18
  if (!commandHandlers || typeof commandHandlers !== "object") {
15
19
  throw new TypeError("createRunCli requires commandHandlers.");
16
20
  }
@@ -30,7 +34,13 @@ function createRunCli({
30
34
  try {
31
35
  const { command, options, positional } = parseArgs(argv, { createCliError });
32
36
  if (options.help || command === "help") {
33
- printUsage(stdout);
37
+ const helpCommand = command === "help" ? String(positional[0] || "").trim() : command;
38
+ printUsage(stdout, { command: helpCommand });
39
+ return 0;
40
+ }
41
+
42
+ if (shouldShowCommandHelpOnBareInvocation(command, positional)) {
43
+ printUsage(stdout, { command });
34
44
  return 0;
35
45
  }
36
46
 
@@ -45,6 +55,9 @@ function createRunCli({
45
55
  if (command === "list") {
46
56
  return await commandHandlers.commandList({ positional, options, cwd, stdout });
47
57
  }
58
+ if (command === "list-placements") {
59
+ return await commandHandlers.commandListPlacements({ options, cwd, stdout });
60
+ }
48
61
  if (command === "show") {
49
62
  return await commandHandlers.commandShow({ positional, options, stdout });
50
63
  }
@@ -0,0 +1,344 @@
1
+ import { resolveCommandAlias } from "./commandCatalog.js";
2
+
3
+ const COMMAND_OVERVIEW = Object.freeze([
4
+ Object.freeze({
5
+ command: "create",
6
+ summary: "Scaffold an app-local runtime package."
7
+ }),
8
+ Object.freeze({
9
+ command: "add",
10
+ summary: "Install a runtime bundle or package into the current app."
11
+ }),
12
+ Object.freeze({
13
+ command: "generate",
14
+ summary: "Run a generator package (or generator subcommand)."
15
+ }),
16
+ Object.freeze({
17
+ command: "list",
18
+ summary: "List bundles, runtime packages, or generator packages."
19
+ }),
20
+ Object.freeze({
21
+ command: "list-placements",
22
+ summary: "List discovered UI placement targets."
23
+ }),
24
+ Object.freeze({
25
+ command: "show",
26
+ summary: "Show detailed metadata for a bundle or package."
27
+ }),
28
+ Object.freeze({
29
+ command: "migrations",
30
+ summary: "Generate managed migration files only."
31
+ }),
32
+ Object.freeze({
33
+ command: "position",
34
+ summary: "Re-apply positioning-only mutations for an installed package."
35
+ }),
36
+ Object.freeze({
37
+ command: "update",
38
+ summary: "Re-apply one installed package."
39
+ }),
40
+ Object.freeze({
41
+ command: "remove",
42
+ summary: "Remove one installed package."
43
+ }),
44
+ Object.freeze({
45
+ command: "doctor",
46
+ summary: "Validate lockfile and managed-file integrity."
47
+ }),
48
+ Object.freeze({
49
+ command: "lint-descriptors",
50
+ summary: "Validate bundle and package descriptor contracts."
51
+ })
52
+ ]);
53
+
54
+ const COMMAND_HELP = Object.freeze({
55
+ create: Object.freeze({
56
+ title: "create",
57
+ minimalUse: "jskit create package <name>",
58
+ parameters: Object.freeze([
59
+ Object.freeze({
60
+ name: "<name>",
61
+ description: "Local package slug used to scaffold packages/<name>."
62
+ })
63
+ ]),
64
+ defaults: Object.freeze([
65
+ "No npm install runs unless --run-npm-install is passed.",
66
+ "If --scope is omitted, scope is inferred from app name.",
67
+ "If --package-id is omitted, it is derived from scope + name."
68
+ ]),
69
+ fullUse: "jskit create package <name> [--scope <scope>] [--package-id <id>] [--description <text>] [--dry-run] [--run-npm-install] [--json]"
70
+ }),
71
+ add: Object.freeze({
72
+ title: "add",
73
+ minimalUse: "jskit add package <packageId>",
74
+ parameters: Object.freeze([
75
+ Object.freeze({
76
+ name: "package | bundle",
77
+ description: "Target type. Use package for one runtime package, bundle for a bundle id."
78
+ }),
79
+ Object.freeze({
80
+ name: "<packageId|bundleId>",
81
+ description: "Catalog id or installed node_modules package id."
82
+ })
83
+ ]),
84
+ defaults: Object.freeze([
85
+ "No npm install runs unless --run-npm-install is passed.",
86
+ "Short ids resolve to @jskit-ai/<id> when available.",
87
+ "Existing matching version is skipped unless options force reapply."
88
+ ]),
89
+ fullUse: "jskit add <package|bundle> <id> [--<option> <value>...] [--dry-run] [--run-npm-install] [--json] [--verbose]"
90
+ }),
91
+ generate: Object.freeze({
92
+ title: "generate",
93
+ minimalUse: "jskit generate <generatorId>",
94
+ parameters: Object.freeze([
95
+ Object.freeze({
96
+ name: "<generatorId>",
97
+ description: "Generator package id (for example: crud-ui-generator)."
98
+ }),
99
+ Object.freeze({
100
+ name: "[subcommand]",
101
+ description: "Optional generator subcommand (for example: add-field)."
102
+ }),
103
+ Object.freeze({
104
+ name: "[subcommand args...]",
105
+ description: "Optional positional args consumed by the chosen subcommand."
106
+ })
107
+ ]),
108
+ defaults: Object.freeze([
109
+ "No npm install runs unless --run-npm-install is passed.",
110
+ "Short ids resolve to @jskit-ai/<id> when available.",
111
+ "If no subcommand is provided, the generator primary command runs."
112
+ ]),
113
+ fullUse: "jskit generate <generatorId> [subcommand] [subcommand args...] [--<option> <value>...] [--dry-run] [--run-npm-install] [--json] [--verbose]"
114
+ }),
115
+ list: Object.freeze({
116
+ title: "list",
117
+ minimalUse: "jskit list",
118
+ parameters: Object.freeze([
119
+ Object.freeze({
120
+ name: "[mode]",
121
+ description: "Optional mode: bundles, packages, or generators."
122
+ })
123
+ ]),
124
+ defaults: Object.freeze([
125
+ "Without mode, list prints bundles + runtime packages + generators.",
126
+ "placements are listed by the dedicated list-placements command.",
127
+ "--full and --expanded only affect bundle/package listing views."
128
+ ]),
129
+ fullUse: "jskit list [bundles|packages|generators] [--full] [--expanded] [--json]"
130
+ }),
131
+ "list-placements": Object.freeze({
132
+ title: "list-placements",
133
+ minimalUse: "jskit list-placements",
134
+ parameters: Object.freeze([]),
135
+ defaults: Object.freeze([
136
+ "Discovers ShellOutlet targets from app src/ Vue files.",
137
+ "Includes placement outlets contributed by installed package metadata.",
138
+ "Shows plain text by default; use --json for structured output."
139
+ ]),
140
+ fullUse: "jskit list-placements [--json]"
141
+ }),
142
+ show: Object.freeze({
143
+ title: "show",
144
+ minimalUse: "jskit show <id>",
145
+ parameters: Object.freeze([
146
+ Object.freeze({
147
+ name: "<id>",
148
+ description: "Bundle id or package id to inspect."
149
+ })
150
+ ]),
151
+ defaults: Object.freeze([
152
+ "view is an alias of show.",
153
+ "Basic output is compact; --details expands capability and runtime sections.",
154
+ "--debug-exports implies --details."
155
+ ]),
156
+ fullUse: "jskit show <id> [--details] [--debug-exports] [--json]"
157
+ }),
158
+ migrations: Object.freeze({
159
+ title: "migrations",
160
+ minimalUse: "jskit migrations changed",
161
+ parameters: Object.freeze([
162
+ Object.freeze({
163
+ name: "<scope>",
164
+ description: "all | changed | package."
165
+ }),
166
+ Object.freeze({
167
+ name: "[packageId]",
168
+ description: "Required only when scope is package."
169
+ })
170
+ ]),
171
+ defaults: Object.freeze([
172
+ "No npm install runs unless --run-npm-install is passed.",
173
+ "Inline options are accepted only for 'migrations package <packageId>'.",
174
+ "Without --json, output lists touched migration files."
175
+ ]),
176
+ fullUse: "jskit migrations <all|changed|package> [packageId] [--<option> <value>...] [--dry-run] [--json] [--verbose]"
177
+ }),
178
+ position: Object.freeze({
179
+ title: "position",
180
+ minimalUse: "jskit position element <packageId>",
181
+ parameters: Object.freeze([
182
+ Object.freeze({
183
+ name: "element",
184
+ description: "Target type for positioning command."
185
+ }),
186
+ Object.freeze({
187
+ name: "<packageId>",
188
+ description: "Installed package id to re-position."
189
+ })
190
+ ]),
191
+ defaults: Object.freeze([
192
+ "Only positioning mutations are applied.",
193
+ "No npm install runs unless --run-npm-install is passed.",
194
+ "Reads current options from lock unless overridden inline."
195
+ ]),
196
+ fullUse: "jskit position element <packageId> [--<option> <value>...] [--dry-run] [--json]"
197
+ }),
198
+ update: Object.freeze({
199
+ title: "update",
200
+ minimalUse: "jskit update package <packageId>",
201
+ parameters: Object.freeze([
202
+ Object.freeze({
203
+ name: "package",
204
+ description: "Target type for update command."
205
+ }),
206
+ Object.freeze({
207
+ name: "<packageId>",
208
+ description: "Installed package id to re-apply."
209
+ })
210
+ ]),
211
+ defaults: Object.freeze([
212
+ "No npm install runs unless --run-npm-install is passed.",
213
+ "Existing lock options are reused unless overridden inline.",
214
+ "update reuses add package flow with forced reapply."
215
+ ]),
216
+ fullUse: "jskit update package <packageId> [--<option> <value>...] [--dry-run] [--run-npm-install] [--json]"
217
+ }),
218
+ remove: Object.freeze({
219
+ title: "remove",
220
+ minimalUse: "jskit remove package <packageId>",
221
+ parameters: Object.freeze([
222
+ Object.freeze({
223
+ name: "package",
224
+ description: "Target type for remove command."
225
+ }),
226
+ Object.freeze({
227
+ name: "<packageId>",
228
+ description: "Installed package id to remove."
229
+ })
230
+ ]),
231
+ defaults: Object.freeze([
232
+ "No npm install runs unless --run-npm-install is passed.",
233
+ "Managed files and lock entries are removed for the package.",
234
+ "Local package source directories are not deleted."
235
+ ]),
236
+ fullUse: "jskit remove package <packageId> [--dry-run] [--run-npm-install] [--json]"
237
+ }),
238
+ doctor: Object.freeze({
239
+ title: "doctor",
240
+ minimalUse: "jskit doctor",
241
+ parameters: Object.freeze([]),
242
+ defaults: Object.freeze([
243
+ "Validates lock entries, managed files, and registry visibility.",
244
+ "Reports issues as plain text by default.",
245
+ "Use --json for machine-readable diagnostics."
246
+ ]),
247
+ fullUse: "jskit doctor [--json]"
248
+ }),
249
+ "lint-descriptors": Object.freeze({
250
+ title: "lint-descriptors",
251
+ minimalUse: "jskit lint-descriptors",
252
+ parameters: Object.freeze([]),
253
+ defaults: Object.freeze([
254
+ "Runs descriptor consistency checks.",
255
+ "check-di-labels is optional and adds stricter DI token label checks.",
256
+ "Outputs plain text by default and supports --json."
257
+ ]),
258
+ fullUse: "jskit lint-descriptors [--check-di-labels] [--json]"
259
+ })
260
+ });
261
+
262
+ const BARE_COMMAND_HELP = new Set([
263
+ "create",
264
+ "add",
265
+ "generate",
266
+ "show",
267
+ "migrations",
268
+ "position",
269
+ "update",
270
+ "remove"
271
+ ]);
272
+
273
+ function writeLine(stream, line = "") {
274
+ stream.write(`${line}\n`);
275
+ }
276
+
277
+ function printTopLevelHelp(stream = process.stderr) {
278
+ writeLine(stream, "JSKit CLI");
279
+ writeLine(stream, "");
280
+ writeLine(stream, "Use: jskit help <command> for command-specific usage.");
281
+ writeLine(stream, "");
282
+ writeLine(stream, "Available commands:");
283
+ for (const entry of COMMAND_OVERVIEW) {
284
+ writeLine(stream, ` ${entry.command.padEnd(16, " ")} ${entry.summary}`);
285
+ }
286
+ writeLine(stream, "");
287
+ writeLine(stream, "Global flags:");
288
+ writeLine(stream, " --dry-run --run-npm-install --json --verbose --help");
289
+ }
290
+
291
+ function printCommandHelp(stream = process.stderr, command = "") {
292
+ const resolvedCommand = resolveCommandAlias(command);
293
+ const entry = COMMAND_HELP[resolvedCommand];
294
+ if (!entry) {
295
+ printTopLevelHelp(stream);
296
+ return;
297
+ }
298
+
299
+ writeLine(stream, `Command: ${entry.title}`);
300
+ writeLine(stream, "");
301
+
302
+ writeLine(stream, "1) Minimal use");
303
+ writeLine(stream, ` ${entry.minimalUse}`);
304
+ if (entry.parameters.length > 0) {
305
+ writeLine(stream, " Parameters:");
306
+ for (const parameter of entry.parameters) {
307
+ writeLine(stream, ` - ${parameter.name}: ${parameter.description}`);
308
+ }
309
+ }
310
+ writeLine(stream, "");
311
+
312
+ writeLine(stream, "2) Defaults");
313
+ for (const defaultLine of entry.defaults) {
314
+ writeLine(stream, ` - ${defaultLine}`);
315
+ }
316
+ writeLine(stream, "");
317
+
318
+ writeLine(stream, "3) Full use");
319
+ writeLine(stream, ` ${entry.fullUse}`);
320
+ }
321
+
322
+ function printUsage(stream = process.stderr, { command = "" } = {}) {
323
+ const normalizedCommand = String(command || "").trim();
324
+ if (!normalizedCommand) {
325
+ printTopLevelHelp(stream);
326
+ return;
327
+ }
328
+
329
+ printCommandHelp(stream, normalizedCommand);
330
+ }
331
+
332
+ function shouldShowCommandHelpOnBareInvocation(command = "", positional = []) {
333
+ const resolvedCommand = resolveCommandAlias(command);
334
+ const argumentList = Array.isArray(positional) ? positional : [];
335
+ if (!BARE_COMMAND_HELP.has(resolvedCommand)) {
336
+ return false;
337
+ }
338
+ return argumentList.length < 1;
339
+ }
340
+
341
+ export {
342
+ printUsage,
343
+ shouldShowCommandHelpOnBareInvocation
344
+ };
@@ -1 +1 @@
1
- export { runCli } from "./cliRuntime.js";
1
+ export { runCli } from "./core/createCliRunner.js";
@@ -187,7 +187,18 @@ function toPluralForm(value) {
187
187
  function normalizePathValue(value) {
188
188
  return String(value || "")
189
189
  .split("/")
190
- .map((segment) => wordsToKebab(splitTextIntoWords(segment)))
190
+ .map((segment) => {
191
+ const normalizedSegment = String(segment || "").trim();
192
+ if (!normalizedSegment) {
193
+ return "";
194
+ }
195
+
196
+ if (/^\[[^\]]+\]$/.test(normalizedSegment)) {
197
+ return normalizedSegment;
198
+ }
199
+
200
+ return wordsToKebab(splitTextIntoWords(normalizedSegment));
201
+ })
191
202
  .filter(Boolean)
192
203
  .join("/");
193
204
  }
@@ -5,7 +5,7 @@ import process from "node:process";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createCliError } from "./cliError.js";
7
7
 
8
- const CLI_PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
8
+ const CLI_PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
9
9
  const require = createRequire(import.meta.url);
10
10
 
11
11
  function resolveCatalogPackagesPath() {