politty 0.0.1 → 0.1.1

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 (56) hide show
  1. package/README.md +297 -28
  2. package/dist/arg-registry-ClI2WGgH.d.cts +89 -0
  3. package/dist/arg-registry-ClI2WGgH.d.cts.map +1 -0
  4. package/dist/arg-registry-D4NsqcNZ.d.ts +89 -0
  5. package/dist/arg-registry-D4NsqcNZ.d.ts.map +1 -0
  6. package/dist/augment.cjs +0 -0
  7. package/dist/augment.d.cts +17 -0
  8. package/dist/augment.d.cts.map +1 -0
  9. package/dist/augment.d.ts +17 -0
  10. package/dist/augment.d.ts.map +1 -0
  11. package/dist/augment.js +1 -0
  12. package/dist/command-Bgd-yIwv.cjs +25 -0
  13. package/dist/command-Bgd-yIwv.cjs.map +1 -0
  14. package/dist/command-CvKyk4ag.js +20 -0
  15. package/dist/command-CvKyk4ag.js.map +1 -0
  16. package/dist/completion/index.cjs +595 -0
  17. package/dist/completion/index.cjs.map +1 -0
  18. package/dist/completion/index.d.cts +153 -0
  19. package/dist/completion/index.d.cts.map +1 -0
  20. package/dist/completion/index.d.ts +153 -0
  21. package/dist/completion/index.d.ts.map +1 -0
  22. package/dist/completion/index.js +588 -0
  23. package/dist/completion/index.js.map +1 -0
  24. package/dist/docs/index.cjs +1239 -0
  25. package/dist/docs/index.cjs.map +1 -0
  26. package/dist/docs/index.d.cts +500 -0
  27. package/dist/docs/index.d.cts.map +1 -0
  28. package/dist/docs/index.d.ts +500 -0
  29. package/dist/docs/index.d.ts.map +1 -0
  30. package/dist/docs/index.js +1182 -0
  31. package/dist/docs/index.js.map +1 -0
  32. package/dist/index.cjs +29 -0
  33. package/dist/index.d.cts +478 -0
  34. package/dist/index.d.cts.map +1 -0
  35. package/dist/index.d.ts +478 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +5 -0
  38. package/dist/runner-BZuYiRhi.cjs +1492 -0
  39. package/dist/runner-BZuYiRhi.cjs.map +1 -0
  40. package/dist/runner-D2BXiWtg.cjs +4 -0
  41. package/dist/runner-DceWXOwD.js +1372 -0
  42. package/dist/runner-DceWXOwD.js.map +1 -0
  43. package/dist/runner-KCql2UKz.js +4 -0
  44. package/dist/schema-extractor-B9D3Rf22.cjs +354 -0
  45. package/dist/schema-extractor-B9D3Rf22.cjs.map +1 -0
  46. package/dist/schema-extractor-D-Eo7I77.d.cts +303 -0
  47. package/dist/schema-extractor-D-Eo7I77.d.cts.map +1 -0
  48. package/dist/schema-extractor-Dk5Z0Iei.js +324 -0
  49. package/dist/schema-extractor-Dk5Z0Iei.js.map +1 -0
  50. package/dist/schema-extractor-kkajLb9E.d.ts +303 -0
  51. package/dist/schema-extractor-kkajLb9E.d.ts.map +1 -0
  52. package/dist/subcommand-router-BiSvDXHg.js +153 -0
  53. package/dist/subcommand-router-BiSvDXHg.js.map +1 -0
  54. package/dist/subcommand-router-Vf-0w9P4.cjs +189 -0
  55. package/dist/subcommand-router-Vf-0w9P4.cjs.map +1 -0
  56. package/package.json +108 -6
