cli-kiss 0.2.6 → 0.2.8

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 (39) hide show
  1. package/README.md +62 -4
  2. package/dist/index.d.ts +135 -128
  3. package/dist/index.js +2 -2
  4. package/dist/index.js.map +1 -1
  5. package/docs/.vitepress/config.mts +1 -1
  6. package/docs/.vitepress/theme/Layout.vue +16 -0
  7. package/docs/.vitepress/theme/index.ts +5 -1
  8. package/docs/.vitepress/theme/style.css +5 -1
  9. package/docs/guide/02_commands.md +1 -1
  10. package/docs/guide/03_options.md +11 -11
  11. package/docs/guide/05_input_types.md +9 -10
  12. package/docs/guide/06_run_as_cli.md +1 -1
  13. package/docs/index.md +2 -2
  14. package/docs/public/favicon.ico +0 -0
  15. package/docs/public/logo.png +0 -0
  16. package/package.json +1 -1
  17. package/src/index.ts +1 -0
  18. package/src/lib/Command.ts +50 -30
  19. package/src/lib/Operation.ts +29 -21
  20. package/src/lib/Option.ts +198 -133
  21. package/src/lib/Positional.ts +46 -24
  22. package/src/lib/Reader.ts +194 -207
  23. package/src/lib/Run.ts +19 -8
  24. package/src/lib/Suggest.ts +78 -0
  25. package/src/lib/Type.ts +46 -48
  26. package/src/lib/Typo.ts +72 -47
  27. package/src/lib/Usage.ts +13 -13
  28. package/tests/unit.Reader.commons.ts +92 -116
  29. package/tests/unit.Reader.parsings.ts +14 -26
  30. package/tests/unit.Reader.shortBig.ts +81 -96
  31. package/tests/unit.command.aliases.ts +100 -0
  32. package/tests/unit.command.execute.ts +1 -1
  33. package/tests/unit.command.usage.ts +12 -6
  34. package/tests/unit.fuzzed.alternatives.ts +43 -0
  35. package/tests/unit.runner.colors.ts +11 -35
  36. package/tests/unit.runner.cycle.ts +181 -128
  37. package/tests/unit.runner.errors.ts +26 -19
  38. package/docs/public/hero.png +0 -0
  39. package/tests/unit.Reader.aliases.ts +0 -62
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
- # CLI - Keep It Simple, Stupid
1
+ # cli-kiss
2
2
 
3
- Full-featured CLI builder for TypeScript. No bloat, no dependency.
3
+ Type-safe CLI builder for TypeScript. No dependencies, no supply-chain attacks.
4
+
5
+ Small API, standard compliance, polished help output, zero runtime dependencies.
6
+
7
+ ## Why
8
+
9
+ - Compose commands, subcommands, options, and positionals with strong types
10
+ - Get built-in `--help`, `--version`, and color-aware error messages
11
+ - Ship a real CLI parser without pulling in a dependency tree
4
12
 
5
13
  ## Install
6
14
 
@@ -8,6 +16,56 @@ Full-featured CLI builder for TypeScript. No bloat, no dependency.
8
16
  npm install cli-kiss
