@silvery/commander 0.2.0 → 0.4.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.
package/README.md CHANGED
@@ -1,23 +1,34 @@
1
1
  # @silvery/commander
2
2
 
3
- Type-safe, colorized [Commander.js](https://github.com/tj/commander.js) wrapper. Infers option types from `.option()` calls using TypeScript 5.4+ const type parameters and template literal types -- no codegen, no separate type package.
3
+ Enhanced [Commander.js](https://github.com/tj/commander.js) with auto-colorized help, Standard Schema validation, and CLI presets. Drop-in replacement -- `Command` is a subclass of Commander's `Command`.
4
+
5
+ ## Three layers
6
+
7
+ ```typescript
8
+ // Layer 1: Enhanced Commander (auto-colorized help, Standard Schema support)
9
+ import { Command, port, csv } from "@silvery/commander"
10
+
11
+ // Layer 2: Zero-dep presets (Standard Schema, standalone use)
12
+ import { port, csv, int } from "@silvery/commander/parse"
13
+
14
+ // Layer 3: Zod + CLI presets (batteries included)
15
+ import { Command, z } from "@silvery/commander"
16
+ ```
4
17
 
5
18
  ## Usage
6
19
 
7
20
  ```typescript
8
- import { createCLI } from "@silvery/commander"
21
+ import { Command, port, csv, oneOf } from "@silvery/commander"
9
22
 
10
- const cli = createCLI("myapp")
11
- .description("My CLI tool")
23
+ const program = new Command("deploy")
24
+ .description("Deploy the application")
12
25
  .version("1.0.0")
13
- .option("-v, --verbose", "Verbose output")
14
- .option("-p, --port <number>", "Port to listen on", parseInt)
15
- .option("-o, --output [path]", "Output path")
16
- .option("--no-color", "Disable color output")
17
-
18
- cli.parse()
19
- const { verbose, port, output, color } = cli.opts()
20
- // ^boolean ^number ^string|true ^boolean
26
+ .option("-p, --port <n>", "Port", port) // number (1-65535)
27
+ .option("--tags <t>", "Tags", csv) // string[]
28
+ .option("-e, --env <e>", "Env", oneOf(["dev", "staging", "prod"]))
29
+
30
+ program.parse()
31
+ const opts = program.opts()
21
32
  ```
22
33
 
23
34
  Help output is automatically colorized using Commander's built-in `configureHelp()` style hooks (headings bold, flags green, commands cyan, descriptions dim, arguments yellow).
@@ -32,103 +43,125 @@ const program = new Command("myapp").description("My CLI tool")
32
43
  colorizeHelp(program) // applies recursively to all subcommands
33
44
  ```
34
45
 
35
- ## Custom parser type inference
46
+ ## Standard Schema validation
36
47
 
37
- When `.option()` is called with a parser function as the third argument, the return type is inferred:
38
-
39
- ```typescript
40
- const cli = createCLI("deploy")
41
- .option("-p, --port <n>", "Port", parseInt) // → port: number
42
- .option("-t, --timeout <ms>", "Timeout", Number) // → timeout: number
43
- .option("--tags <items>", "Tags", (v) => v.split(",")) // → tags: string[]
44
- ```
45
-
46
- Default values can be passed as the fourth argument:
47
-
48
- ```typescript
49
- .option("-p, --port <n>", "Port", parseInt, 8080) // → port: number (defaults to 8080)
50
- ```
51
-
52
- ## Zod schema validation
53
-
54
- Pass a [Zod](https://zod.dev) schema as the third argument for combined parsing, validation, and type inference:
48
+ Pass any [Standard Schema v1](https://github.com/standard-schema/standard-schema) compatible schema as the third argument to `.option()` for combined parsing, validation, and type inference. This works with the built-in presets, Zod (>=3.24), Valibot (>=1.0), ArkType (>=2.0), and any other library implementing the standard:
55
49
 
56
50
  ```typescript
51
+ import { Command } from "@silvery/commander"
57
52
  import { z } from "zod"
58
53
 
59
- const cli = createCLI("deploy")
54
+ const program = new Command("deploy")
60
55
  .option("-p, --port <n>", "Port", z.coerce.number().min(1).max(65535))
61
- // port: number (validated at parse time)
62
-
63
- .option("-e, --env <env>", "Environment", z.enum(["dev", "staging", "prod"]))
64
- // → env: "dev" | "staging" | "prod" (union type)
65
-
56
+ .option("-e, --env <env>", "Env", z.enum(["dev", "staging", "prod"]))
66
57
  .option(
67
58
  "--tags <t>",
68
59
  "Tags",
69
60
  z.string().transform((v) => v.split(",")),
70
61
  )
71
- // → tags: string[] (transformed)
72
62
  ```
73
63
 
74
- Zod is an optional peer dependency -- duck-typed at runtime, never imported at the top level. If Zod validation fails, the error is formatted as a Commander-style error message.
64
+ Schema libraries are optional peer dependencies -- detected at runtime via the Standard Schema `~standard` interface, never imported at the top level. A legacy fallback supports older Zod versions (pre-3.24) that don't implement Standard Schema yet.
75
65
 
76
- ## Typed action handlers
66
+ ## Zod CLI presets
77
67
 
78
- Action callbacks receive typed arguments and options:
68
+ Import `z` from `@silvery/commander` for an extended Zod object with CLI-specific schemas:
79
69
 
80
70
  ```typescript
81
- const cli = createCLI("deploy")
82
- .argument("<env>", "Target environment")
83
- .argument("[tag]", "Optional deploy tag")
84
- .option("-f, --force", "Force deploy")
85
- .action((env, tag, opts) => {
86
- // env: string, tag: string | undefined, opts: { force: boolean | undefined }
87
- })
71
+ import { Command, z } from "@silvery/commander"
72
+
73
+ const program = new Command("deploy")
74
+ .option("-p, --port <n>", "Port", z.port) // z.coerce.number().int().min(1).max(65535)
75
+ .option("--tags <t>", "Tags", z.csv) // z.string().transform(...)
76
+ .option("-e, --env <e>", "Env", z.oneOf(["dev", "staging", "prod"]))
77
+ .option("-r, --retries <n>", "Retries", z.int) // z.coerce.number().int()
88
78
  ```
89
79
 
90
- Required arguments (`<name>`) are `string`, optional arguments (`[name]`) are `string | undefined`.
80
+ The `z` export is tree-shakeable -- if you don't import it, Zod won't be in your bundle.
81
+
82
+ Available `z` CLI presets: `z.port`, `z.int`, `z.uint`, `z.float`, `z.csv`, `z.url`, `z.path`, `z.email`, `z.date`, `z.json`, `z.bool`, `z.intRange(min, max)`, `z.oneOf(values)`.
91
83
 
92
- ## Choices narrowing
84
+ ## Presets
93
85
 
94
- Use `.optionWithChoices()` to restrict an option to a fixed set of values with union type inference:
86
+ Pre-built validators for common CLI argument patterns. Each preset implements [Standard Schema v1](https://github.com/standard-schema/standard-schema) and works with Commander's `.option()` or standalone.
95
87
 
96
88
  ```typescript
97
- const cli = createCLI("deploy").optionWithChoices("-e, --env <env>", "Environment", ["dev", "staging", "prod"] as const)
98
- // → env: "dev" | "staging" | "prod" | undefined
89
+ import { Command, port, csv, int, url, oneOf } from "@silvery/commander"
90
+
91
+ const program = new Command("deploy")
92
+ .option("-p, --port <n>", "Port", port) // number (1-65535, validated)
93
+ .option("-r, --retries <n>", "Retries", int) // number (integer)
94
+ .option("--tags <t>", "Tags", csv) // string[]
95
+ .option("--callback <url>", "Callback", url) // string (validated URL)
96
+ .option("-e, --env <e>", "Env", oneOf(["dev", "staging", "prod"]))
97
+ ```
98
+
99
+ ### Standalone usage
100
+
101
+ Presets also work outside Commander for validating env vars, config files, etc. Import from the `@silvery/commander/parse` subpath for tree-shaking:
102
+
103
+ ```typescript
104
+ import { port, csv, oneOf } from "@silvery/commander/parse"
105
+
106
+ // .parse() — returns value or throws
107
+ const dbPort = port.parse(process.env.DB_PORT ?? "5432") // 3000
99
108
 
100
- cli.parse()
101
- const { env } = cli.opts() // env: "dev" | "staging" | "prod" | undefined
109
+ // .safeParse() — returns result object, never throws
110
+ const result = port.safeParse("abc")
111
+ // { success: false, issues: [{ message: 'Expected port (1-65535), got "abc"' }] }
112
+
113
+ // Standard Schema ~standard.validate() also available
114
+ const validated = port["~standard"].validate("8080")
115
+ // { value: 8080 }
102
116
  ```
103
117
 
104
- Commander validates the choice at parse time and rejects invalid values.
118
+ ### Available presets
119
+
120
+ | Preset | Type | Validation |
121
+ | ------- | ---------- | ---------------------------------------- |
122
+ | `int` | `number` | Integer (coerced from string) |
123
+ | `uint` | `number` | Unsigned integer (>= 0) |
124
+ | `float` | `number` | Any finite number (rejects NaN) |
125
+ | `port` | `number` | Integer 1-65535 |
126
+ | `url` | `string` | Valid URL (via `URL` constructor) |
127
+ | `path` | `string` | Non-empty string |
128
+ | `csv` | `string[]` | Comma-separated, trimmed, empty filtered |
129
+ | `json` | `unknown` | Parsed JSON |
130
+ | `bool` | `boolean` | true/false/yes/no/1/0 (case-insensitive) |
131
+ | `date` | `Date` | Valid date string |
132
+ | `email` | `string` | Basic email validation (has @ and .) |
133
+ | `regex` | `RegExp` | Valid regex pattern |
134
+
135
+ ### Factory presets
105
136
 
106
- ## Environment variable support
137
+ ```typescript
138
+ import { intRange, oneOf } from "@silvery/commander"
107
139
 
108
- Chain `.env()` to set an environment variable fallback for the last-added option:
140
+ intRange(1, 100) // Preset<number> integer within bounds
141
+ oneOf(["a", "b", "c"]) // Preset<"a" | "b" | "c"> — enum from values
142
+ ```
143
+
144
+ ## Custom parser type inference
145
+
146
+ When `.option()` is called with a parser function as the third argument, Commander infers the return type:
109
147
 
110
148
  ```typescript
111
- .option("-p, --port <n>", "Port").env("PORT")
149
+ const program = new Command("deploy")
150
+ .option("-p, --port <n>", "Port", parseInt) // port: number
151
+ .option("-t, --timeout <ms>", "Timeout", Number) // timeout: number
152
+ .option("--tags <items>", "Tags", (v) => v.split(",")) // tags: string[]
112
153
  ```
113
154
 
114
- ## Improvements over @commander-js/extra-typings
155
+ Default values can be passed as the fourth argument:
115
156
 
116
- | Feature | extra-typings | @silvery/commander |
117
- | ---------------------- | --------------------------------------------------- | --------------------------------------------------------------- |
118
- | Type inference | 1536-line .d.ts with recursive generic accumulation | ~120 lines using TS 5.4+ const type params + template literals |
119
- | Custom parser types | Yes (.option with parseFloat -> number) | Yes (parser return type inference) |
120
- | Zod schema support | No | Yes (parse + validate + infer from Zod schemas) |
121
- | Typed action handlers | Yes (full signature inference) | Yes (arguments + options) |
122
- | Choices narrowing | Via .addOption() | Via .optionWithChoices() |
123
- | Colorized help | Not included | Built-in via Commander's native style hooks |
124
- | Package size | Types only (25 lines runtime) | Types + colorizer + Zod bridge (~300 lines, zero required deps) |
125
- | Installation | Separate package alongside commander | Single package, re-exports Commander |
126
- | Negated flags (--no-X) | Partial | Key extraction + boolean type inference |
157
+ ```typescript
158
+ .option("-p, --port <n>", "Port", parseInt, 8080) // port: number (defaults to 8080)
159
+ ```
127
160
 
128
161
  ## Credits
129
162
 
130
163
  - [Commander.js](https://github.com/tj/commander.js) by TJ Holowaychuk and contributors -- the underlying CLI framework
131
- - [@commander-js/extra-typings](https://github.com/commander-js/extra-typings) -- inspired the type inference approach; our implementation uses modern TypeScript features (const type parameters, template literal types) to achieve similar results in fewer lines
164
+ - [Standard Schema](https://github.com/standard-schema/standard-schema) -- universal schema interop protocol for type-safe validation
132
165
  - [@silvery/ansi](https://github.com/beorn/silvery/tree/main/packages/ansi) -- optional ANSI color detection for respecting NO_COLOR/FORCE_COLOR/terminal capabilities
133
166
  - Uses Commander's built-in `configureHelp()` style hooks (added in Commander 12) for colorization
134
167
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silvery/commander",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Colorized Commander.js help output using ANSI escape codes",
5
5
  "keywords": [
6
6
  "ansi",
@@ -20,25 +20,22 @@
20
20
  "src"
21
21
  ],
22
22
  "type": "module",
23
+ "sideEffects": false,
23
24
  "exports": {
24
- ".": "./src/index.ts"
25
+ ".": "./src/index.ts",
26
+ "./parse": "./src/presets.ts"
25
27
  },
26
28
  "publishConfig": {
27
29
  "access": "public"
28
30
  },
31
+ "dependencies": {
32
+ "commander": ">=12.0.0"
33
+ },
29
34
  "peerDependencies": {
30
- "@commander-js/extra-typings": ">=12.0.0",
31
35
  "@silvery/ansi": ">=0.1.0",
32
- "commander": ">=12.0.0",
33
36
  "zod": ">=3.0.0"
34
37
  },
35
38
  "peerDependenciesMeta": {
36
- "commander": {
37
- "optional": true
38
- },
39
- "@commander-js/extra-typings": {
40
- "optional": true
41
- },
42
39
  "@silvery/ansi": {
43
40
  "optional": true
44
41
  },
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Commander.js help colorization using ANSI escape codes.
3
+ *
4
+ * Uses Commander's built-in style hooks (styleTitle, styleOptionText, etc.)
5
+ * rather than regex post-processing. Works with @silvery/commander
6
+ * or plain commander — accepts a minimal CommandLike interface so Commander
7
+ * is a peer dependency, not a hard one.
8
+ *
9
+ * Zero dependencies — only raw ANSI escape codes.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { Command } from "@silvery/commander"
14
+ * import { colorizeHelp } from "@silvery/commander"
15
+ *
16
+ * const program = new Command("myapp").description("My CLI tool")
17
+ * colorizeHelp(program)
18
+ * ```
19
+ */
20
+
21
+ // Raw ANSI escape codes — no framework dependencies.
22
+ const RESET = "\x1b[0m"
23
+ const BOLD = "\x1b[1m"
24
+ const DIM = "\x1b[2m"
25
+ const CYAN = "\x1b[36m"
26
+ const GREEN = "\x1b[32m"
27
+ const YELLOW = "\x1b[33m"
28
+
29
+ /**
30
+ * Check if color output should be enabled.
31
+ * Uses @silvery/ansi detectColor() if available, falls back to basic
32
+ * NO_COLOR/FORCE_COLOR/isTTY checks.
33
+ */
34
+ let _shouldColorize: boolean | undefined
35
+
36
+ export function shouldColorize(): boolean {
37
+ if (_shouldColorize !== undefined) return _shouldColorize
38
+
39
+ // Try @silvery/ansi for full detection (respects NO_COLOR, FORCE_COLOR, TERM, etc.)
40
+ try {
41
+ const { detectColor } = require("@silvery/ansi") as { detectColor: (stdout: NodeJS.WriteStream) => string | null }
42
+ _shouldColorize = detectColor(process.stdout) !== null
43
+ } catch {
44
+ // Fallback: basic NO_COLOR / FORCE_COLOR / isTTY checks
45
+ if (process.env.NO_COLOR !== undefined) {
46
+ _shouldColorize = false
47
+ } else if (process.env.FORCE_COLOR !== undefined) {
48
+ _shouldColorize = true
49
+ } else {
50
+ _shouldColorize = process.stdout?.isTTY ?? true
51
+ }
52
+ }
53
+
54
+ return _shouldColorize
55
+ }
56
+
57
+ /** Wrap a string with ANSI codes, handling nested resets. */
58
+ function ansi(text: string, code: string): string {
59
+ return `${code}${text}${RESET}`
60
+ }
61
+
62
+ /**
63
+ * Minimal interface for Commander's Command — avoids requiring Commander
64
+ * as a direct dependency. Works with both `commander` and
65
+ * `@silvery/commander`.
66
+ *
67
+ * Uses permissive types to ensure structural compatibility with all
68
+ * Commander versions, overloads, and generic instantiations.
69
+ */
70
+ export interface CommandLike {
71
+ // biome-ignore lint: permissive to match Commander's overloaded signatures
72
+ configureHelp(...args: any[]): any
73
+ // biome-ignore lint: permissive to match Commander's overloaded signatures
74
+ configureOutput(...args: any[]): any
75
+ // biome-ignore lint: permissive to match Commander's Command[] structurally
76
+ readonly commands: readonly any[]
77
+ }
78
+
79
+ /** Color scheme for help output. Values are raw ANSI escape sequences. */
80
+ export interface ColorizeHelpOptions {
81
+ /** ANSI code for command/subcommand names. Default: cyan */
82
+ commands?: string
83
+ /** ANSI code for --flags and -short options. Default: green */
84
+ flags?: string
85
+ /** ANSI code for description text. Default: dim */
86
+ description?: string
87
+ /** ANSI code for section headings (Usage:, Options:, etc.). Default: bold */
88
+ heading?: string
89
+ /** ANSI code for <required> and [optional] argument brackets. Default: yellow */
90
+ brackets?: string
91
+ }
92
+
93
+ /**
94
+ * Apply colorized help output to a Commander.js program and all its subcommands.
95
+ *
96
+ * Uses Commander's built-in `configureHelp()` style hooks rather than
97
+ * post-processing the formatted string. This approach is robust against
98
+ * formatting changes in Commander and handles wrapping correctly.
99
+ *
100
+ * @param program - A Commander Command instance (or compatible object)
101
+ * @param options - Override default ANSI color codes for each element
102
+ */
103
+ export function colorizeHelp(program: CommandLike, options?: ColorizeHelpOptions): void {
104
+ const cmds = options?.commands ?? CYAN
105
+ const flags = options?.flags ?? GREEN
106
+ const desc = options?.description ?? DIM
107
+ const heading = options?.heading ?? BOLD
108
+ const brackets = options?.brackets ?? YELLOW
109
+
110
+ const helpConfig: Record<string, unknown> = {
111
+ // Section headings: "Usage:", "Options:", "Commands:", "Arguments:"
112
+ styleTitle(str: string): string {
113
+ return ansi(str, heading)
114
+ },
115
+
116
+ // Command name in usage line and subcommand terms
117
+ styleCommandText(str: string): string {
118
+ return ansi(str, cmds)
119
+ },
120
+
121
+ // Option terms: "-v, --verbose", "--repo <path>", "[options]"
122
+ styleOptionText(str: string): string {
123
+ return ansi(str, flags)
124
+ },
125
+
126
+ // Subcommand names in the commands list
127
+ styleSubcommandText(str: string): string {
128
+ return ansi(str, cmds)
129
+ },
130
+
131
+ // Argument terms: "<file>", "[dir]"
132
+ styleArgumentText(str: string): string {
133
+ return ansi(str, brackets)
134
+ },
135
+
136
+ // Description text for options, subcommands, arguments
137
+ styleDescriptionText(str: string): string {
138
+ return ansi(str, desc)
139
+ },
140
+
141
+ // Command description (the main program description line) — keep normal
142
+ styleCommandDescription(str: string): string {
143
+ return str
144
+ },
145
+ }
146
+
147
+ program.configureHelp(helpConfig)
148
+
149
+ // Tell Commander that color output is supported, even when stdout is not
150
+ // a TTY (e.g., piped output, CI, tests). Without this, Commander strips
151
+ // all ANSI codes from helpInformation() output.
152
+ //
153
+ // Callers who want to respect NO_COLOR/FORCE_COLOR should check
154
+ // shouldColorize() before calling colorizeHelp().
155
+ program.configureOutput({
156
+ getOutHasColors: () => true,
157
+ getErrHasColors: () => true,
158
+ })
159
+
160
+ // Apply recursively to all existing subcommands
161
+ for (const sub of program.commands) {
162
+ colorizeHelp(sub, options)
163
+ }
164
+ }
package/src/command.ts ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Enhanced Commander Command with auto-colorized help and Standard Schema support.
3
+ *
4
+ * Subclasses Commander's Command so `new Command("app")` just works —
5
+ * it's Commander with auto-colorized help and automatic Standard Schema /
6
+ * legacy Zod detection in `.option()`.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { Command } from "@silvery/commander"
11
+ * import { port, csv } from "@silvery/commander/parse"
12
+ *
13
+ * const program = new Command("myapp")
14
+ * .description("My CLI tool")
15
+ * .version("1.0.0")
16
+ * .option("-p, --port <n>", "Port", port)
17
+ * .option("--tags <t>", "Tags", csv)
18
+ *
19
+ * program.parse()
20
+ * ```
21
+ */
22
+
23
+ import { Command as BaseCommand } from "commander"
24
+ import { colorizeHelp } from "./colorize.ts"
25
+ import type { StandardSchemaV1 } from "./presets.ts"
26
+
27
+ // --- Standard Schema support ---
28
+
29
+ /** Runtime check: is this value a Standard Schema v1 object? */
30
+ function isStandardSchema(value: unknown): value is StandardSchemaV1 {
31
+ return typeof value === "object" && value !== null && "~standard" in (value as any)
32
+ }
33
+
34
+ /** Wrap a Standard Schema as a Commander parser function */
35
+ function standardSchemaParser<T>(schema: StandardSchemaV1<T>): (value: string) => T {
36
+ return (value: string) => {
37
+ const result = schema["~standard"].validate(value)
38
+ if ("issues" in result) {
39
+ const msg = result.issues.map((i) => i.message).join(", ")
40
+ throw new Error(msg)
41
+ }
42
+ return result.value
43
+ }
44
+ }
45
+
46
+ // --- Legacy Zod support (pre-3.24, no ~standard) ---
47
+
48
+ /**
49
+ * Duck-type interface for older Zod schemas that don't implement Standard Schema.
50
+ * Any object with `parse(value: string) => T` and `_def` qualifies.
51
+ */
52
+ interface ZodLike<T = any> {
53
+ parse(value: unknown): T
54
+ _def: unknown
55
+ }
56
+
57
+ /** Runtime check: is this value a legacy Zod-like schema (without Standard Schema)? */
58
+ function isLegacyZodSchema(value: unknown): value is ZodLike {
59
+ return (
60
+ typeof value === "object" &&
61
+ value !== null &&
62
+ typeof (value as any).parse === "function" &&
63
+ "_def" in (value as any) &&
64
+ !("~standard" in (value as any))
65
+ )
66
+ }
67
+
68
+ /** Wrap a legacy Zod schema as a Commander parser function */
69
+ function legacyZodParser<T>(schema: ZodLike<T>): (value: string) => T {
70
+ return (value: string) => {
71
+ try {
72
+ return schema.parse(value)
73
+ } catch (err: any) {
74
+ // Format Zod errors as Commander-style messages
75
+ if (err?.issues) {
76
+ const messages = err.issues.map((i: any) => i.message).join(", ")
77
+ throw new Error(messages)
78
+ }
79
+ throw err
80
+ }
81
+ }
82
+ }
83
+
84
+ export class Command extends BaseCommand {
85
+ constructor(name?: string) {
86
+ super(name)
87
+ colorizeHelp(this as any)
88
+ }
89
+
90
+ /**
91
+ * Add an option with automatic Standard Schema / legacy Zod detection.
92
+ *
93
+ * When the third argument is a Standard Schema v1 object (Zod >=3.24,
94
+ * Valibot >=1.0, ArkType >=2.0, or @silvery/commander presets), it's
95
+ * automatically wrapped as a Commander parser function.
96
+ *
97
+ * When the third argument is a legacy Zod schema (pre-3.24, has `_def`
98
+ * and `parse` but no `~standard`), it's also wrapped automatically.
99
+ */
100
+ option(flags: string, description?: string, parseArgOrDefault?: any, defaultValue?: any): this {
101
+ if (isStandardSchema(parseArgOrDefault)) {
102
+ return super.option(flags, description ?? "", standardSchemaParser(parseArgOrDefault))
103
+ }
104
+ if (isLegacyZodSchema(parseArgOrDefault)) {
105
+ return super.option(flags, description ?? "", legacyZodParser(parseArgOrDefault))
106
+ }
107
+ if (typeof parseArgOrDefault === "function") {
108
+ return super.option(flags, description ?? "", parseArgOrDefault, defaultValue)
109
+ }
110
+ return super.option(flags, description ?? "", parseArgOrDefault)
111
+ }
112
+
113
+ // Subcommands also get colorized help and Standard Schema support
114
+ createCommand(name?: string): Command {
115
+ return new Command(name)
116
+ }
117
+ }