@lenne.tech/cli 1.29.0 → 1.30.0

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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Tools = void 0;
4
4
  const fs_1 = require("fs");
5
+ const command_help_1 = require("../lib/command-help");
5
6
  const singleComment = Symbol('singleComment');
6
7
  const multiComment = Symbol('multiComment');
7
8
  const stripWithoutWhitespace = () => '';
@@ -27,6 +28,14 @@ class Tools {
27
28
  * Check if --help-json flag is set; if so, print the command definition as JSON and return true.
28
29
  * Commands should call this early and return immediately when it returns true.
29
30
  *
31
+ * In normal CLI usage the global `installHelpInterceptor` (see
32
+ * `src/lib/command-help.ts`) handles `--help-json` for ALL commands before
33
+ * their `run()` ever fires — this method is therefore mostly a no-op in
34
+ * production. It is still kept as a public escape hatch for tests that
35
+ * invoke a command's `run()` directly without the interceptor, and for
36
+ * legacy callers — output goes through the same `buildHelpJson` /
37
+ * `emitHelpJson` pair as the global path, so the schema cannot drift.
38
+ *
30
39
  * @param definition - The command's help definition (name, description, options, etc.)
31
40
  * @returns true if --help-json was handled (caller should return), false otherwise
32
41
  */
@@ -35,7 +44,7 @@ class Tools {
35
44
  if (!parameters.options['help-json'] && !parameters.options.helpJson) {
36
45
  return false;
37
46
  }
38
- console.debug(JSON.stringify(definition, null, 2));
47
+ (0, command_help_1.emitHelpJson)((0, command_help_1.buildHelpJson)({ description: definition.description, name: definition.name }, definition));
39
48
  return true;
40
49
  }
41
50
  /**
@@ -18,12 +18,16 @@
18
18
  */
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.installHelpInterceptor = installHelpInterceptor;
21
+ exports.isHelpJsonRequested = isHelpJsonRequested;
21
22
  exports.isHelpRequested = isHelpRequested;
22
23
  exports.loadCommandHelp = loadCommandHelp;
23
24
  exports.renderCommandHelp = renderCommandHelp;
25
+ exports.buildHelpJson = buildHelpJson;
26
+ exports.emitHelpJson = emitHelpJson;
24
27
  /**
25
- * Wrap every command's `run` so that `--help` / `-h` prints help and returns
26
- * without executing. Call once after `build().create()`, before `cli.run()`.
28
+ * Wrap every command's `run` so that `--help` / `-h` and `--help-json` print
29
+ * help and return without executing. Call once after `build().create()`,
30
+ * before `cli.run()`.
27
31
  *
28
32
  * Idempotent per command (guarded by `__helpWrapped`), so a command is never
29
33
  * double-wrapped if this runs more than once in the same process (e.g. tests).
@@ -43,6 +47,10 @@ function installHelpInterceptor(commands, defaultCommand) {
43
47
  }
44
48
  const originalRun = command.run;
45
49
  command.run = (toolbox) => {
50
+ if (isHelpJsonRequested(toolbox === null || toolbox === void 0 ? void 0 : toolbox.parameters)) {
51
+ emitHelpJson(buildHelpJson(command, loadCommandHelp(command.file)));
52
+ return undefined;
53
+ }
46
54
  if ((toolbox === null || toolbox === void 0 ? void 0 : toolbox.print) && isHelpRequested(toolbox.parameters)) {
47
55
  renderCommandHelp(toolbox.print, command, loadCommandHelp(command.file));
48
56
  return undefined;
@@ -52,10 +60,12 @@ function installHelpInterceptor(commands, defaultCommand) {
52
60
  command.__helpWrapped = true;
53
61
  }
54
62
  }
55
- /**
56
- * True when the invocation asks for help (`--help` or `-h`) — but NOT for
57
- * `--help-json` (handled separately by `tools.helpJson`).
58
- */
63
+ /** True when the invocation asks for machine-readable help (`--help-json`). */
64
+ function isHelpJsonRequested(parameters) {
65
+ const options = (parameters === null || parameters === void 0 ? void 0 : parameters.options) || {};
66
+ return options['help-json'] === true || options.helpJson === true;
67
+ }
68
+ /** True when the invocation asks for human-readable help (`--help` or `-h`). */
59
69
  function isHelpRequested(parameters) {
60
70
  const options = (parameters === null || parameters === void 0 ? void 0 : parameters.options) || {};
61
71
  if (options['help-json'] === true || options.helpJson === true) {
@@ -132,7 +142,7 @@ function renderCommandHelp(print, command, help) {
132
142
  }
133
143
  // Always-present global flags
134
144
  print.info(` ${'--help, -h'.padEnd(28)} Show this help and exit (does not run the command)`);
135
- print.info(` ${'--help-json'.padEnd(28)} Machine-readable help as JSON (where provided)`);
145
+ print.info(` ${'--help-json'.padEnd(28)} Machine-readable help as JSON (does not run the command)`);
136
146
  if (!options.some((o) => o.flag === '--noConfirm')) {
137
147
  print.info(` ${'--noConfirm'.padEnd(28)} Skip confirmation prompts (where supported)`);
138
148
  }
@@ -159,3 +169,41 @@ function usagePath(command) {
159
169
  const path = command.commandPath && command.commandPath.length ? command.commandPath : [command.name || ''];
160
170
  return `lt ${path.filter(Boolean).join(' ')}`.trim();
161
171
  }
172
+ const GLOBAL_HELP_FLAGS = [
173
+ { description: 'Show human-readable help; the command is NOT executed.', flag: '--help', type: 'boolean' },
174
+ { description: 'Alias for --help.', flag: '-h', type: 'boolean' },
175
+ {
176
+ description: 'Print this JSON description on stdout; the command is NOT executed.',
177
+ flag: '--help-json',
178
+ type: 'boolean',
179
+ },
180
+ ];
181
+ /**
182
+ * Build the JSON shape returned by `--help-json` for a command, merging the
183
+ * gluegun-known metadata (name, commandPath, description, aliases) with the
184
+ * command's optional rich `CommandHelp` export.
185
+ */
186
+ function buildHelpJson(command, help) {
187
+ const aliases = aliasList(command, help);
188
+ return {
189
+ aliases,
190
+ command: usagePath(command),
191
+ configuration: help === null || help === void 0 ? void 0 : help.configuration,
192
+ description: (help === null || help === void 0 ? void 0 : help.description) || command.description || '',
193
+ examples: help === null || help === void 0 ? void 0 : help.examples,
194
+ features: help === null || help === void 0 ? void 0 : help.features,
195
+ globalFlags: GLOBAL_HELP_FLAGS,
196
+ name: (help === null || help === void 0 ? void 0 : help.name) || command.name || '',
197
+ options: (help === null || help === void 0 ? void 0 : help.options) || [],
198
+ richHelp: Boolean(help),
199
+ };
200
+ }
201
+ /**
202
+ * Emit a help-json payload to stdout as a single pretty-printed JSON document.
203
+ * Kept tiny + side-effect-only so it can be stubbed in tests via a captured
204
+ * `console.log`.
205
+ */
206
+ function emitHelpJson(payload) {
207
+ // eslint-disable-next-line no-console
208
+ console.log(JSON.stringify(payload, null, 2));
209
+ }
package/docs/commands.md CHANGED
@@ -10,6 +10,7 @@ This document provides a comprehensive reference for all `lt` CLI commands. For
10
10
 
11
11
  ## Table of Contents
12
12
 
13
+ - [Global Flags](#global-flags)
13
14
  - [CLI Commands](#cli-commands)
14
15
  - [Server Commands](#server-commands)
15
16
  - [Local Development Commands](#local-development-commands)
@@ -32,6 +33,44 @@ This document provides a comprehensive reference for all `lt` CLI commands. For
32
33
 
33
34
  ---
34
35
 
36
+ ## Global Flags
37
+
38
+ These flags work on **every** `lt` subcommand. They are intercepted before the command's `run()` fires, so the command is never executed when one of them is set.
39
+
40
+ | Flag | Description |
41
+ |--------|-------------|
42
+ | `--help`, `-h` | Print human-readable help (usage, aliases, options, examples). |
43
+ | `--help-json` | Print the same help as a single JSON document on stdout. Stable contract — see shape below. Intended for AI agents and tooling that want to discover a command's surface programmatically. |
44
+ | `--noConfirm` | Skip interactive confirmations (where supported by the command). |
45
+
46
+ **`--help-json` payload shape** (`HelpJsonShape` in [src/lib/command-help.ts](../src/lib/command-help.ts)):
47
+
48
+ ```jsonc
49
+ {
50
+ "aliases": ["c"],
51
+ "command": "lt server create",
52
+ "configuration": "commands.server.create.*",
53
+ "description": "Create new server",
54
+ "examples": ["server create --name Foo --noConfirm"],
55
+ "features": ["..."],
56
+ "globalFlags": [
57
+ { "flag": "--help", "type": "boolean", "description": "..." },
58
+ { "flag": "-h", "type": "boolean", "description": "..." },
59
+ { "flag": "--help-json", "type": "boolean", "description": "..." }
60
+ ],
61
+ "name": "create",
62
+ "options": [
63
+ { "flag": "--name", "type": "string", "required": true, "description": "Server name" }
64
+ ],
65
+ "richHelp": true
66
+ }
67
+ ```
68
+
69
+ - `richHelp: true` means the command exported a typed `CommandHelp` — `options`, `features`, `examples` and `configuration` are authoritative.
70
+ - `richHelp: false` means only gluegun metadata was available — `options` is the empty array, but `globalFlags` is still guaranteed.
71
+
72
+ ---
73
+
35
74
  ## CLI Commands
36
75
 
37
76
  ### `lt cli create`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/cli",
3
- "version": "1.29.0",
3
+ "version": "1.30.0",
4
4
  "description": "lenne.Tech CLI: lt",
5
5
  "keywords": [
6
6
  "lenne.Tech",