9
17
  ```
10
18
 
11
- ## Cookbook
19
+ ## Example
20
+
21
+ ```ts
22
+ import {
23
+ command,
24
+ operation,
25
+ optionFlag,
26
+ positionalRequired,
27
+ runAndExit,
28
+ type,
29
+ } from "cli-kiss";
30
+
31
+ const greet = command(
32
+ { description: "Greet someone" },
33
+ operation(
34
+ {
35
+ options: {
36
+ loud: optionFlag({ long: "loud", description: "Print in uppercase" }),
37
+ },
38
+ positionals: [positionalRequired({ type: type("name") })],
39
+ },
40
+ async (_ctx, { options: { loud }, positionals: [name] }) => {
41
+ const text = `Hello, ${name}!`;
42
+ console.log(loud ? text.toUpperCase() : text);
43
+ },
44
+ ),
45
+ );
46
+
47
+ await runAndExit("greet", process.argv.slice(2), {}, greet, {
48
+ buildVersion: "1.0.0",
49
+ });
50
+ ```
51
+
52
+ ```sh
53
+ greet --help
54
+ ```
55
+
56
+ ```text
57
+ Usage: greet <name>
58
+
59
+ Greet someone
60
+
61
+ Positionals:
62
+ <name>
63
+
64
+ Options:
65
+ --loud[=no] Print in uppercase
66
+ ```
67
+
68
+ ## Docs
12
69
 
13
- Documentation and examples: **<https://crypto-vincent.github.io/cli-kiss/>** 💋
70
+ Guides, examples, and API usage:
71
+ [crypto-vincent.github.io/cli-kiss](https://crypto-vincent.github.io/cli-kiss/)
package/dist/index.d.ts CHANGED
@@ -1,47 +1,43 @@
1
1
  /**
2
- * Opaque key returned by {@link ReaderArgs.registerOption}.
3
2
  */
4
- type ReaderOptionKey = (string | {
5
- __brand: "ReaderOptionKey";
6
- }) & {
7
- __brand: "ReaderOptionKey";
3
+ type ReaderOptionLongSpec = {
4
+ key: string;
5
+ nextGuard: ReaderOptionNextGuard;
8
6
  };
9
7
  /**
10
- * Parsing behaviour for a registered option, passed to {@link ReaderArgs.registerOption}.
11
8
  */
12
- type ReaderOptionParsing = {
13
- consumeShortGroup: boolean;
14
- consumeNextArg: (inlined: string | null, separated: Array<string>, nextArg: string | undefined) => boolean;
9
+ type ReaderOptionShortSpec = {
10
+ key: string;
11
+ restGuard: ReaderOptionRestGuard;
12
+ nextGuard: ReaderOptionNextGuard;
13
+ };
14
+ /**
15
+ */
16
+ type ReaderOptionRestGuard = (rest: string) => boolean;
17
+ /**
18
+ */
19
+ type ReaderOptionNextGuard = (value: ReaderOptionValue, next: string | undefined) => boolean;
20
+ /**
21
+ */
22
+ type ReaderOptionResult = {
23
+ identifier: string;
24
+ values: ReadonlyArray<ReaderOptionValue>;
15
25
  };
16
26
  /**
17
- * Result of parsing an option, including its inlined value and any following separated values.
18
27
  */
19
28
  type ReaderOptionValue = {
20
29
  inlined: string | null;
21
- separated: Array<string>;
30
+ separated: ReadonlyArray<string>;
22
31
  };
32
+ /**
33
+ */
34
+ type ReaderOptionGetter = () => ReaderOptionResult;
23
35
  /**
24
36
  * Option registration/query interface. Subset of {@link ReaderArgs}.
25
37
  */
26
38
  type ReaderOptions = {
27
- /**
28
- * Registers an option; all `longs` and `shorts` share the same key.
29
- *
30
- * @param definition.longs - Long-form names (without `--`).
31
- * @param definition.shorts - Short-form names (without `-`).
32
- * @param definition.parsing - Parsing behaviour.
33
- * @returns A {@link ReaderOptionKey} for later retrieval.
34
- * @throws `Error` if a name is already registered or short names overlap.
35
- */
36
- registerOption(definition: {
37
- longs: Array<string>;
38
- shorts: Array<string>;
39
- parsing: ReaderOptionParsing;
40
- }): ReaderOptionKey;
41
- /**
42
- * Returns all values collected for `key`.
43
- */
44
- getOptionValues(key: ReaderOptionKey): Array<ReaderOptionValue>;
39
+ registerOptionLong(longSpec: ReaderOptionLongSpec): ReaderOptionGetter;
40
+ registerOptionShort(shortSpec: ReaderOptionShortSpec): ReaderOptionGetter;
45
41
  };
46
42
  /**
47
43
  * Positional consumption interface. Subset of {@link ReaderArgs}.
@@ -51,7 +47,7 @@ type ReaderPositionals = {
51
47
  * Returns the next positional token, parsing intervening options as side-effects.
52
48
  *
53
49
  * @returns The next positional, or `undefined` when exhausted.
54
- * @throws {@link TypoError} on an unrecognised option.
50
+ * @throws on an unrecognised option.
55
51
  */
56
52
  consumePositional(): string | undefined;
57
53
  };
@@ -67,38 +63,21 @@ type ReaderPositionals = {
67
63
  declare class ReaderArgs {
68
64
  #private;
69
65
  /**
70
- * @param args - Raw CLI tokens (e.g. `process.argv.slice(2)`). Not mutated.
66
+ * @param tokens - Raw CLI tokens (e.g. `process.argv.slice(2)`).
71
67
  */
72
- constructor(args: ReadonlyArray<string>);
68
+ constructor(tokens: ReadonlyArray<string>);
69
+ /**
70
+ */
71
+ registerOptionLong(longSpec: ReaderOptionLongSpec): ReaderOptionGetter;
73
72
  /**
74
- * Registers an option; all `longs` and `shorts` share the same key.
75
- * Short names must not be prefixes of one another.
76
- *
77
- * @param definition.longs - Long-form names (without `--`).
78
- * @param definition.shorts - Short-form names (without `-`).
79
- * @param definition.parsing - Parsing behaviour.
80
- * @returns A {@link ReaderOptionKey} for {@link ReaderArgs.getOptionValues}.
81
- * @throws `Error` if any name is already registered or short names overlap.
82
- */
83
- registerOption(definition: {
84
- longs: Array<string>;
85
- shorts: Array<string>;
86
- parsing: ReaderOptionParsing;
87
- }): ReaderOptionKey;
88
- /**
89
- * Returns all values collected for `key`.
90
- *
91
- * @param key - Key from {@link ReaderArgs.registerOption}.
92
- * @returns One entry per occurrence.
93
- * @throws `Error` if `key` was not registered.
94
73
  */
95
- getOptionValues(key: ReaderOptionKey): Array<ReaderOptionValue>;
74
+ registerOptionShort(shortSpec: ReaderOptionShortSpec): ReaderOptionGetter;
96
75
  /**
97
76
  * Returns the next positional token; parses intervening options as a side-effect.
98
77
  * All tokens after `--` are treated as positionals.
99
78
  *
100
79
  * @returns The next positional, or `undefined` when exhausted.
101
- * @throws {@link TypoError} on an unrecognised option.
80
+ * @throws on an unrecognised option.
102
81
  */
103
82
  consumePositional(): string | undefined;
104
83
  }
@@ -115,7 +94,7 @@ declare class ReaderArgs {
115
94
  */
116
95
  type Type<Value> = {
117
96
  /**
118
- * Human-readable name shown in help and errors (e.g. `"name"`, `"number"`).
97
+ * Human-readable name shown in help and errors (e.g. `"name"`, `"context"`).
119
98
  */
120
99
  content: string;
121
100
  /**
@@ -123,7 +102,7 @@ type Type<Value> = {
123
102
  *
124
103
  * @param input - Raw string from the command line.
125
104
  * @returns The decoded value.
126
- * @throws {@link TypoError} on invalid input.
105
+ * @throws on invalid input.
127
106
  */
128
107
  decoder(input: string): Value;
129
108
  };
@@ -142,8 +121,6 @@ type Type<Value> = {
142
121
  * ```