@@ -0,0 +1,1372 @@
1
+ import { n as getExtractedFields, t as extractFields } from "./schema-extractor-Dk5Z0Iei.js";
2
+ import { a as emptyLogs, i as createLogCollector, n as resolveLazyCommand, o as mergeLogs, r as resolveSubcommand, t as listSubCommands } from "./subcommand-router-BiSvDXHg.js";
3
+ import { z } from "zod";
4
+ import { styleText } from "node:util";
5
+
6
+ //#region src/executor/command-runner.ts
7
+ /**
8
+ * Execute a command lifecycle: setup → run → cleanup
9
+ *
10
+ * This is an internal function that executes the command's lifecycle hooks.
11
+ * For running commands with argument parsing, use `runCommand` instead.
12
+ *
13
+ * @param command - The command to execute
14
+ * @param args - Already validated arguments
15
+ * @param options - Lifecycle options
16
+ * @returns The result of command execution
17
+ * @internal
18
+ */
19
+ async function executeLifecycle(command, args, _options = {}) {
20
+ let error;
21
+ let result;
22
+ const collector = _options.captureLogs ?? false ? createLogCollector() : null;
23
+ collector?.start();
24
+ const setupContext = { args };
25
+ const cleanupContext = {
26
+ args,
27
+ error
28
+ };
29
+ let signalHandler;
30
+ if (_options.handleSignals) {
31
+ signalHandler = async (_signal) => {
32
+ if (signalHandler) {
33
+ process.off("SIGINT", signalHandler);
34
+ process.off("SIGTERM", signalHandler);
35
+ }
36
+ if (command.cleanup) try {
37
+ await command.cleanup(cleanupContext);
38
+ } catch (e) {
39
+ console.error("Error during signal cleanup:", e);
40
+ }
41
+ collector?.stop();
42
+ process.exit(1);
43
+ };
44
+ process.on("SIGINT", signalHandler);
45
+ process.on("SIGTERM", signalHandler);
46
+ }
47
+ try {
48
+ if (command.setup) await command.setup(setupContext);
49
+ if (command.run) result = await command.run(args);
50
+ } catch (e) {
51
+ error = e instanceof Error ? e : new Error(String(e));
52
+ } finally {
53
+ if (signalHandler) {
54
+ process.off("SIGINT", signalHandler);
55
+ process.off("SIGTERM", signalHandler);
56
+ }
57
+ }
58
+ if (command.cleanup) {
59
+ cleanupContext.error = error;
60
+ try {
61
+ await command.cleanup(cleanupContext);
62
+ } catch (cleanupError) {
63
+ if (!error) error = cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError));
64
+ }
65
+ }
66
+ collector?.stop();
67
+ const logs = mergeLogs(_options.existingLogs ?? emptyLogs(), collector?.getLogs() ?? emptyLogs());
68
+ if (error) return {
69
+ success: false,
70
+ error,
71
+ exitCode: 1,
72
+ logs
73
+ };
74
+ return {
75
+ success: true,
76
+ result,
77
+ exitCode: 0,
78
+ logs
79
+ };
80
+ }
81
+
82
+ //#endregion
83
+ //#region src/output/logger.ts
84
+ /**
85
+ * Check if color output should be disabled
86
+ */
87
+ function shouldDisableColor() {
88
+ if (process.env.NO_COLOR !== void 0) return true;
89
+ if (process.env.FORCE_COLOR === "0") return true;
90
+ if (process.env.FORCE_COLOR) return false;
91
+ if (process.env.CI) return true;
92
+ if (!process.stdout.isTTY) return true;
93
+ return false;
94
+ }
95
+ /**
96
+ * Global flag to control color output
97
+ */
98
+ let colorDisabled = shouldDisableColor();
99
+ /**
100
+ * Enable or disable color output programmatically
101
+ */
102
+ function setColorEnabled(enabled) {
103
+ colorDisabled = !enabled;
104
+ }
105
+ /**
106
+ * Check if color output is currently enabled
107
+ */
108
+ function isColorEnabled() {
109
+ return !colorDisabled;
110
+ }
111
+ /**
112
+ * Create a style function that applies the given styles
113
+ */
114
+ function createStyleFn(...styleArgs) {
115
+ return (text) => {
116
+ if (colorDisabled) return text;
117
+ let result = text;
118
+ for (const style of styleArgs) result = styleText(style, result);
119
+ return result;
120
+ };
121
+ }
122
+ /**
123
+ * Semantic style functions for inline text styling
124
+ */
125
+ const styles = {
126
+ success: createStyleFn("green"),
127
+ error: createStyleFn("red"),
128
+ warning: createStyleFn("yellow"),
129
+ info: createStyleFn("cyan"),
130
+ bold: createStyleFn("bold"),
131
+ dim: createStyleFn("dim"),
132
+ italic: createStyleFn("italic"),
133
+ underline: createStyleFn("underline"),
134
+ red: createStyleFn("red"),
135
+ green: createStyleFn("green"),
136
+ yellow: createStyleFn("yellow"),
137
+ blue: createStyleFn("blue"),
138
+ magenta: createStyleFn("magenta"),
139
+ cyan: createStyleFn("cyan"),
140
+ white: createStyleFn("white"),
141
+ gray: createStyleFn("gray"),
142
+ command: createStyleFn("bold"),
143
+ commandName: createStyleFn("bold", "cyan"),
144
+ option: createStyleFn("cyan"),
145
+ optionName: createStyleFn("bold"),
146
+ placeholder: createStyleFn("dim"),
147
+ defaultValue: createStyleFn("dim"),
148
+ required: createStyleFn("yellow"),
149
+ description: (text) => text,
150
+ sectionHeader: createStyleFn("bold", "underline"),
151
+ version: createStyleFn("dim")
152
+ };
153
+ /**
154
+ * Standardized symbols for CLI output
155
+ */
156
+ const symbols = {
157
+ success: styles.green("✓"),
158
+ error: styles.red("✖"),
159
+ warning: styles.yellow("⚠"),
160
+ info: styles.cyan("ℹ"),
161
+ bullet: styles.gray("•"),
162
+ arrow: styles.gray("→")
163
+ };
164
+ /**
165
+ * Logger for CLI output
166
+ */
167
+ const logger = {
168
+ info(message) {
169
+ console.log(message);
170
+ },
171
+ success(message) {
172
+ console.log(`${symbols.success} ${styles.success(message)}`);
173
+ },
174
+ warn(message) {
175
+ console.warn(`${symbols.warning} ${styles.warning(message)}`);
176
+ },
177
+ error(message) {
178
+ console.error(`${symbols.error} ${styles.error(message)}`);
179
+ },
180
+ log(message) {
181
+ console.log(message);
182
+ },
183
+ newline() {
184
+ console.log("");
185
+ },
186
+ debug(message) {
187
+ console.log(styles.dim(message));
188
+ }
189
+ };
190
+
191
+ //#endregion
192
+ //#region src/output/help-generator.ts
193
+ /**
194
+ * Default descriptions for built-in options
195
+ */
196
+ const defaultBuiltinDescriptions = {
197
+ help: "Show help",
198
+ helpAll: "Show help with all subcommand options",
199
+ version: "Show version"
200
+ };
201
+ /**
202
+ * Build full command name from context
203
+ */
204
+ function buildFullCommandName(command, context) {
205
+ if (context?.rootName && context.commandPath && context.commandPath.length > 0) return context.commandPath.join(" ");
206
+ return command.name ?? "command";
207
+ }
208
+ /**
209
+ * Build usage command name (includes root name for subcommands)
210
+ */
211
+ function buildUsageCommandName(command, context) {
212
+ if (context?.rootName && context.commandPath && context.commandPath.length > 0) return `${context.rootName} ${context.commandPath.join(" ")}`;
213
+ return command.name ?? "command";
214
+ }
215
+ /**
216
+ * Render the usage line for a command
217
+ */
218
+ function renderUsageLine(command, context) {
219
+ const parts = [];
220
+ const name = buildUsageCommandName(command, context);
221
+ parts.push(styles.commandName(name));
222
+ const extracted = getExtractedFields(command);
223
+ if (extracted) {
224
+ const positionals = extracted.fields.filter((a) => a.positional);
225
+ if (extracted.fields.filter((a) => !a.positional).length > 0) parts.push(styles.placeholder("[options]"));
226
+ if (command.subCommands && Object.keys(command.subCommands).length > 0) parts.push(styles.placeholder("[command]"));
227
+ for (const arg of positionals) if (arg.required) parts.push(styles.option(`<${arg.name}>`));
228
+ else parts.push(styles.placeholder(`[${arg.name}]`));
229
+ } else if (command.subCommands && Object.keys(command.subCommands).length > 0) parts.push(styles.placeholder("[command]"));
230
+ return parts.join(" ");
231
+ }
232
+ /**
233
+ * Render the options section
234
+ */
235
+ function renderOptions(command, descriptions = {}, context) {
236
+ const lines = [];
237
+ const desc = {
238
+ help: descriptions.help ?? defaultBuiltinDescriptions.help,
239
+ helpAll: descriptions.helpAll ?? defaultBuiltinDescriptions.helpAll,
240
+ version: descriptions.version ?? defaultBuiltinDescriptions.version
241
+ };
242
+ const extracted = getExtractedFields(command);
243
+ const hasUserDefinedh = extracted?.fields.some((f) => f.alias === "h" && f.overrideBuiltinAlias === true) ?? false;
244
+ const hasUserDefinedH = extracted?.fields.some((f) => f.alias === "H" && f.overrideBuiltinAlias === true) ?? false;
245
+ if (hasUserDefinedh) lines.push(formatOption(styles.option("--help"), desc.help));
246
+ else lines.push(formatOption(`${styles.option("-h")}, ${styles.option("--help")}`, desc.help));
247
+ if (hasUserDefinedH) lines.push(formatOption(styles.option("--help-all"), desc.helpAll));
248
+ else lines.push(formatOption(`${styles.option("-H")}, ${styles.option("--help-all")}`, desc.helpAll));
249
+ if (context?.rootVersion) lines.push(formatOption(styles.option("--version"), desc.version));
250
+ if (!extracted) return lines.join("\n");
251
+ if (extracted.schemaType === "discriminatedUnion" && extracted.discriminator) return renderDiscriminatedUnionOptions(extracted, command, lines);
252
+ if (extracted.schemaType === "union" && extracted.unionOptions) return renderUnionOptions(extracted, command, lines);
253
+ if (extracted.schemaType === "xor" && extracted.unionOptions) return renderUnionOptions(extracted, command, lines);
254
+ const options = extracted.fields.filter((a) => !a.positional);
255
+ for (const opt of options) {
256
+ const flags = formatFlags(opt);
257
+ let desc$1 = opt.description ?? "";
258
+ if (opt.defaultValue !== void 0) desc$1 += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
259
+ if (opt.required) desc$1 += ` ${styles.required("(required)")}`;
260
+ const envInfo = formatEnvInfo(opt.env);
261
+ if (envInfo) desc$1 += ` ${envInfo}`;
262
+ lines.push(formatOption(flags, desc$1));
263
+ }
264
+ return lines.join("\n");
265
+ }
266
+ /**
267
+ * Render options for discriminated union with variants
268
+ */
269
+ function renderDiscriminatedUnionOptions(extracted, _command, lines) {
270
+ const discriminator = extracted.discriminator;
271
+ const variants = extracted.variants ?? [];
272
+ const discriminatorField = extracted.fields.find((f) => f.name === discriminator);
273
+ if (discriminatorField) {
274
+ const variantValues = variants.map((v) => v.discriminatorValue).join("|");
275
+ const flags = `${styles.option(`--${discriminator}`)} ${styles.placeholder(`<${variantValues}>`)}`;
276
+ const description = extracted.description ?? discriminatorField.description ?? "Action to perform";
277
+ lines.push(formatOption(flags, description));
278
+ }
279
+ const commonFields = /* @__PURE__ */ new Set();
280
+ const allFieldNames = /* @__PURE__ */ new Set();
281
+ for (const variant of variants) for (const field of variant.fields) allFieldNames.add(field.name);
282
+ for (const fieldName of allFieldNames) {
283
+ if (fieldName === discriminator) continue;
284
+ if (variants.every((v) => v.fields.some((f) => f.name === fieldName))) commonFields.add(fieldName);
285
+ }
286
+ for (const fieldName of commonFields) {
287
+ const field = extracted.fields.find((f) => f.name === fieldName);
288
+ if (field && !field.positional) {
289
+ const flags = formatFlags(field);
290
+ let desc = field.description ?? "";
291
+ if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
292
+ const envInfo = formatEnvInfo(field.env);
293
+ if (envInfo) desc += ` ${envInfo}`;
294
+ lines.push(formatOption(flags, desc));
295
+ }
296
+ }
297
+ for (const variant of variants) {
298
+ const variantFields = variant.fields.filter((f) => f.name !== discriminator && !commonFields.has(f.name) && !f.positional);
299
+ if (variantFields.length > 0) {
300
+ lines.push("");
301
+ const variantLabel = variant.description ? `${styles.dim("When")} ${styles.option(discriminator)}=${styles.bold(variant.discriminatorValue)}: ${variant.description}` : `${styles.dim("When")} ${styles.option(discriminator)}=${styles.bold(variant.discriminatorValue)}:`;
302
+ lines.push(variantLabel);
303
+ for (const field of variantFields) {
304
+ const flags = formatFlags(field);
305
+ let desc = field.description ?? "";
306
+ if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
307
+ if (field.required) desc += ` ${styles.required("(required)")}`;
308
+ const envInfo = formatEnvInfo(field.env);
309
+ if (envInfo) desc += ` ${envInfo}`;
310
+ lines.push(formatOption(` ${flags}`, desc));
311
+ }
312
+ }
313
+ }
314
+ return lines.join("\n");
315
+ }
316
+ /**
317
+ * Render options for union with multiple options
318
+ */
319
+ function renderUnionOptions(extracted, _command, lines) {
320
+ const unionOptions = extracted.unionOptions ?? [];
321
+ const commonFields = /* @__PURE__ */ new Set();
322
+ const allFieldNames = /* @__PURE__ */ new Set();
323
+ for (const option of unionOptions) for (const field of option.fields) allFieldNames.add(field.name);
324
+ for (const fieldName of allFieldNames) if (unionOptions.every((o) => o.fields.some((f) => f.name === fieldName))) commonFields.add(fieldName);
325
+ for (const fieldName of commonFields) {
326
+ const field = extracted.fields.find((f) => f.name === fieldName);
327
+ if (field && !field.positional) {
328
+ const flags = formatFlags(field);
329
+ let desc = field.description ?? "";
330
+ if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
331
+ const envInfo = formatEnvInfo(field.env);
332
+ if (envInfo) desc += ` ${envInfo}`;
333
+ lines.push(formatOption(flags, desc));
334
+ }
335
+ }
336
+ for (let i = 0; i < unionOptions.length; i++) {
337
+ const option = unionOptions[i];
338
+ if (!option) continue;
339
+ const uniqueFields = option.fields.filter((f) => !commonFields.has(f.name) && !f.positional);
340
+ if (uniqueFields.length > 0) {
341
+ lines.push("");
342
+ const label = option.description ?? `Variant ${i + 1}`;
343
+ lines.push(` ${styles.bold(`${label}:`)}`);
344
+ for (const field of uniqueFields) {
345
+ const flags = formatFlags(field);
346
+ let desc = field.description ?? "";
347
+ if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
348
+ if (field.required) desc += ` ${styles.required("(required)")}`;
349
+ const envInfo = formatEnvInfo(field.env);
350
+ if (envInfo) desc += ` ${envInfo}`;
351
+ lines.push(formatOption(` ${flags}`, desc));
352
+ }
353
+ }
354
+ }
355
+ return lines.join("\n");
356
+ }
357
+ /**
358
+ * Format option flags (-v, --verbose <VALUE>)
359
+ * Uses cliName (kebab-case) for display
360
+ */
361
+ function formatFlags(opt) {
362
+ const parts = [];
363
+ if (opt.alias) parts.push(styles.option(`-${opt.alias}`));
364
+ let longFlag = styles.option(`--${opt.cliName}`);
365
+ if (opt.type !== "boolean") {
366
+ const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
367
+ longFlag += ` ${styles.placeholder(`<${placeholder}>`)}`;
368
+ }
369
+ parts.push(longFlag);
370
+ return parts.join(", ");
371
+ }
372
+ /**
373
+ * Format environment variable info for help display
374
+ */
375
+ function formatEnvInfo(env) {
376
+ if (!env) return "";
377
+ const envNames = Array.isArray(env) ? env : [env];
378
+ return styles.dim(`[env: ${envNames.join(", ")}]`);
379
+ }
380
+ /**
381
+ * Strip ANSI escape codes from a string to get visual length
382
+ */
383
+ function stripAnsi(str) {
384
+ return str.replace(/\x1B\[[0-9;]*m/g, "");
385
+ }
386
+ /**
387
+ * Pad a string that may contain ANSI codes to a visual width
388
+ */
389
+ function padEndVisual(str, width) {
390
+ const visualLength = stripAnsi(str).length;
391
+ const padding = Math.max(0, width - visualLength);
392
+ return str + " ".repeat(padding);
393
+ }
394
+ /**
395
+ * Format a single option line
396
+ * If flags exceed the column width, description is moved to the next line
397
+ */
398
+ function formatOption(flags, description, indent = 0, extraDescPadding = 0) {
399
+ const flagWidth = 32;
400
+ const indentStr = " ".repeat(indent);
401
+ const visualFlagLength = stripAnsi(flags).length;
402
+ const effectiveFlagWidth = flagWidth - indent * 2 + extraDescPadding;
403
+ if (visualFlagLength >= effectiveFlagWidth) return `${indentStr} ${flags}\n${" ".repeat(effectiveFlagWidth + 2 + indent * 2)}${description}`;
404
+ return `${indentStr} ${padEndVisual(flags, effectiveFlagWidth)}${description}`;
405
+ }
406
+ /**
407
+ * Render options for a subcommand (used by showSubcommandOptions)
408
+ */
409
+ function renderSubcommandOptionsCompact(command, indent) {
410
+ const lines = [];
411
+ const extracted = getExtractedFields(command);
412
+ if (extracted) {
413
+ const options = extracted.fields.filter((a) => !a.positional);
414
+ for (const opt of options) {
415
+ const flags = formatFlags(opt);
416
+ let desc = opt.description ?? "";
417
+ if (opt.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
418
+ const envInfo = formatEnvInfo(opt.env);
419
+ if (envInfo) desc += ` ${envInfo}`;
420
+ lines.push(formatOption(flags, desc, indent, 2));
421
+ }
422
+ }
423
+ return lines;
424
+ }
425
+ /**
426
+ * Render subcommands recursively with their options (flat style)
427
+ */
428
+ function renderSubcommandsWithOptions(subCommands, parentPath, baseIndent) {
429
+ const lines = [];
430
+ for (const [name, subCmd] of Object.entries(subCommands)) {
431
+ const cmd = typeof subCmd === "function" ? null : subCmd;
432
+ const fullPath = parentPath ? `${parentPath} ${name}` : name;
433
+ const desc = cmd?.description ?? "";
434
+ lines.push(formatOption(styles.command(fullPath), desc, baseIndent));
435
+ if (cmd) {
436
+ const optionLines = renderSubcommandOptionsCompact(cmd, baseIndent + 1);
437
+ lines.push(...optionLines);
438
+ if (cmd.subCommands && Object.keys(cmd.subCommands).length > 0) {
439
+ const nestedLines = renderSubcommandsWithOptions(cmd.subCommands, fullPath, baseIndent);
440
+ lines.push(...nestedLines);
441
+ }
442
+ }
443
+ }
444
+ return lines;
445
+ }
446
+ /**
447
+ * Generate help text for a command
448
+ *
449
+ * @param command - The command to generate help for
450
+ * @param options - Help generation options
451
+ * @returns Formatted help text
452
+ */
453
+ function generateHelp(command, options) {
454
+ const sections = [];
455
+ const context = options.context;
456
+ const headerLines = [];
457
+ const displayName = buildFullCommandName(command, context);
458
+ if (displayName) {
459
+ let header = styles.commandName(displayName);
460
+ if (context?.rootName && context.commandPath && context.commandPath.length > 0) if (context.rootVersion) header += ` ${styles.version(`(${context.rootName} v${context.rootVersion})`)}`;
461
+ else header += ` ${styles.version(`(${context.rootName})`)}`;
462
+ else if (context?.rootVersion) header += ` ${styles.version(`v${context.rootVersion}`)}`;
463
+ headerLines.push(header);
464
+ }
465
+ if (command.description) headerLines.push(command.description);
466
+ if (headerLines.length > 0) sections.push(headerLines.join("\n"));
467
+ sections.push(`${styles.sectionHeader("Usage:")} ${renderUsageLine(command, context)}`);
468
+ const optionsText = renderOptions(command, options.descriptions, context);
469
+ if (optionsText) sections.push(`${styles.sectionHeader("Options:")}\n${optionsText}`);
470
+ if (options.showSubcommands !== false && command.subCommands && Object.keys(command.subCommands).length > 0) {
471
+ const currentPath = context?.commandPath?.join(" ") ?? "";
472
+ if (options.showSubcommandOptions) {
473
+ const subLines = renderSubcommandsWithOptions(command.subCommands, currentPath, 0);
474
+ sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
475
+ } else {
476
+ const subLines = [];
477
+ for (const [name, subCmd] of Object.entries(command.subCommands)) {
478
+ const desc = (typeof subCmd === "function" ? { description: void 0 } : subCmd).description ?? "";
479
+ const fullName = currentPath ? `${currentPath} ${name}` : name;
480
+ subLines.push(formatOption(styles.command(fullName), desc));
481
+ }
482
+ sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
483
+ }
484
+ }
485
+ if (command.examples && command.examples.length > 0) {
486
+ const exampleLines = renderExamplesForHelp(command.examples, context);
487
+ sections.push(`${styles.sectionHeader("Examples:")}\n${exampleLines}`);
488
+ }
489
+ if (command.notes) sections.push(`${styles.sectionHeader("Notes:")}\n${command.notes}`);
490
+ return sections.join("\n\n");
491
+ }
492
+ /**
493
+ * Render examples for CLI help output
494
+ */
495
+ function renderExamplesForHelp(examples, context) {
496
+ const lines = [];
497
+ const cmdPrefix = context?.rootName ? `${context.rootName} ` : "";
498
+ const cmdPath = context?.commandPath?.join(" ") ?? "";
499
+ const fullPrefix = cmdPath ? `${cmdPrefix}${cmdPath} ` : cmdPrefix;
500
+ for (const example of examples) {
501
+ lines.push(` ${styles.dim(example.desc)}`);
502
+ lines.push(` ${styles.dim("$")} ${fullPrefix}${example.cmd}`);
503
+ if (example.output) for (const line of example.output.split("\n")) lines.push(` ${line}`);
504
+ lines.push("");
505
+ }
506
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
507
+ return lines.join("\n");
508
+ }
509
+
510
+ //#endregion
511
+ //#region src/validator/validation-errors.ts
512
+ /**
513
+ * Error thrown when positional argument configuration is invalid
514
+ */
515
+ var PositionalConfigError = class extends Error {
516
+ constructor(message) {
517
+ super(message);
518
+ this.name = "PositionalConfigError";
519
+ }
520
+ };
521
+ /**
522
+ * Error thrown when a reserved alias is used
523
+ */
524
+ var ReservedAliasError = class extends Error {
525
+ constructor(message) {
526
+ super(message);
527
+ this.name = "ReservedAliasError";
528
+ }
529
+ };
530
+ /**
531
+ * Error thrown when duplicate field names are detected
532
+ */
533
+ var DuplicateFieldError = class extends Error {
534
+ constructor(message) {
535
+ super(message);
536
+ this.name = "DuplicateFieldError";
537
+ }
538
+ };
539
+ /**
540
+ * Error thrown when duplicate aliases are detected
541
+ */
542
+ var DuplicateAliasError = class extends Error {
543
+ constructor(message) {
544
+ super(message);
545
+ this.name = "DuplicateAliasError";
546
+ }
547
+ };
548
+
549
+ //#endregion
550
+ //#region src/validator/command-validator.ts
551
+ /**
552
+ * Check for duplicate field names
553
+ */
554
+ function checkDuplicateFields(extracted, commandPath) {
555
+ const errors = [];
556
+ const seenNames = /* @__PURE__ */ new Map();
557
+ for (const field of extracted.fields) {
558
+ if (seenNames.has(field.name)) errors.push({
559
+ commandPath,
560
+ type: "duplicate_field",
561
+ message: `Duplicate field name "${field.name}" detected.`,
562
+ field: field.name
563
+ });
564
+ seenNames.set(field.name, field.name);
565
+ }
566
+ return errors;
567
+ }
568
+ /**
569
+ * Check for duplicate aliases and alias-field name conflicts
570
+ */
571
+ function checkDuplicateAliases(extracted, commandPath) {
572
+ const errors = [];
573
+ const seenAliases = /* @__PURE__ */ new Map();
574
+ const fieldNames = new Set(extracted.fields.map((f) => f.name));
575
+ for (const field of extracted.fields) {
576
+ if (!field.alias) continue;
577
+ if (fieldNames.has(field.alias)) errors.push({
578
+ commandPath,
579
+ type: "duplicate_alias",
580
+ message: `Alias "${field.alias}" for field "${field.name}" conflicts with existing field name "${field.alias}".`,
581
+ field: field.name
582
+ });
583
+ const existingField = seenAliases.get(field.alias);
584
+ if (existingField) errors.push({
585
+ commandPath,
586
+ type: "duplicate_alias",
587
+ message: `Duplicate alias "${field.alias}" detected. Both "${existingField}" and "${field.name}" use the same alias.`,
588
+ field: field.name
589
+ });
590
+ seenAliases.set(field.alias, field.name);
591
+ }
592
+ return errors;
593
+ }
594
+ /**
595
+ * Check positional argument configuration
596
+ */
597
+ function checkPositionalConfig(extracted, commandPath) {
598
+ const errors = [];
599
+ const positionalFields = extracted.fields.filter((f) => f.positional);
600
+ let foundArrayPositional = null;
601
+ let foundOptionalPositional = null;
602
+ for (const field of positionalFields) {
603
+ if (foundArrayPositional !== null) errors.push({
604
+ commandPath,
605
+ type: "positional_config",
606
+ message: `Positional argument "${field.name}" cannot follow array positional argument "${foundArrayPositional}".`,
607
+ field: field.name
608
+ });
609
+ if (field.type === "array" && foundOptionalPositional !== null) errors.push({
610
+ commandPath,
611
+ type: "positional_config",
612
+ message: `Array positional "${field.name}" cannot be used with optional positional "${foundOptionalPositional}" (ambiguous parsing).`,
613
+ field: field.name
614
+ });
615
+ if (foundOptionalPositional !== null && field.required) errors.push({
616
+ commandPath,
617
+ type: "positional_config",
618
+ message: `Required positional "${field.name}" cannot follow optional positional "${foundOptionalPositional}".`,
619
+ field: field.name
620
+ });
621
+ if (field.type === "array") foundArrayPositional = field.name;
622
+ if (!field.required) foundOptionalPositional = field.name;
623
+ }
624
+ return errors;
625
+ }
626
+ /**
627
+ * Check for reserved aliases used without override flag
628
+ */
629
+ function checkReservedAliases(extracted, commandPath) {
630
+ const errors = [];
631
+ for (const field of extracted.fields) if ((field.alias === "h" || field.alias === "H") && field.overrideBuiltinAlias !== true) errors.push({
632
+ commandPath,
633
+ type: "reserved_alias",
634
+ message: `Alias "${field.alias}" is reserved for --${field.alias === "h" ? "help" : "help-all"}.`,
635
+ field: field.name
636
+ });
637
+ return errors;
638
+ }
639
+ /**
640
+ * Validate that no duplicate field names exist
641
+ *
642
+ * @param extracted - Extracted fields from schema
643
+ * @throws {DuplicateFieldError} If duplicate field names are found
644
+ */
645
+ function validateDuplicateFields(extracted) {
646
+ const errors = checkDuplicateFields(extracted, []);
647
+ if (errors.length > 0) throw new DuplicateFieldError(`Duplicate field name "${errors[0]?.field ?? "unknown"}" detected. Each field must have a unique name.`);
648
+ }
649
+ /**
650
+ * Validate that no duplicate aliases exist
651
+ *
652
+ * Also checks for conflicts between aliases and field names
653
+ *
654
+ * @param extracted - Extracted fields from schema
655
+ * @throws {DuplicateAliasError} If duplicate aliases are found or alias conflicts with field name
656
+ */
657
+ function validateDuplicateAliases(extracted) {
658
+ const errors = checkDuplicateAliases(extracted, []);
659
+ if (errors.length > 0) {
660
+ const err = errors[0];
661
+ throw new DuplicateAliasError(err.message);
662
+ }
663
+ }
664
+ /**
665
+ * Validate positional argument configuration
666
+ *
667
+ * Rules:
668
+ * - Array positional arguments must be the last positional
669
+ * - No positional arguments can follow an array positional
670
+ * - Required positional arguments cannot follow optional positional arguments
671
+ * - Array positional and optional positional cannot be used together (ambiguous parsing)
672
+ *
673
+ * @param extracted - Extracted fields from schema
674
+ * @throws {PositionalConfigError} If configuration is invalid
675
+ */
676
+ function validatePositionalConfig(extracted) {
677
+ const errors = checkPositionalConfig(extracted, []);
678
+ if (errors.length > 0) {
679
+ const err = errors[0];
680
+ throw new PositionalConfigError(err.message);
681
+ }
682
+ }
683
+ /**
684
+ * Validate that no reserved aliases are used without explicit override
685
+ *
686
+ * Reserved aliases:
687
+ * - 'h' is reserved for --help
688
+ * - 'H' is reserved for --help-all
689
+ *
690
+ * Users can override these by setting overrideBuiltinAlias: true
691
+ *
692
+ * @param extracted - Extracted fields from schema
693
+ * @param _hasSubCommands - Whether the command has subcommands (reserved for future use)
694
+ * @throws {ReservedAliasError} If a reserved alias is used without override flag
695
+ */
696
+ function validateReservedAliases(extracted, _hasSubCommands) {
697
+ const errors = checkReservedAliases(extracted, []);
698
+ if (errors.length > 0) {
699
+ const field = errors[0].field ?? "unknown";
700
+ const alias = extracted.fields.find((f) => f.name === field)?.alias ?? "h";
701
+ throw new ReservedAliasError(`Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}. To override this, set { alias: "${alias}", overrideBuiltinAlias: true } for "${field}".`);
702
+ }
703
+ }
704
+ /**
705
+ * Collect validation errors for a single command's schema (non-throwing)
706
+ */
707
+ function collectSchemaErrors(extracted, _hasSubCommands, commandPath) {
708
+ return [
709
+ ...checkDuplicateFields(extracted, commandPath),
710
+ ...checkDuplicateAliases(extracted, commandPath),
711
+ ...checkPositionalConfig(extracted, commandPath),
712
+ ...checkReservedAliases(extracted, commandPath)
713
+ ];
714
+ }
715
+ /**
716
+ * Validate a command and all its subcommands recursively
717
+ *
718
+ * This function collects all validation errors without throwing,
719
+ * making it suitable for test assertions.
720
+ *
721
+ * @param command - The command to validate
722
+ * @param options - Validation options
723
+ * @returns Validation result with all errors collected
724
+ *
725
+ * @example
726
+ * ```ts
727
+ * const result = await validateCommand(myCommand);
728
+ * if (!result.valid) {
729
+ * console.error(result.errors);
730
+ * }
731
+ * ```
732
+ */
733
+ async function validateCommand(command, options = {}) {
734
+ const commandPath = options.commandPath ?? [command.name];
735
+ const errors = [];
736
+ const hasSubCommands = command.subCommands ? Object.keys(command.subCommands).length > 0 : false;
737
+ if (command.args) {
738
+ const extracted = extractFields(command.args);
739
+ errors.push(...collectSchemaErrors(extracted, hasSubCommands, commandPath));
740
+ }
741
+ if (command.subCommands) for (const [name, subCmd] of Object.entries(command.subCommands)) {
742
+ const subResult = await validateCommand(await resolveLazyCommand(subCmd), { commandPath: [...commandPath, name] });
743
+ if (!subResult.valid) errors.push(...subResult.errors);
744
+ }
745
+ if (errors.length === 0) return { valid: true };
746
+ return {
747
+ valid: false,
748
+ errors
749
+ };
750
+ }
751
+ /**
752
+ * Format command validation errors for display
753
+ *
754
+ * @param errors - Array of validation errors
755
+ * @returns Formatted error message
756
+ */
757
+ function formatCommandValidationErrors(errors) {
758
+ if (errors.length === 0) return "";
759
+ const lines = ["Command definition errors:"];
760
+ for (const error of errors) {
761
+ const path = error.commandPath.join(" > ");
762
+ lines.push(` - [${path}] ${error.message}`);
763
+ }
764
+ return lines.join("\n");
765
+ }
766
+
767
+ //#endregion
768
+ //#region src/parser/argv-parser.ts
769
+ /**
770
+ * Parse argv into a flat record
771
+ *
772
+ * Supports:
773
+ * - Long options: --flag, --flag=value, --flag value
774
+ * - Short options: -f, -f=value, -f value
775
+ * - Combined short options: -abc (treated as -a -b -c if all are boolean)
776
+ * - Positional arguments
777
+ * - -- to stop parsing options
778
+ *
779
+ * @param argv - Command line arguments
780
+ * @param options - Parser options
781
+ * @returns Parsed arguments
782
+ */
783
+ function parseArgv(argv, options = {}) {
784
+ const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set() } = options;
785
+ const result = {
786
+ options: {},
787
+ positionals: [],
788
+ rest: []
789
+ };
790
+ let i = 0;
791
+ let stopParsing = false;
792
+ const setOption = (name, value) => {
793
+ const resolvedName = aliasMap.get(name) ?? name;
794
+ if (arrayFlags.has(resolvedName)) {
795
+ const existing = result.options[resolvedName];
796
+ if (Array.isArray(existing)) existing.push(value);
797
+ else if (existing !== void 0) result.options[resolvedName] = [existing, value];
798
+ else result.options[resolvedName] = [value];
799
+ } else result.options[resolvedName] = value;
800
+ };
801
+ while (i < argv.length) {
802
+ const arg = argv[i];
803
+ if (stopParsing) {
804
+ result.rest.push(arg);
805
+ i++;
806
+ continue;
807
+ }
808
+ if (arg === "--") {
809
+ stopParsing = true;
810
+ i++;
811
+ continue;
812
+ }
813
+ if (arg.startsWith("--")) {
814
+ const withoutDashes = arg.slice(2);
815
+ if (withoutDashes.startsWith("no-")) {
816
+ const flagName = withoutDashes.slice(3);
817
+ const resolvedName = aliasMap.get(flagName) ?? flagName;
818
+ if (booleanFlags.has(resolvedName)) {
819
+ setOption(flagName, false);
820
+ i++;
821
+ continue;
822
+ }
823
+ }
824
+ const eqIndex = withoutDashes.indexOf("=");
825
+ if (eqIndex !== -1) {
826
+ setOption(withoutDashes.slice(0, eqIndex), withoutDashes.slice(eqIndex + 1));
827
+ i++;
828
+ } else {
829
+ const name = withoutDashes;
830
+ const resolvedName = aliasMap.get(name) ?? name;
831
+ if (booleanFlags.has(resolvedName)) {
832
+ setOption(name, true);
833
+ i++;
834
+ } else {
835
+ const nextArg = argv[i + 1];
836
+ if (nextArg !== void 0 && !nextArg.startsWith("-")) {
837
+ setOption(name, nextArg);
838
+ i += 2;
839
+ } else {
840
+ setOption(name, true);
841
+ i++;
842
+ }
843
+ }
844
+ }
845
+ continue;
846
+ }
847
+ if (arg.startsWith("-") && arg.length > 1 && !arg.startsWith("--")) {
848
+ const withoutDash = arg.slice(1);
849
+ const eqIndex = withoutDash.indexOf("=");
850
+ if (eqIndex !== -1) {
851
+ setOption(withoutDash.slice(0, eqIndex), withoutDash.slice(eqIndex + 1));
852
+ i++;
853
+ } else if (withoutDash.length === 1) {
854
+ const name = withoutDash;
855
+ const resolvedName = aliasMap.get(name) ?? name;
856
+ if (booleanFlags.has(resolvedName)) {
857
+ setOption(name, true);
858
+ i++;
859
+ } else {
860
+ const nextArg = argv[i + 1];
861
+ if (nextArg !== void 0 && !nextArg.startsWith("-")) {
862
+ setOption(name, nextArg);
863
+ i += 2;
864
+ } else {
865
+ setOption(name, true);
866
+ i++;
867
+ }
868
+ }
869
+ } else {
870
+ for (const char of withoutDash) setOption(char, true);
871
+ i++;
872
+ }
873
+ continue;
874
+ }
875
+ result.positionals.push(arg);
876
+ i++;
877
+ }
878
+ return result;
879
+ }
880
+ /**
881
+ * Build parser options from extracted fields
882
+ */
883
+ function buildParserOptions(extracted) {
884
+ const aliasMap = /* @__PURE__ */ new Map();
885
+ const booleanFlags = /* @__PURE__ */ new Set();
886
+ const arrayFlags = /* @__PURE__ */ new Set();
887
+ for (const field of extracted.fields) {
888
+ if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
889
+ if (field.alias) aliasMap.set(field.alias, field.name);
890
+ if (field.type === "boolean") booleanFlags.add(field.name);
891
+ if (field.type === "array") arrayFlags.add(field.name);
892
+ }
893
+ return {
894
+ aliasMap,
895
+ booleanFlags,
896
+ arrayFlags
897
+ };
898
+ }
899
+ /**
900
+ * Merge parsed argv with positional fields to create a flat record
901
+ */
902
+ function mergeWithPositionals(parsed, extracted) {
903
+ const result = { ...parsed.options };
904
+ const positionalFields = extracted.fields.filter((f) => f.positional);
905
+ let positionalIndex = 0;
906
+ for (const field of positionalFields) {
907
+ if (positionalIndex >= parsed.positionals.length) break;
908
+ if (field.type === "array") {
909
+ result[field.name] = parsed.positionals.slice(positionalIndex);
910
+ break;
911
+ } else {
912
+ result[field.name] = parsed.positionals[positionalIndex];
913
+ positionalIndex++;
914
+ }
915
+ }
916
+ return result;
917
+ }
918
+
919
+ //#endregion
920
+ //#region src/parser/arg-parser.ts
921
+ /**
922
+ * Parse CLI arguments for a command
923
+ *
924
+ * @param argv - Command line arguments
925
+ * @param command - The command to parse for
926
+ * @param options - Parse options
927
+ * @returns Parse result
928
+ */
929
+ function parseArgs(argv, command, options = {}) {
930
+ const subCommandNames = command.subCommands ? Object.keys(command.subCommands) : [];
931
+ const hasSubCommands = subCommandNames.length > 0;
932
+ if (hasSubCommands && argv.length > 0) {
933
+ const firstArg = argv[0];
934
+ if (firstArg && !firstArg.startsWith("-") && subCommandNames.includes(firstArg)) return {
935
+ helpRequested: false,
936
+ helpAllRequested: false,
937
+ versionRequested: false,
938
+ subCommand: firstArg,
939
+ remainingArgs: argv.slice(1),
940
+ rawArgs: {},
941
+ positionals: [],
942
+ unknownFlags: []
943
+ };
944
+ }
945
+ let extracted;
946
+ if (command.args) {
947
+ extracted = extractFields(command.args);
948
+ if (!options.skipValidation) {
949
+ validateDuplicateFields(extracted);
950
+ validateDuplicateAliases(extracted);
951
+ validatePositionalConfig(extracted);
952
+ validateReservedAliases(extracted, hasSubCommands);
953
+ }
954
+ }
955
+ const hasUserDefinedH = extracted?.fields.some((f) => f.alias === "H" && f.overrideBuiltinAlias === true) ?? false;
956
+ const hasUserDefinedh = extracted?.fields.some((f) => f.alias === "h" && f.overrideBuiltinAlias === true) ?? false;
957
+ const helpAllRequested = argv.includes("--help-all") || !hasUserDefinedH && argv.includes("-H");
958
+ const helpRequested = !helpAllRequested && (argv.includes("--help") || !hasUserDefinedh && argv.includes("-h"));
959
+ const versionRequested = argv.includes("--version");
960
+ if (helpRequested || helpAllRequested || versionRequested) return {
961
+ helpRequested,
962
+ helpAllRequested,
963
+ versionRequested,
964
+ subCommand: void 0,
965
+ remainingArgs: [],
966
+ rawArgs: {},
967
+ positionals: [],
968
+ unknownFlags: []
969
+ };
970
+ if (!extracted) return {
971
+ helpRequested: false,
972
+ helpAllRequested: false,
973
+ versionRequested: false,
974
+ subCommand: void 0,
975
+ remainingArgs: [],
976
+ rawArgs: {},
977
+ positionals: [],
978
+ unknownFlags: []
979
+ };
980
+ const parsed = parseArgv(argv, buildParserOptions(extracted));
981
+ const rawArgs = mergeWithPositionals(parsed, extracted);
982
+ for (const field of extracted.fields) if (field.env && rawArgs[field.name] === void 0) {
983
+ const envNames = Array.isArray(field.env) ? field.env : [field.env];
984
+ for (const envName of envNames) {
985
+ const envValue = process.env[envName];
986
+ if (envValue !== void 0) {
987
+ rawArgs[field.name] = envValue;
988
+ break;
989
+ }
990
+ }
991
+ }
992
+ const knownFlags = new Set(extracted.fields.map((f) => f.name));
993
+ const knownCliNames = new Set(extracted.fields.map((f) => f.cliName));
994
+ const knownAliases = new Set(extracted.fields.filter((f) => f.alias).map((f) => f.alias));
995
+ const unknownFlags = [];
996
+ for (const key of Object.keys(parsed.options)) if (!knownFlags.has(key) && !knownCliNames.has(key) && !knownAliases.has(key)) unknownFlags.push(key);
997
+ return {
998
+ helpRequested: false,
999
+ helpAllRequested: false,
1000
+ versionRequested: false,
1001
+ subCommand: void 0,
1002
+ remainingArgs: [],
1003
+ rawArgs,
1004
+ positionals: parsed.positionals,
1005
+ unknownFlags,
1006
+ extractedFields: extracted
1007
+ };
1008
+ }
1009
+
1010
+ //#endregion
1011
+ //#region src/validator/error-formatter.ts
1012
+ /**
1013
+ * Calculate Levenshtein distance between two strings
1014
+ */
1015
+ function levenshteinDistance(a, b) {
1016
+ const matrix = [];
1017
+ for (let i = 0; i <= b.length; i++) matrix[i] = [i];
1018
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
1019
+ for (let i = 1; i <= b.length; i++) for (let j = 1; j <= a.length; j++) if (b.charAt(i - 1) === a.charAt(j - 1)) matrix[i][j] = matrix[i - 1][j - 1];
1020
+ else matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
1021
+ return matrix[b.length][a.length];
1022
+ }
1023
+ /**
1024
+ * Find similar strings from a list
1025
+ */
1026
+ function findSimilar(target, candidates) {
1027
+ const threshold = Math.max(2, Math.floor(target.length / 2));
1028
+ return candidates.map((candidate) => ({
1029
+ candidate,
1030
+ distance: levenshteinDistance(target.toLowerCase(), candidate.toLowerCase())
1031
+ })).filter(({ distance }) => distance <= threshold).sort((a, b) => a.distance - b.distance).map(({ candidate }) => candidate).slice(0, 3);
1032
+ }
1033
+ /**
1034
+ * Format validation errors into a human-readable message
1035
+ *
1036
+ * @param errors - Array of validation errors
1037
+ * @returns Formatted error message
1038
+ */
1039
+ function formatValidationErrors$1(errors) {
1040
+ if (errors.length === 0) return "";
1041
+ const lines = [styles.error("Validation errors:")];
1042
+ for (const error of errors) {
1043
+ const path = error.path.join(".");
1044
+ lines.push(` ${symbols.bullet} ${styles.bold(path)}: ${error.message}`);
1045
+ }
1046
+ return lines.join("\n");
1047
+ }
1048
+ /**
1049
+ * Format unknown flag error with suggestions
1050
+ *
1051
+ * @param flag - The unknown flag (e.g., "--verbos")
1052
+ * @param knownFlags - List of known flag names
1053
+ * @returns Formatted error message with suggestions
1054
+ */
1055
+ function formatUnknownFlag(flag, knownFlags) {
1056
+ const similar = findSimilar(flag.replace(/^-{1,2}/, ""), knownFlags);
1057
+ let message = `${styles.error("Unknown option:")} ${styles.bold(flag)}`;
1058
+ if (similar.length > 0) {
1059
+ message += `\n\n${styles.info("Did you mean?")}`;
1060
+ for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.option(`--${suggestion}`)}`;
1061
+ }
1062
+ return message;
1063
+ }
1064
+ /**
1065
+ * Format unknown flag warning with suggestions (for strip mode)
1066
+ *
1067
+ * @param flag - The unknown flag (e.g., "--verbos")
1068
+ * @param knownFlags - List of known flag names
1069
+ * @returns Formatted warning message with suggestions
1070
+ */
1071
+ function formatUnknownFlagWarning(flag, knownFlags) {
1072
+ const similar = findSimilar(flag.replace(/^-{1,2}/, ""), knownFlags);
1073
+ let message = `${styles.warning("Warning: Unknown option:")} ${styles.bold(flag)}`;
1074
+ if (similar.length > 0) {
1075
+ message += `\n\n${styles.info("Did you mean?")}`;
1076
+ for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.option(`--${suggestion}`)}`;
1077
+ }
1078
+ return message;
1079
+ }
1080
+ /**
1081
+ * Format runtime error
1082
+ *
1083
+ * @param error - The error that occurred
1084
+ * @param debug - Whether to include stack trace
1085
+ * @returns Formatted error message
1086
+ */
1087
+ function formatRuntimeError(error, debug) {
1088
+ if (debug && error.stack) return `${styles.error("Error:")} ${error.message}\n\n${styles.dim(error.stack)}`;
1089
+ return `${styles.error("Error:")} ${error.message}`;
1090
+ }
1091
+ /**
1092
+ * Format unknown subcommand error with suggestions
1093
+ *
1094
+ * @param subcommand - The unknown subcommand name
1095
+ * @param knownSubcommands - List of known subcommand names
1096
+ * @returns Formatted error message with suggestions
1097
+ */
1098
+ function formatUnknownSubcommand(subcommand, knownSubcommands) {
1099
+ const similar = findSimilar(subcommand, knownSubcommands);
1100
+ let message = `${styles.error("Unknown command:")} ${styles.bold(subcommand)}`;
1101
+ if (similar.length > 0) {
1102
+ message += `\n\n${styles.info("Did you mean?")}`;
1103
+ for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.command(suggestion)}`;
1104
+ }
1105
+ return message;
1106
+ }
1107
+
1108
+ //#endregion
1109
+ //#region src/validator/zod-validator.ts
1110
+ /**
1111
+ * Convert ZodError to ValidationError array (zod v4 compatible)
1112
+ */
1113
+ function formatZodErrors(error) {
1114
+ return error.issues.map((issue) => ({
1115
+ path: issue.path.map(String),
1116
+ message: issue.message,
1117
+ code: issue.code,
1118
+ received: "received" in issue ? issue.received : void 0,
1119
+ expected: "expected" in issue ? String(issue.expected) : void 0
1120
+ }));
1121
+ }
1122
+ /**
1123
+ * Validate raw arguments against a schema
1124
+ *
1125
+ * @param rawArgs - Parsed but unvalidated arguments
1126
+ * @param schema - Zod schema (ZodObject, ZodDiscriminatedUnion, etc.)
1127
+ * @returns Validation result with typed data or errors
1128
+ */
1129
+ function validateArgs(rawArgs, schema) {
1130
+ const result = schema.safeParse(rawArgs);
1131
+ if (result.success) return {
1132
+ success: true,
1133
+ data: result.data
1134
+ };
1135
+ return {
1136
+ success: false,
1137
+ errors: formatZodErrors(result.error)
1138
+ };
1139
+ }
1140
+ /**
1141
+ * Format validation errors for display
1142
+ */
1143
+ function formatValidationErrors(errors) {
1144
+ return errors.map((e) => {
1145
+ return `${e.path.length > 0 ? `${e.path.join(".")}: ` : ""}${e.message}`;
1146
+ }).join("\n");
1147
+ }
1148
+
1149
+ //#endregion
1150
+ //#region src/core/runner.ts
1151
+ /**
1152
+ * Default logger using console
1153
+ */
1154
+ const defaultLogger = {
1155
+ log: (message) => console.log(message),
1156
+ error: (message) => console.error(message)
1157
+ };
1158
+ /**
1159
+ * Run a command with the given arguments (programmatic/test usage)
1160
+ *
1161
+ * This function parses arguments, validates them, routes to subcommands,
1162
+ * and executes the command. It does NOT call process.exit.
1163
+ *
1164
+ * @param command - The command to run
1165
+ * @param argv - Command line arguments to parse
1166
+ * @param options - Run options
1167
+ * @returns The result of command execution
1168
+ *
1169
+ * @example
1170
+ * ```ts
1171
+ * import { defineCommand, runCommand } from "politty";
1172
+ *
1173
+ * const command = defineCommand({
1174
+ * name: "my-cli",
1175
+ * args: z.object({ name: z.string() }),
1176
+ * run: ({ name }) => console.log(`Hello, ${name}!`),
1177
+ * });
1178
+ *
1179
+ * // In tests
1180
+ * const result = await runCommand(command, ["--name", "World"]);
1181
+ * expect(result.exitCode).toBe(0);
1182
+ * ```
1183
+ */
1184
+ async function runCommand(command, argv, options = {}) {
1185
+ return runCommandInternal(command, argv, {
1186
+ ...options,
1187
+ handleSignals: false,
1188
+ skipValidation: options.skipValidation,
1189
+ logger: options.logger
1190
+ });
1191
+ }
1192
+ /**
1193
+ * Run a CLI command as the main entry point
1194
+ *
1195
+ * This function:
1196
+ * - Uses process.argv for arguments
1197
+ * - Handles SIGINT/SIGTERM signals
1198
+ * - Calls process.exit with the appropriate exit code
1199
+ *
1200
+ * @param command - The command to run
1201
+ * @param options - Main options (version, debug)
1202
+ *
1203
+ * @example
1204
+ * ```ts
1205
+ * import { defineCommand, runMain } from "politty";
1206
+ *
1207
+ * const command = defineCommand({
1208
+ * name: "my-cli",
1209
+ * run: () => console.log("Hello!"),
1210
+ * });
1211
+ *
1212
+ * runMain(command, { version: "1.0.0" });
1213
+ * ```
1214
+ */
1215
+ async function runMain(command, options = {}) {
1216
+ const result = await runCommandInternal(command, process.argv.slice(2), {
1217
+ debug: options.debug,
1218
+ captureLogs: options.captureLogs,
1219
+ skipValidation: options.skipValidation,
1220
+ handleSignals: true,
1221
+ logger: options.logger,
1222
+ _context: {
1223
+ commandPath: [],
1224
+ rootName: command.name,
1225
+ rootVersion: options.version
1226
+ }
1227
+ });
1228
+ process.exit(result.exitCode);
1229
+ }
1230
+ /**
1231
+ * Internal implementation of command running
1232
+ */
1233
+ async function runCommandInternal(command, argv, options = {}) {
1234
+ const logger$1 = options.logger ?? defaultLogger;
1235
+ const context = options._context ?? {
1236
+ commandPath: [],
1237
+ rootName: command.name
1238
+ };
1239
+ const collector = options.captureLogs ?? false ? createLogCollector() : null;
1240
+ collector?.start();
1241
+ const getCurrentLogs = () => {
1242
+ return mergeLogs(options._existingLogs ?? emptyLogs(), collector?.getLogs() ?? emptyLogs());
1243
+ };
1244
+ try {
1245
+ const parseResult = parseArgs(argv, command, { skipValidation: options.skipValidation });
1246
+ if (parseResult.helpRequested || parseResult.helpAllRequested) {
1247
+ let hasUnknownSubcommand = false;
1248
+ const subCmdNames = listSubCommands(command);
1249
+ if (subCmdNames.length > 0) {
1250
+ const potentialSubCmd = argv.find((arg) => !arg.startsWith("-"));
1251
+ if (potentialSubCmd && !subCmdNames.includes(potentialSubCmd)) {
1252
+ logger$1.error(formatUnknownSubcommand(potentialSubCmd, subCmdNames));
1253
+ logger$1.error("");
1254
+ hasUnknownSubcommand = true;
1255
+ }
1256
+ }
1257
+ const help = generateHelp(command, {
1258
+ showSubcommands: options.showSubcommands ?? true,
1259
+ showSubcommandOptions: parseResult.helpAllRequested || options.showSubcommandOptions,
1260
+ context
1261
+ });
1262
+ logger$1.log(help);
1263
+ collector?.stop();
1264
+ if (hasUnknownSubcommand) return {
1265
+ success: false,
1266
+ error: /* @__PURE__ */ new Error(`Unknown subcommand: ${argv.find((arg) => !arg.startsWith("-"))}`),
1267
+ exitCode: 1,
1268
+ logs: getCurrentLogs()
1269
+ };
1270
+ return {
1271
+ success: true,
1272
+ result: void 0,
1273
+ exitCode: 0,
1274
+ logs: getCurrentLogs()
1275
+ };
1276
+ }
1277
+ if (parseResult.versionRequested) {
1278
+ const version = context.rootVersion;
1279
+ if (version) logger$1.log(version);
1280
+ collector?.stop();
1281
+ return {
1282
+ success: true,
1283
+ result: void 0,
1284
+ exitCode: 0,
1285
+ logs: getCurrentLogs()
1286
+ };
1287
+ }
1288
+ if (parseResult.subCommand) {
1289
+ const subCmd = await resolveSubcommand(command, parseResult.subCommand);
1290
+ if (subCmd) {
1291
+ const subContext = {
1292
+ commandPath: [...context.commandPath ?? [], parseResult.subCommand],
1293
+ rootName: context.rootName,
1294
+ rootVersion: context.rootVersion
1295
+ };
1296
+ collector?.stop();
1297
+ return runCommandInternal(subCmd, parseResult.remainingArgs, {
1298
+ ...options,
1299
+ _context: subContext,
1300
+ _existingLogs: getCurrentLogs()
1301
+ });
1302
+ }
1303
+ }
1304
+ if (listSubCommands(command).length > 0 && !parseResult.subCommand && !command.run) {
1305
+ const help = generateHelp(command, {
1306
+ showSubcommands: options.showSubcommands ?? true,
1307
+ context
1308
+ });
1309
+ logger$1.log(help);
1310
+ collector?.stop();
1311
+ return {
1312
+ success: true,
1313
+ result: void 0,
1314
+ exitCode: 0,
1315
+ logs: getCurrentLogs()
1316
+ };
1317
+ }
1318
+ if (parseResult.unknownFlags.length > 0) {
1319
+ const unknownKeysMode = parseResult.extractedFields?.unknownKeysMode ?? "strip";
1320
+ const knownFlags = parseResult.extractedFields?.fields.map((f) => f.name) ?? [];
1321
+ if (unknownKeysMode === "strict") {
1322
+ for (const flag of parseResult.unknownFlags) logger$1.error(formatUnknownFlag(flag, knownFlags));
1323
+ collector?.stop();
1324
+ return {
1325
+ success: false,
1326
+ error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
1327
+ exitCode: 1,
1328
+ logs: getCurrentLogs()
1329
+ };
1330
+ } else if (unknownKeysMode === "strip") for (const flag of parseResult.unknownFlags) logger$1.error(formatUnknownFlagWarning(flag, knownFlags));
1331
+ }
1332
+ if (!command.args) {
1333
+ collector?.stop();
1334
+ return await executeLifecycle(command, {}, {
1335
+ handleSignals: options.handleSignals,
1336
+ captureLogs: options.captureLogs,
1337
+ existingLogs: getCurrentLogs()
1338
+ });
1339
+ }
1340
+ const validationResult = validateArgs(parseResult.rawArgs, command.args);
1341
+ if (!validationResult.success) {
1342
+ logger$1.error(formatValidationErrors$1(validationResult.errors));
1343
+ collector?.stop();
1344
+ return {
1345
+ success: false,
1346
+ error: new Error(formatValidationErrors$1(validationResult.errors)),
1347
+ exitCode: 1,
1348
+ logs: getCurrentLogs()
1349
+ };
1350
+ }
1351
+ collector?.stop();
1352
+ return await executeLifecycle(command, validationResult.data, {
1353
+ handleSignals: options.handleSignals,
1354
+ captureLogs: options.captureLogs,
1355
+ existingLogs: getCurrentLogs()
1356
+ });
1357
+ } catch (error) {
1358
+ const err = error instanceof Error ? error : new Error(String(error));
1359
+ logger$1.error(formatRuntimeError(err, options.debug ?? false));
1360
+ collector?.stop();
1361
+ return {
1362
+ success: false,
1363
+ error: err,
1364
+ exitCode: 1,
1365
+ logs: getCurrentLogs()
1366
+ };
1367
+ }
1368
+ }
1369
+
1370
+ //#endregion
1371
+ export { logger as _, formatCommandValidationErrors as a, symbols as b, validateDuplicateFields as c, DuplicateAliasError as d, DuplicateFieldError as f, isColorEnabled as g, generateHelp as h, parseArgv as i, validatePositionalConfig as l, ReservedAliasError as m, runMain as n, validateCommand as o, PositionalConfigError as p, formatValidationErrors as r, validateDuplicateAliases as s, runCommand as t, validateReservedAliases as u, setColorEnabled as v, styles as y };
1372
+ //# sourceMappingURL=runner-DceWXOwD.js.map