143
122
  */
144
123
  declare function typeBoolean(name?: string): Type<boolean>;
145
- declare const typeBooleanValuesTrue: Set<string>;
146
- declare const typeBooleanValuesFalse: Set<string>;
147
124
  /**
148
125
  * Parses a date/time string via `Date.parse`.
149
126
  * Accepts any format supported by `Date.parse`, including ISO 8601.
@@ -157,7 +134,7 @@ declare const typeBooleanValuesFalse: Set<string>;
157
134
  */
158
135
  declare function typeDatetime(name?: string): Type<Date>;
159
136
  /**
160
- * Parses a string to `number` via `Number()`; `NaN` throws {@link TypoError}.
137
+ * Parses a string to `number` via `Number()`; `NaN` throws.
161
138
  *
162
139
  * @example
163
140
  * ```ts
@@ -169,7 +146,7 @@ declare function typeDatetime(name?: string): Type<Date>;
169
146
  declare function typeNumber(name?: string): Type<number>;
170
147
  /**
171
148
  * Parses an integer string to `bigint` via `BigInt()`.
172
- * Floats and non-numeric strings throw {@link TypoError}.
149
+ * Floats and non-numeric strings throws.
173
150
  *
174
151
  * @example
175
152
  * ```ts
@@ -181,7 +158,7 @@ declare function typeNumber(name?: string): Type<number>;
181
158
  declare function typeInteger(name?: string): Type<bigint>;
182
159
  /**
183
160
  * Parses an absolute URL string to a `URL` object.
184
- * Relative or malformed URLs throw {@link TypoError}.
161
+ * Relative or malformed URLs throws.
185
162
  *
186
163
  * @example
187
164
  * ```ts
@@ -241,7 +218,6 @@ declare function typePath(name?: string, checks?: {
241
218
  }): Type<string>;
242
219
  /**
243
220
  * Creates a {@link Type}`<string>` that only accepts a fixed set of values.
244
- * Out-of-set inputs throw {@link TypoError} listing up to 5 valid options.
245
221
  *
246
222
  * @param name - Name shown in help and errors.
247
223
  * @param values - Ordered list of accepted values.
@@ -251,13 +227,13 @@ declare function typePath(name?: string, checks?: {
251
227
  * ```ts
252
228
  * const typeEnv = typeChoice("environment", ["dev", "staging", "prod"]);
253
229
  * typeEnv.decoder("prod") // → "prod"
254
- * typeEnv.decoder("unknown") // throws TypoError: Invalid value: "unknown" (expected one of: "dev" | "staging" | "prod")
230
+ * typeEnv.decoder("unknown") // throws
255
231
  * ```
256
232
  */
257
233
  declare function typeChoice<const Value extends string>(name: string, values: Array<Value>, caseSensitive?: boolean): Type<Value>;
258
234
  /**
259
235
  * Splits a delimited string into a typed tuple.
260
- * Each part is decoded by the corresponding element type; wrong count or decode failure throws {@link TypoError}.
236
+ * Each part is decoded by the corresponding element type; wrong count or decode failure throws.
261
237
  *
262
238
  * @typeParam Elements - Tuple of decoded value types (inferred from `elementTypes`).
263
239
  *
@@ -269,8 +245,9 @@ declare function typeChoice<const Value extends string>(name: string, values: Ar
269
245
  * ```ts
270
246
  * const typePoint = typeTuple([typeNumber("x"), typeNumber("y")]);
271
247
  * typePoint.decoder("3.14,2.71") // → [3.14, 2.71]
272
- * typePoint.decoder("1,2,3") // → [1, 2]
273
- * typePoint.decoder("x,2") // throws TypoError: at 0: Number: Unable to parse: "x"
248
+ * typePoint.decoder("1") // throws
249
+ * typePoint.decoder("1,2,3") // throws
250
+ * typePoint.decoder("x,2") // throws
274
251
  * ```
275
252
  */
276
253
  declare function typeTuple<const Elements extends Array<any>>(elementTypes: {
@@ -278,7 +255,7 @@ declare function typeTuple<const Elements extends Array<any>>(elementTypes: {
278
255
  }, separator?: string): Type<Elements>;
279
256
  /**
280
257
  * Splits a delimited string into a typed array.
281
- * Each part is decoded by `elementType`; failed decodes throw {@link TypoError}.
258
+ * Each part is decoded by `elementType`; failed decodes throws.
282
259
  * Note: splitting an empty string yields one empty element — prefer {@link optionRepeatable} for a zero-default.
283
260
  *
284
261
  * @typeParam Value - Element type produced by `elementType.decoder`.
@@ -289,10 +266,9 @@ declare function typeTuple<const Elements extends Array<any>>(elementTypes: {
289
266
  *
290
267
  * @example
291
268
  * ```ts
292
- * const typeNumbers = typeList(typeNumber);
269
+ * const typeNumbers = typeList(typeNumber("v"));
293
270
  * typeNumbers.decoder("1,2,3") // → [1, 2, 3]
294
- * typeNumbers.decoder("1,x,3") // throws TypoError: at 1: Number: Unable to parse: "x"
295
- *
271
+ * typeNumbers.decoder("1,x,3") // throws
296
272
  * const typePaths = typeList(typePath(), ":");
297
273
  * typePaths.decoder("/usr/bin:/usr/local/bin") // → ["/usr/bin", "/usr/local/bin"]
298
274
  * ```
@@ -381,9 +357,9 @@ declare class TypoString {
381
357
  #private;
382
358
  /**
383
359
  * @param value - Raw text content.
384
- * @param typoStyle - Style to apply when rendering. Defaults to `{}` (no style).
360
+ * @param style - Style to apply when rendering.
385
361
  */
386
- constructor(value: string, typoStyle?: TypoStyle);
362
+ constructor(value: string, style?: TypoStyle);
387
363
  /**
388
364
  * Returns the text styled by `typoSupport`.
389
365
  *
@@ -394,7 +370,15 @@ declare class TypoString {
394
370
  * Returns the raw text.
395
371
  */
396
372
  getRawString(): string;
373
+ /**
374
+ * Predefined ellipsis segment with subtle styling.
375
+ */
376
+ static elipsis: TypoString;
397
377
  }
378
+ /**
379
+ * A segment of styled text, a string, or an array of segments.
380
+ */
381
+ type TypoSegment = TypoText | TypoString | Array<TypoSegment>;
398
382
  /**
399
383
  * Mutable sequence of {@link TypoString} segments.
400
384
  */
@@ -403,13 +387,15 @@ declare class TypoText {
403
387
  /**
404
388
  * @param segments - Initial text segments
405
389
  */
406
- constructor(...segments: Array<TypoText | Array<TypoString> | TypoString | string>);
390
+ constructor(...segments: Array<TypoSegment>);
407
391
  /**
408
392
  * Appends new text segment(s).
409
- *
410
- * @param segment - Text segment(s) to append.
411
393
  */
412
- push(segment: TypoText | Array<TypoString> | TypoString | string): void;
394
+ push(...segments: Array<TypoSegment>): void;
395
+ /**
396
+ * Appends separated segments, optionally truncating with an ellipsis.
397
+ */
398
+ pushJoined(segments: Array<TypoSegment>, separator: TypoSegment, ellipsisLimit: number): void;
413
399
  /**
414
400
  * Renders all segments into a single styled string.
415
401
  *
@@ -505,7 +491,7 @@ declare class TypoSupport {
505
491
  * @param typoStyle - Style to apply.
506
492
  * @returns Styled string.
507
493
  */
508
- computeStyledString(value: string, typoStyle: TypoStyle): string;
494
+ computeStyledString(value: string, typoStyle: TypoStyle | undefined): string;
509
495
  /**
510
496
  * Formats any thrown value as `"Error: <message>"` with {@link typoStyleFailure} on the prefix.
511
497
  *
@@ -671,7 +657,7 @@ type Option<Value> = {
671
657
  /**
672
658
  * Registers the option on `readerOptions` and returns an {@link OptionDecoder}.
673
659
  */
674
- registerAndMakeDecoder(readerOptions: ReaderArgs): OptionDecoder<Value>;
660
+ registerAndMakeDecoder(readerOptions: ReaderOptions): OptionDecoder<Value>;
675
661
  };
676
662
  /**
677
663
  * Produced by {@link Option.registerAndMakeDecoder}.
@@ -682,15 +668,19 @@ type OptionDecoder<Value> = {
682
668
  /**
683
669
  * Returns the decoded option value.
684
670
  *
685
- * @throws {@link TypoError} if decoding failed.
671
+ * @throws if decoding failed.
686
672
  */
687
673
  getAndDecodeValue(): Value;
688
674
  };
689
675
  /**
690
676
  * Creates a boolean flag option (`--verbose`, optionally `--flag=no`).
691
677
  *
692
- * Parsing: absent → default value; `--flag` / `--flag=yes` `true`; `--flag=no` → `false`;
693
- * specified more than once → throws {@link TypoError}.
678
+ * Syntax: `--long`, `--long=no`, `-s`, `-s=no`.
679
+ * Parsing logic:
680
+ * - absent → default value
681
+ * - `--flag` / `--flag=yes` → `true`
682
+ * - `--flag=no` → `false`
683
+ * - specified more than once → throws.
694
684
  *
695
685
  * @param definition.long - Long-form name (without `--`).
696
686
  * @param definition.short - Short-form name (without `-`).
@@ -707,11 +697,6 @@ type OptionDecoder<Value> = {
707
697
  * short: "v",
708
698
  * description: "Enable verbose output",
709
699
  * });
710
- * // Usage:
711
- * // my-cli → false
712
- * // my-cli --verbose → true
713
- * // my-cli --verbose=yes → true
714
- * // my-cli -v=no → false
715
700
  * ```
716
701
  */
717
702
  declare function optionFlag(definition: {
@@ -728,8 +713,13 @@ declare function optionFlag(definition: {
728
713
  /**
729
714
  * Creates an option that accepts exactly one value (e.g. `--output dist/`).
730
715
  *
731
- * Parsing: absent `defaultValue()`; once decoded with `type`; more than once → {@link TypoError}.
732
- * Value syntax: `--long value`, `--long=value`, `-s value`, `-s=value`, `-svalue`.
716
+ * Syntax: `--long value`, `--long=value`, `-s value`, `-s=value`, `-svalue`.
717
+ * Parsing logic:
718
+ * - absent → `fallbackValueIfAbsent()`
719
+ * - `--long value` → decoded with `type.decoder("value")`
720
+ * - more than once → throws
721
+ * - if `impliedValueIfNotInlined` is not provided, then: `--long` / `-s` without a value → throws
722
+ * - if `impliedValueIfNotInlined` is provided, then: `--long` / `-s` without an inline value → `impliedValueIfNotInlined()`
733
723
  *
734
724
  * @typeParam Value - Type produced by the decoder.
735
725
  *
@@ -739,8 +729,8 @@ declare function optionFlag(definition: {
739
729
  * @param definition.hint - Short note shown in parentheses.
740
730
  * @param definition.aliases - Additional names.
741
731
  * @param definition.type - Decoder for the raw string value.
742
- * @param definition.valueWhenNotDefined - Default value when the option is not specified at all.
743
- * @param definition.valueWhenNotInlined - Default value when the option is specified without an inline value (e.g. `--option` or `-o`).
732
+ * @param definition.fallbackValueIfAbsent - Default value when the option is not specified at all.
733
+ * @param definition.impliedValueIfNotInlined - Default value when the option is specified without an inline value (e.g. `--option` or `-o`).
744
734
  * @returns An {@link Option}`<Value>`.
745
735
  *
746
736
  * @example
@@ -750,12 +740,8 @@ declare function optionFlag(definition: {
750
740
  * short: "o",
751
741
  * type: typePath(),
752
742
  * description: "Output directory",
753
- * valueWhenNotDefined: () => "dist",
743
+ * fallbackValueIfAbsent: () => "dist",
754
744
  * });
755
- * // Usage:
756
- * // my-cli → "dist"
757
- * // my-cli --output folder → "folder"
758
- * // my-cli -o folder → "folder"
759
745
  * ```
760
746
  */
761
747
  declare function optionSingleValue<Value>(definition: {
@@ -768,14 +754,16 @@ declare function optionSingleValue<Value>(definition: {
768
754
  shorts?: Array<string>;
769
755
  };
770
756
  type: Type<Value>;
771
- defaultWhenNotDefined: () => Value;
772
- defaultWhenNotInlined?: () => Value;
757
+ fallbackValueIfAbsent?: () => Value;
758
+ impliedValueIfNotInlined?: () => Value;
773
759
  }): Option<Value>;
774
760
  /**
775
761
  * Creates an option that collects every occurrence into an array (e.g. `--file a.ts --file b.ts`).
776
762
  *
777
- * Parsing: absent `[]`; N occurrences array of N decoded values in order.
778
- * Value syntax: `--long value`, `--long=value`, `-s value`, `-s=value`, `-svalue`.
763
+ * Syntax: `--long value`, `--long=value`, `-s value`, `-s=value`, `-svalue`.
764
+ * Parsing logic:
765
+ * - absent → `[]`
766
+ * - N occurrences → array of N decoded values in order.
779
767
  *
780
768
  * @typeParam Value - Type produced by the decoder for each occurrence.
781
769
  *
@@ -796,7 +784,6 @@ declare function optionSingleValue<Value>(definition: {
796
784
  * label: "PATH",
797
785
  * description: "Input file (may be repeated)",
798
786
  * });
799
- * // Usage: my-cli --file a.ts --file b.ts → ["a.ts", "b.ts"]
800
787
  * ```
801
788
  */
802
789
  declare function optionRepeatable<Value>(definition: {
@@ -837,18 +824,24 @@ type PositionalDecoder<Value> = {
837
824
  /**
838
825
  * Returns the decoded positional value.
839
826
  *
840
- * @throws {@link TypoError} if decoding failed.
827
+ * @throws if decoding failed.
841
828
  */
842
829
  decodeValue(): Value;
843
830
  };
844
831
  /**
845
- * Creates a required positional — missing token throws {@link TypoError}.
832
+ * Creates a required positional — missing token throws.
833
+ *
834
+ * Syntax: `<type>`, e.g. `<NAME>`.
835
+ * Parsing logic:
836
+ * - "token" → decoded with `type.decoder("token")`
837
+ * - token missing → throws
846
838
  *
847
839
  * @typeParam Value - Type produced by the decoder.
848
840
  *
849
841
  * @param definition.description - Help text.
850
842
  * @param definition.hint - Short note shown in parentheses.
851
843
  * @param definition.type - Decoder for the raw token.
844
+ * @param definition.missing - Message shown when the token is missing.
852
845
  * @returns A {@link Positional}`<Value>`.
853
846
  *
854
847
  * @example
@@ -857,8 +850,6 @@ type PositionalDecoder<Value> = {
857
850
  * type: type("name"),
858
851
  * description: "The name to greet",
859
852
  * });
860
- * // Usage:
861
- * // my-cli Alice → "Alice"
862
853
  * ```
863
854
  */
864
855
  declare function positionalRequired<Value>(definition: {
@@ -869,6 +860,11 @@ declare function positionalRequired<Value>(definition: {
869
860
  /**
870
861
  * Creates an optional positional — absent token falls back to `default()`.
871
862
  *
863
+ * Syntax: `[type]`, e.g. `[NAME]`.
864
+ * Parsing logic:
865
+ * - "token" → decoded with `type.decoder("token")`
866
+ * - token missing → `default()`
867
+ *
872
868
  * @typeParam Value - Type produced by the decoder (or the default).
873
869
  *
874
870
  * @param definition.description - Help text.
@@ -881,12 +877,10 @@ declare function positionalRequired<Value>(definition: {
881
877
  * ```ts
882
878
  * const greeteePositional = positionalOptional({
883
879
  * type: type("name"),
884
- * description: "Name to greet (default: world)",
880
+ * description: "Name to greet",
881
+ * hint: "Defaults to \"world\"",
885
882
  * default: () => "world",
886
883
  * });
887
- * // Usage:
888
- * // my-cli → "world"
889
- * // my-cli Alice → "Alice"
890
884
  * ```
891
885
  */
892
886
  declare function positionalOptional<Value>(definition: {
@@ -899,6 +893,12 @@ declare function positionalOptional<Value>(definition: {
899
893
  * Creates a variadic positional that collects zero or more remaining tokens into an array.
900
894
  * Optionally stops at `endDelimiter` (consumed, not included).
901
895
  *
896
+ * Syntax: `[type]...`, e.g. `[NAME]...`.
897
+ * Parsing logic:
898
+ * - "a b ..." → decoded with `[type.decoder("a")`, `type.decoder("b"), ...]``
899
+ * - token missing → stops collection
900
+ * - endDelimiter encountered → stops collection
901
+ *
902
902
  * @typeParam Value - Type produced by the decoder for each token.
903
903
  *
904
904
  * @param definition.endDelimiter - Sentinel token that stops collection (consumed, not included).
@@ -913,9 +913,6 @@ declare function positionalOptional<Value>(definition: {
913
913
  * type: typePath(),
914
914
  * description: "Files to process",
915
915
  * });
916
- * // Usage:
917
- * // my-cli → []
918
- * // my-cli a.ts b.ts c.ts → ["a.ts", "b.ts", "c.ts"]
919
916
  * ```
920
917
  */
921
918
  declare function positionalVariadics<Value>(definition: {
@@ -963,7 +960,7 @@ type OperationDecoder<Context, Result> = {
963
960
  /**
964
961
  * Creates a ready-to-execute {@link OperationInterpreter}.
965
962
  *
966
- * @throws {@link TypoError} if parsing or decoding failed.
963
+ * @throws if parsing or decoding failed.
967
964
  */
968
965
  decodeAndMakeInterpreter(): OperationInterpreter<Context, Result>;
969
966
  };
@@ -1000,7 +997,7 @@ type OperationInterpreter<Context, Result> = {
1000
997
  * const greetOperation = operation(
1001
998
  * {
1002
999
  * options: {
1003
- * loud: optionFlag({ long: "loud", description: "Print in uppercase", default: false }),
1000
+ * loud: optionFlag({ long: "loud", description: "Print in uppercase" }),
1004
1001
  * },
1005
1002
  * positionals: [
1006
1003
  * positionalRequired({ type: type("name"), description: "Name to greet" }),
@@ -1013,18 +1010,22 @@ type OperationInterpreter<Context, Result> = {
1013
1010
  * );
1014
1011
  * ```
1015
1012
  */
1016
- declare function operation<Context, Result, Options extends {
1013
+ declare function operation<Context, Result, const Options extends {
1017
1014
  [option: string]: any;
1018
- }, const Positionals extends Array<any>>(inputs: {
1019
- options: {
1015
+ } = {}, const Positionals extends Array<any> = []>(inputs: {
1016
+ options?: {
1020
1017
  [K in keyof Options]: Option<Options[K]>;
1021
1018
  };
1022
- positionals: {
1019
+ positionals?: {
1023
1020
  [K in keyof Positionals]: Positional<Positionals[K]>;
1024
1021
  };
1025
1022
  }, handler: (context: Context, inputs: {
1026
- options: Options;
1027
- positionals: Positionals;
1023
+ options: {
1024
+ [K in keyof Options]: Options[K];
1025
+ };
1026
+ positionals: {
1027
+ [K in keyof Positionals]: Positionals[K];
1028
+ };
1028
1029
  }) => Promise<Result>): Operation<Context, Result>;
1029
1030
 
1030
1031
  /**
@@ -1057,7 +1058,7 @@ type CommandDecoder<Context, Result> = {
1057
1058
  /**
1058
1059
  * Creates a ready-to-execute {@link CommandInterpreter}.
1059
1060
  *
1060
- * @throws {@link TypoError} if parsing or decoding failed.
1061
+ * @throws if parsing or decoding failed.
1061
1062
  */
1062
1063
  decodeAndMakeInterpreter(): CommandInterpreter<Context, Result>;
1063
1064
  };
@@ -1132,15 +1133,16 @@ type CommandInformation = {
1132
1133
  * const greet = command(
1133
1134
  * { description: "Greet a user" },
1134
1135
  * operation(
1135
- * { options: {}, positionals: [positionalRequired({ type: type("name") })] },
1136
+ * { positionals: [positionalRequired({ type: type("name") })] },
1136
1137
  * async (_ctx, { positionals: [name] }) => console.log(`Hello, ${name}!`),
1137
1138
  * ),
1138
1139
  * );
1139
1140
  * ```
1140
1141
  */
1141
- declare function command<Context, Result>(information: CommandInformation, operation: Operation<Context, Result>): Command<Context, Result>;
1142
+ declare function command<Context, Result = void>(information: CommandInformation, operation: Operation<Context, Result>): Command<Context, Result>;
1142
1143
  /**
1143
- * Creates a command that runs `operation` first, then dispatches to a named subcommand.
1144
+ * Creates a command that runs `operation` first,
1145
+ * then dispatches result to a named subcommand.
1144
1146
  *
1145
1147
  * @typeParam Context - Context accepted by `operation`.
1146
1148
  * @typeParam Payload - Output of `operation`; becomes the subcommand's context.
@@ -1163,12 +1165,12 @@ declare function command<Context, Result>(information: CommandInformation, opera
1163
1165
  * );
1164
1166
  * ```
1165
1167
  */
1166
- declare function commandWithSubcommands<Context, Payload, Result>(information: CommandInformation, operation: Operation<Context, Payload>, subcommands: {
1168
+ declare function commandWithSubcommands<Context, Payload, Result = void>(information: CommandInformation, operation: Operation<Context, Payload>, subcommands: {
1167
1169
  [subcommand: string]: Command<Payload, Result>;
1168
1170
  }): Command<Context, Result>;
1169
1171
  /**
1170
- * Chains an {@link Operation} and a {@link Command}: `operation` runs first, its
1171
- * output becomes `subcommand`'s context. No token is consumed for routing.
1172
+ * Chains an {@link Operation} and a {@link Command}: `operation` runs first,
1173
+ * its output becomes `subcommand`'s context. No token is consumed for routing.
1172
1174
  *
1173
1175
  * @typeParam Context - Context accepted by `operation`.
1174
1176
  * @typeParam Payload - Output of `operation`; becomes `subcommand`'s context.
@@ -1179,7 +1181,7 @@ declare function commandWithSubcommands<Context, Payload, Result>(information: C
1179
1181
  * @param subcommand - Runs after `operation`.
1180
1182
  * @returns A {@link Command} composing both stages.
1181
1183
  */
1182
- declare function commandChained<Context, Payload, Result>(information: CommandInformation, operation: Operation<Context, Payload>, subcommand: Command<Payload, Result>): Command<Context, Result>;
1184
+ declare function commandChained<Context, Payload, Result = void>(information: CommandInformation, operation: Operation<Context, Payload>, subcommand: Command<Payload, Result>): Command<Context, Result>;
1183
1185
 
1184
1186
  /**
1185
1187
  * Color selection modes availables
@@ -1236,4 +1238,9 @@ declare function runAndExit<Context>(cliName: string, cliArgs: ReadonlyArray<str
1236
1238
  onExit?: ((code: number) => never) | undefined;
1237
1239
  }): Promise<never>;
1238
1240
 
1239
- export { type Command, type CommandDecoder, type CommandInformation, type CommandInterpreter, type Operation, type OperationDecoder, type OperationInterpreter, type Option, type OptionDecoder, type Positional, type PositionalDecoder, ReaderArgs, type ReaderOptionKey, type ReaderOptionParsing, type ReaderOptionValue, type ReaderOptions, type ReaderPositionals, type RunColorMode, type Type, type TypoColor, TypoError, TypoGrid, TypoString, type TypoStyle, TypoSupport, TypoText, type UsageCommand, type UsageOption, type UsagePositional, type UsageSubcommand, command, commandChained, commandWithSubcommands, operation, optionFlag, optionRepeatable, optionSingleValue, positionalOptional, positionalRequired, positionalVariadics, runAndExit, type, typeBoolean, typeBooleanValuesFalse, typeBooleanValuesTrue, typeChoice, typeConverted, typeDatetime, typeInteger, typeList, typeNumber, typePath, typeRenamed, typeTuple, typeUrl, typoStyleConstants, typoStyleFailure, typoStyleLogic, typoStyleQuote, typoStyleRegularStrong, typoStyleRegularWeaker, typoStyleTitle, typoStyleUserInput, usageToStyledLines };
1241
+ declare function suggestTextPushMessage(text: TypoText, query: string, candidates: Array<{
1242
+ reference: string;
1243
+ hint: TypoSegment;
1244
+ }>): void;
1245
+
1246
+ export { type Command, type CommandDecoder, type CommandInformation, type CommandInterpreter, type Operation, type OperationDecoder, type OperationInterpreter, type Option, type OptionDecoder, type Positional, type PositionalDecoder, ReaderArgs, type ReaderOptionGetter, type ReaderOptionLongSpec, type ReaderOptionNextGuard, type ReaderOptionRestGuard, type ReaderOptionResult, type ReaderOptionShortSpec, type ReaderOptionValue, type ReaderOptions, type ReaderPositionals, type RunColorMode, type Type, type TypoColor, TypoError, TypoGrid, type TypoSegment, TypoString, type TypoStyle, TypoSupport, TypoText, type UsageCommand, type UsageOption, type UsagePositional, type UsageSubcommand, command, commandChained, commandWithSubcommands, operation, optionFlag, optionRepeatable, optionSingleValue, positionalOptional, positionalRequired, positionalVariadics, runAndExit, suggestTextPushMessage, type, typeBoolean, typeChoice, typeConverted, typeDatetime, typeInteger, typeList, typeNumber, typePath, typeRenamed, typeTuple, typeUrl, typoStyleConstants, typoStyleFailure, typoStyleLogic, typoStyleQuote, typoStyleRegularStrong, typoStyleRegularWeaker, typoStyleTitle, typoStyleUserInput, usageToStyledLines };