padrone 1.3.0 → 1.5.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +105 -284
  3. package/dist/{args-DFEI7_G_.mjs → args-D5PNDyNu.mjs} +46 -21
  4. package/dist/args-D5PNDyNu.mjs.map +1 -0
  5. package/dist/chunk-CjcI7cDX.mjs +15 -0
  6. package/dist/codegen/index.d.mts +28 -3
  7. package/dist/codegen/index.d.mts.map +1 -1
  8. package/dist/codegen/index.mjs +169 -19
  9. package/dist/codegen/index.mjs.map +1 -1
  10. package/dist/command-utils-B1D-HqCd.mjs +1117 -0
  11. package/dist/command-utils-B1D-HqCd.mjs.map +1 -0
  12. package/dist/completion.d.mts +1 -1
  13. package/dist/completion.d.mts.map +1 -1
  14. package/dist/completion.mjs +77 -29
  15. package/dist/completion.mjs.map +1 -1
  16. package/dist/docs/index.d.mts +22 -2
  17. package/dist/docs/index.d.mts.map +1 -1
  18. package/dist/docs/index.mjs +94 -7
  19. package/dist/docs/index.mjs.map +1 -1
  20. package/dist/errors-BiVrBgi6.mjs +114 -0
  21. package/dist/errors-BiVrBgi6.mjs.map +1 -0
  22. package/dist/{formatter-XroimS3Q.d.mts → formatter-DtHzbP22.d.mts} +35 -5
  23. package/dist/formatter-DtHzbP22.d.mts.map +1 -0
  24. package/dist/help-bbmu9-qd.mjs +735 -0
  25. package/dist/help-bbmu9-qd.mjs.map +1 -0
  26. package/dist/index.d.mts +32 -3
  27. package/dist/index.d.mts.map +1 -1
  28. package/dist/index.mjs +495 -267
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/mcp-mLWIdUIu.mjs +379 -0
  31. package/dist/mcp-mLWIdUIu.mjs.map +1 -0
  32. package/dist/serve-B0u43DK7.mjs +404 -0
  33. package/dist/serve-B0u43DK7.mjs.map +1 -0
  34. package/dist/stream-BcC146Ud.mjs +56 -0
  35. package/dist/stream-BcC146Ud.mjs.map +1 -0
  36. package/dist/test.d.mts +1 -1
  37. package/dist/test.mjs +4 -15
  38. package/dist/test.mjs.map +1 -1
  39. package/dist/{types-BS7RP5Ls.d.mts → types-Ch8Mk6Qb.d.mts} +311 -63
  40. package/dist/types-Ch8Mk6Qb.d.mts.map +1 -0
  41. package/dist/{update-check-EbNDkzyV.mjs → update-check-CFX1FV3v.mjs} +2 -2
  42. package/dist/{update-check-EbNDkzyV.mjs.map → update-check-CFX1FV3v.mjs.map} +1 -1
  43. package/dist/zod.d.mts +32 -0
  44. package/dist/zod.d.mts.map +1 -0
  45. package/dist/zod.mjs +50 -0
  46. package/dist/zod.mjs.map +1 -0
  47. package/package.json +10 -2
  48. package/src/args.ts +76 -44
  49. package/src/cli/docs.ts +1 -7
  50. package/src/cli/doctor.ts +195 -10
  51. package/src/cli/index.ts +1 -1
  52. package/src/cli/init.ts +2 -3
  53. package/src/cli/link.ts +2 -2
  54. package/src/codegen/discovery.ts +80 -28
  55. package/src/codegen/index.ts +2 -1
  56. package/src/codegen/parsers/bash.ts +179 -0
  57. package/src/codegen/schema-to-code.ts +2 -1
  58. package/src/colorizer.ts +126 -13
  59. package/src/command-utils.ts +401 -23
  60. package/src/completion.ts +120 -47
  61. package/src/create.ts +483 -130
  62. package/src/docs/index.ts +122 -8
  63. package/src/formatter.ts +173 -125
  64. package/src/help.ts +46 -12
  65. package/src/index.ts +29 -1
  66. package/src/interactive.ts +45 -4
  67. package/src/mcp.ts +390 -0
  68. package/src/repl-loop.ts +16 -3
  69. package/src/runtime.ts +195 -2
  70. package/src/serve.ts +442 -0
  71. package/src/stream.ts +75 -0
  72. package/src/test.ts +7 -16
  73. package/src/type-utils.ts +28 -4
  74. package/src/types.ts +212 -30
  75. package/src/wrap.ts +23 -25
  76. package/src/zod.ts +50 -0
  77. package/dist/args-DFEI7_G_.mjs.map +0 -1
  78. package/dist/chunk-y_GBKt04.mjs +0 -5
  79. package/dist/formatter-XroimS3Q.d.mts.map +0 -1
  80. package/dist/help-CgGP7hQU.mjs +0 -1229
  81. package/dist/help-CgGP7hQU.mjs.map +0 -1
  82. package/dist/types-BS7RP5Ls.d.mts.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { n as asyncStreamRegistry } from "./stream-BcC146Ud.mjs";
1
2
  //#region src/args.ts
2
3
  /**
3
4
  * Convert a camelCase string to kebab-case.
@@ -8,17 +9,37 @@ function camelToKebab(str) {
8
9
  return str.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);
9
10
  }
10
11
  /**
11
- * Normalizes stdin config into its explicit form.
12
+ * Returns the stdin field name from the config.
12
13
  */
13
14
  function parseStdinConfig(stdin) {
14
- if (typeof stdin === "string") return {
15
- field: stdin,
16
- as: "text"
17
- };
18
- return {
19
- field: stdin.field,
20
- as: stdin.as ?? "text"
21
- };
15
+ return stdin;
16
+ }
17
+ /** Options passed to `jsonSchema.input()` to handle unrepresentable types like `z.custom()`. */
18
+ const JSON_SCHEMA_OPTS = {
19
+ target: "draft-2020-12",
20
+ libraryOptions: { unrepresentable: "any" }
21
+ };
22
+ function getFieldJsonSchema(schema, field) {
23
+ if (!schema) return void 0;
24
+ try {
25
+ const jsonSchema = schema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS);
26
+ if (jsonSchema.type === "object" && jsonSchema.properties) return jsonSchema.properties[field];
27
+ } catch {}
28
+ }
29
+ /**
30
+ * Checks if a field in the schema is an array type (e.g. `z.string().array()`).
31
+ */
32
+ function isArrayField(schema, field) {
33
+ return getFieldJsonSchema(schema, field)?.type === "array";
34
+ }
35
+ /**
36
+ * Checks if a field is an async stream (marked with `asyncStream()` metadata).
37
+ * Returns the item schema if provided, or `true` if it's a plain string stream.
38
+ */
39
+ function isAsyncStreamField(schema, field) {
40
+ const asyncStreamId = getFieldJsonSchema(schema, field)?.asyncStream;
41
+ if (asyncStreamId && asyncStreamRegistry.has(asyncStreamId)) return { itemSchema: asyncStreamRegistry.get(asyncStreamId)?.itemSchema };
42
+ return false;
22
43
  }
23
44
  /**
24
45
  * Parse positional configuration to extract names and variadic info.
@@ -50,7 +71,7 @@ function extractSchemaMetadata(schema, meta, autoAlias) {
50
71
  if (value.alias) addEntries(aliases, key, value.alias, (item) => item.length > 1);
51
72
  }
52
73
  try {
53
- const jsonSchema = schema["~standard"].jsonSchema.input({ target: "draft-2020-12" });
74
+ const jsonSchema = schema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS);
54
75
  if (jsonSchema.type === "object" && jsonSchema.properties) for (const [propertyName, propertySchema] of Object.entries(jsonSchema.properties)) {
55
76
  if (!propertySchema) continue;
56
77
  const propFlags = propertySchema.flags;
@@ -113,7 +134,7 @@ function preprocessArgs(data, ctx) {
113
134
  function coerceArgs(data, schema) {
114
135
  let properties;
115
136
  try {
116
- const jsonSchema = schema["~standard"].jsonSchema.input({ target: "draft-2020-12" });
137
+ const jsonSchema = schema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS);
117
138
  if (jsonSchema.type !== "object" || !jsonSchema.properties) return data;
118
139
  properties = jsonSchema.properties;
119
140
  } catch {
@@ -131,25 +152,29 @@ function coerceArgs(data, schema) {
131
152
  }
132
153
  } else if (targetType === "boolean") {
133
154
  if (typeof value === "string") {
134
- if (value === "true" || value === "1") result[key] = true;
135
- else if (value === "false" || value === "0") result[key] = false;
155
+ const lower = value.toLowerCase();
156
+ if (lower === "true" || lower === "1" || lower === "yes" || lower === "on") result[key] = true;
157
+ else if (lower === "false" || lower === "0" || lower === "no" || lower === "off") result[key] = false;
136
158
  }
137
- } else if (targetType === "array" && Array.isArray(value)) {
159
+ } else if (targetType === "array") {
160
+ const arr = Array.isArray(value) ? value : [value];
138
161
  const itemType = prop.items?.type;
139
- if (itemType === "number" || itemType === "integer") result[key] = value.map((v) => {
162
+ if (itemType === "number" || itemType === "integer") result[key] = arr.map((v) => {
140
163
  if (typeof v === "string") {
141
164
  const num = Number(v);
142
165
  return Number.isNaN(num) ? v : num;
143
166
  }
144
167
  return v;
145
168
  });
146
- else if (itemType === "boolean") result[key] = value.map((v) => {
169
+ else if (itemType === "boolean") result[key] = arr.map((v) => {
147
170
  if (typeof v === "string") {
148
- if (v === "true" || v === "1") return true;
149
- if (v === "false" || v === "0") return false;
171
+ const lower = v.toLowerCase();
172
+ if (lower === "true" || lower === "1" || lower === "yes" || lower === "on") return true;
173
+ if (lower === "false" || lower === "0" || lower === "no" || lower === "off") return false;
150
174
  }
151
175
  return v;
152
176
  });
177
+ else if (!Array.isArray(value)) result[key] = arr;
153
178
  }
154
179
  }
155
180
  return result;
@@ -165,7 +190,7 @@ function detectUnknownArgs(data, schema, flags, aliases, suggestFn) {
165
190
  let properties;
166
191
  let isLoose = false;
167
192
  try {
168
- const jsonSchema = schema["~standard"].jsonSchema.input({ target: "draft-2020-12" });
193
+ const jsonSchema = schema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS);
169
194
  if (jsonSchema.type !== "object" || !jsonSchema.properties) return [];
170
195
  properties = jsonSchema.properties;
171
196
  if (jsonSchema.additionalProperties !== void 0 && jsonSchema.additionalProperties !== false) isLoose = true;
@@ -192,6 +217,6 @@ function detectUnknownArgs(data, schema, flags, aliases, suggestFn) {
192
217
  return unknowns;
193
218
  }
194
219
  //#endregion
195
- export { parsePositionalConfig as a, extractSchemaMetadata as i, coerceArgs as n, parseStdinConfig as o, detectUnknownArgs as r, preprocessArgs as s, camelToKebab as t };
220
+ export { extractSchemaMetadata as a, parsePositionalConfig as c, detectUnknownArgs as i, parseStdinConfig as l, camelToKebab as n, isArrayField as o, coerceArgs as r, isAsyncStreamField as s, JSON_SCHEMA_OPTS as t, preprocessArgs as u };
196
221
 
197
- //# sourceMappingURL=args-DFEI7_G_.mjs.map
222
+ //# sourceMappingURL=args-D5PNDyNu.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args-D5PNDyNu.mjs","names":[],"sources":["../src/args.ts"],"sourcesContent":["import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';\nimport { asyncStreamRegistry } from './stream.ts';\n\ntype Letter =\n | 'a'\n | 'b'\n | 'c'\n | 'd'\n | 'e'\n | 'f'\n | 'g'\n | 'h'\n | 'i'\n | 'j'\n | 'k'\n | 'l'\n | 'm'\n | 'n'\n | 'o'\n | 'p'\n | 'q'\n | 'r'\n | 's'\n | 't'\n | 'u'\n | 'v'\n | 'w'\n | 'x'\n | 'y'\n | 'z';\n\n/** A single letter character, valid as a short CLI flag (e.g. `'v'`, `'n'`, `'V'`). */\nexport type SingleChar = Letter | Uppercase<Letter>;\n\nexport interface PadroneFieldMeta {\n description?: string;\n /** Single-character short flags (stackable: `-abc` = `-a -b -c`). Used with single dash. */\n flags?: readonly SingleChar[] | SingleChar;\n /** Multi-character alternative long names. Used with double dash (e.g. `--dry-run` for `--dryRun`). */\n alias?: readonly string[] | string;\n deprecated?: boolean | string;\n hidden?: boolean;\n examples?: readonly unknown[];\n /** Group name for organizing this option under a labeled section in help output. */\n group?: string;\n}\n\ntype PositionalArgs<TObj> =\n TObj extends Record<string, any>\n ? {\n [K in keyof TObj]: NonNullable<TObj[K]> extends Array<any> ? `...${K & string}` | (K & string) : K & string;\n }[keyof TObj]\n : string;\n\n/**\n * Meta configuration for arguments, including positional arguments.\n * The `positional` array defines which arguments are positional and their order.\n * Use '...name' prefix to indicate variadic (rest) arguments, matching JS/TS rest syntax.\n *\n * @example\n * ```ts\n * .arguments(schema, {\n * positional: ['source', '...files', 'dest'], // '...files' is variadic\n * })\n * ```\n */\n/**\n * Configuration for reading from stdin and mapping it to an argument field.\n * Simply specify the field name — the read mode is inferred from the schema:\n * - `string` field → reads all stdin as text\n * - `string[]` field → reads stdin line-by-line\n */\nexport type StdinConfig<TObj = Record<string, any>> = keyof TObj & string;\n\nexport interface PadroneArgsSchemaMeta<TObj = Record<string, any>> {\n /**\n * Array of argument names that should be treated as positional arguments.\n * Order in array determines position. Use '...name' prefix for variadic args.\n * @example ['source', '...files', 'dest'] - 'files' captures multiple values\n */\n positional?: readonly PositionalArgs<TObj>[];\n /**\n * Per-argument metadata.\n */\n fields?: { [K in keyof TObj]?: PadroneFieldMeta };\n /**\n * Automatically generate kebab-case aliases for camelCase option names.\n * For example, `dryRun` automatically gets `--dry-run` as an alias.\n * Defaults to `true`. Set to `false` to disable.\n *\n * @default true\n * @example\n * ```ts\n * // Auto-aliases enabled (default): --dry-run → dryRun\n * .arguments(z.object({ dryRun: z.boolean() }))\n *\n * // Disable auto-aliases\n * .arguments(z.object({ dryRun: z.boolean() }), { autoAlias: false })\n * ```\n */\n autoAlias?: boolean;\n /**\n * Read from stdin and inject the data into the specified argument field.\n * Only reads when stdin is piped (not a TTY) and the field wasn't already provided via CLI flags.\n *\n * The read mode is inferred from the schema type of the target field:\n * - `string` field → reads all stdin as a single string\n * - `string[]` field → reads stdin line-by-line into an array\n *\n * Precedence: CLI flags > stdin > env vars > config file > schema defaults.\n *\n * @example\n * ```ts\n * // Read all stdin as text into 'data' field\n * .arguments(z.object({ data: z.string() }), { stdin: 'data' })\n *\n * // Read stdin lines into 'lines' field (inferred from array schema)\n * .arguments(z.object({ lines: z.string().array() }), { stdin: 'lines' })\n * ```\n */\n stdin?: StdinConfig<TObj>;\n /**\n * Fields to interactively prompt for when their values are missing after CLI/env/config resolution.\n * - `true`: prompt for all required fields that are missing.\n * - `string[]`: prompt for these specific fields if missing.\n *\n * Interactive prompting only occurs in `cli()` when the runtime has `interactive: true`.\n * Setting this makes `parse()` and `cli()` return Promises.\n *\n * @example\n * ```ts\n * .arguments(schema, {\n * interactive: true, // prompt all missing required fields\n * interactive: ['name', 'template'], // prompt only these fields\n * })\n * ```\n */\n interactive?: true | readonly (keyof TObj & string)[];\n /**\n * Optional fields offered after required interactive prompts.\n * Users are shown a multi-select to choose which of these fields to configure.\n * - `true`: offer all optional fields that are missing.\n * - `string[]`: offer these specific fields.\n *\n * @example\n * ```ts\n * .arguments(schema, {\n * interactive: ['name'],\n * optionalInteractive: ['typescript', 'eslint', 'prettier'],\n * })\n * ```\n */\n optionalInteractive?: true | readonly (keyof TObj & string)[];\n}\n\n/**\n * Convert a camelCase string to kebab-case.\n * Returns null if the string has no uppercase letters (no conversion needed).\n */\nexport function camelToKebab(str: string): string | null {\n if (!/[A-Z]/.test(str)) return null;\n return str.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);\n}\n\n/**\n * Returns the stdin field name from the config.\n */\nexport function parseStdinConfig(stdin: StdinConfig): string {\n return stdin;\n}\n\n/** Options passed to `jsonSchema.input()` to handle unrepresentable types like `z.custom()`. */\nexport const JSON_SCHEMA_OPTS = { target: 'draft-2020-12', libraryOptions: { unrepresentable: 'any' } } as const;\n\nfunction getFieldJsonSchema(schema: StandardJSONSchemaV1 | undefined, field: string): Record<string, any> | undefined {\n if (!schema) return undefined;\n try {\n const jsonSchema = schema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;\n if (jsonSchema.type === 'object' && jsonSchema.properties) return jsonSchema.properties[field];\n } catch {}\n return undefined;\n}\n\n/**\n * Checks if a field in the schema is an array type (e.g. `z.string().array()`).\n */\nexport function isArrayField(schema: StandardJSONSchemaV1 | undefined, field: string): boolean {\n return getFieldJsonSchema(schema, field)?.type === 'array';\n}\n\n/**\n * Checks if a field is an async stream (marked with `asyncStream()` metadata).\n * Returns the item schema if provided, or `true` if it's a plain string stream.\n */\nexport function isAsyncStreamField(schema: StandardJSONSchemaV1 | undefined, field: string): { itemSchema?: StandardSchemaV1 } | false {\n const prop = getFieldJsonSchema(schema, field);\n const asyncStreamId = prop?.asyncStream;\n if (asyncStreamId && asyncStreamRegistry.has(asyncStreamId)) {\n const meta = asyncStreamRegistry.get(asyncStreamId);\n return { itemSchema: meta?.itemSchema };\n }\n\n return false;\n}\n\n/**\n * Parse positional configuration to extract names and variadic info.\n */\nexport function parsePositionalConfig(positional: readonly string[]): { name: string; variadic: boolean }[] {\n return positional.map((p) => {\n const isVariadic = p.startsWith('...');\n const name = isVariadic ? p.slice(3) : p;\n return { name, variadic: isVariadic };\n });\n}\n\n/**\n * Result type for extractSchemaMetadata function.\n */\ninterface SchemaMetadataResult {\n /** Single-char flags: maps flag char → full arg name (e.g. `{ v: 'verbose' }`) */\n flags: Record<string, string>;\n /** Multi-char aliases: maps alias → full arg name (e.g. `{ 'dry-run': 'dryRun' }`) */\n aliases: Record<string, string>;\n}\n\nfunction addEntries(target: Record<string, string>, key: string, items: string | readonly string[], filter?: (item: string) => boolean) {\n const list = typeof items === 'string' ? [items] : items;\n for (const item of list) {\n if (typeof item === 'string' && item && item !== key && !(item in target) && (!filter || filter(item))) {\n target[item] = key;\n }\n }\n}\n\n/**\n * Extract all arg metadata from schema and meta in a single pass.\n * Returns flags (single-char, stackable) and aliases (multi-char, long names) separately.\n * When `autoAlias` is true (default), camelCase property names automatically get kebab-case aliases.\n */\nexport function extractSchemaMetadata(\n schema: StandardJSONSchemaV1,\n meta?: Record<string, PadroneFieldMeta | undefined>,\n autoAlias?: boolean,\n): SchemaMetadataResult {\n const flags: Record<string, string> = {};\n const aliases: Record<string, string> = {};\n\n // Extract from meta object\n if (meta) {\n for (const [key, value] of Object.entries(meta)) {\n if (!value) continue;\n\n if (value.flags) {\n addEntries(flags, key, value.flags, (item) => item.length === 1);\n }\n if (value.alias) {\n addEntries(aliases, key, value.alias, (item) => item.length > 1);\n }\n }\n }\n\n // Extract from JSON schema properties\n try {\n const jsonSchema = schema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;\n if (jsonSchema.type === 'object' && jsonSchema.properties) {\n for (const [propertyName, propertySchema] of Object.entries(jsonSchema.properties as Record<string, any>)) {\n if (!propertySchema) continue;\n\n // Extract flags from schema `.meta({ flags: ... })`\n const propFlags = propertySchema.flags;\n if (propFlags) {\n addEntries(flags, propertyName, propFlags, (item) => item.length === 1);\n }\n\n // Extract aliases from schema `.meta({ alias: ... })`\n const propAlias = propertySchema.alias;\n if (propAlias) {\n const list = typeof propAlias === 'string' ? [propAlias] : propAlias;\n if (Array.isArray(list)) {\n addEntries(aliases, propertyName, list, (item) => item.length > 1);\n }\n }\n\n // Auto-generate kebab-case alias for camelCase property names\n if (autoAlias !== false) {\n const kebab = camelToKebab(propertyName);\n if (kebab && !(kebab in aliases)) {\n aliases[kebab] = propertyName;\n }\n }\n }\n }\n } catch {\n // Ignore errors from JSON schema generation\n }\n\n return { flags, aliases };\n}\n\nfunction preprocessMappings(data: Record<string, unknown>, mappings: Record<string, string>): Record<string, unknown> {\n const result = { ...data };\n\n for (const [mappedKey, fullArgName] of Object.entries(mappings)) {\n if (mappedKey in data && mappedKey !== fullArgName) {\n const mappedValue = data[mappedKey];\n // Prefer full arg name if it exists\n if (!(fullArgName in result)) result[fullArgName] = mappedValue;\n delete result[mappedKey];\n }\n }\n\n return result;\n}\n\ninterface ParseArgsContext {\n flags?: Record<string, string>;\n aliases?: Record<string, string>;\n stdinData?: Record<string, unknown>;\n envData?: Record<string, unknown>;\n configData?: Record<string, unknown>;\n}\n\n/**\n * Apply values directly to arguments.\n * CLI values take precedence over the provided values.\n */\nfunction applyValues(data: Record<string, unknown>, values: Record<string, unknown>): Record<string, unknown> {\n const result = { ...data };\n\n for (const [key, value] of Object.entries(values)) {\n // Only apply value if arg wasn't already set\n if (key in result && result[key] !== undefined) continue;\n if (value !== undefined) {\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Combined preprocessing of arguments with all features.\n * Precedence order (highest to lowest): CLI args > stdin > env vars > config file\n */\nexport function preprocessArgs(data: Record<string, unknown>, ctx: ParseArgsContext): Record<string, unknown> {\n let result = { ...data };\n\n // 1. Apply flags and aliases first\n if (ctx.flags && Object.keys(ctx.flags).length > 0) {\n result = preprocessMappings(result, ctx.flags);\n }\n if (ctx.aliases && Object.keys(ctx.aliases).length > 0) {\n result = preprocessMappings(result, ctx.aliases);\n }\n\n // 2. Apply stdin data (higher precedence than env)\n // Only applies if CLI didn't set the arg\n if (ctx.stdinData) {\n result = applyValues(result, ctx.stdinData);\n }\n\n // 3. Apply environment variables (higher precedence than config)\n // These only apply if CLI/stdin didn't set the arg\n if (ctx.envData) {\n result = applyValues(result, ctx.envData);\n }\n\n // 4. Apply config file values (lowest precedence)\n // These only apply if neither CLI, stdin, nor env set the arg\n if (ctx.configData) {\n result = applyValues(result, ctx.configData);\n }\n\n return result;\n}\n\n/**\n * Auto-coerce CLI string values to match the expected schema types.\n * Handles: string → number, string → boolean for primitive schema fields.\n * Arrays of primitives are also coerced element-wise.\n */\nexport function coerceArgs(data: Record<string, unknown>, schema: StandardJSONSchemaV1): Record<string, unknown> {\n let properties: Record<string, any>;\n try {\n const jsonSchema = schema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;\n if (jsonSchema.type !== 'object' || !jsonSchema.properties) return data;\n properties = jsonSchema.properties;\n } catch {\n return data;\n }\n\n const result = { ...data };\n\n for (const [key, value] of Object.entries(result)) {\n const prop = properties[key];\n if (!prop) continue;\n\n const targetType = prop.type as string | undefined;\n\n if (targetType === 'number' || targetType === 'integer') {\n if (typeof value === 'string') {\n const num = Number(value);\n if (!Number.isNaN(num)) result[key] = num;\n }\n } else if (targetType === 'boolean') {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower === 'true' || lower === '1' || lower === 'yes' || lower === 'on') result[key] = true;\n else if (lower === 'false' || lower === '0' || lower === 'no' || lower === 'off') result[key] = false;\n }\n } else if (targetType === 'array') {\n // Coerce single items to array\n const arr = Array.isArray(value) ? value : [value];\n const itemType = prop.items?.type as string | undefined;\n if (itemType === 'number' || itemType === 'integer') {\n result[key] = arr.map((v) => {\n if (typeof v === 'string') {\n const num = Number(v);\n return Number.isNaN(num) ? v : num;\n }\n return v;\n });\n } else if (itemType === 'boolean') {\n result[key] = arr.map((v) => {\n if (typeof v === 'string') {\n const lower = v.toLowerCase();\n if (lower === 'true' || lower === '1' || lower === 'yes' || lower === 'on') return true;\n if (lower === 'false' || lower === '0' || lower === 'no' || lower === 'off') return false;\n }\n return v;\n });\n } else if (!Array.isArray(value)) {\n result[key] = arr;\n }\n }\n }\n\n return result;\n}\n\n/** Keys consumed by the CLI framework that are not user-defined args. */\nconst frameworkReservedKeys = new Set(['config', 'c']);\n\n/**\n * Detect unknown keys in the args that don't match any schema property.\n * Returns an array of { key, suggestion? } for each unknown key.\n * Framework-reserved keys (--config, -c) are always allowed.\n */\nexport function detectUnknownArgs(\n data: Record<string, unknown>,\n schema: StandardJSONSchemaV1,\n flags: Record<string, string>,\n aliases: Record<string, string>,\n suggestFn: (input: string, candidates: string[]) => string,\n): { key: string; suggestion: string }[] {\n let properties: Record<string, any>;\n let isLoose = false;\n try {\n const jsonSchema = schema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;\n if (jsonSchema.type !== 'object' || !jsonSchema.properties) return [];\n properties = jsonSchema.properties;\n // If additionalProperties is set (true, {}, or a schema), the schema allows extra keys\n if (jsonSchema.additionalProperties !== undefined && jsonSchema.additionalProperties !== false) isLoose = true;\n } catch {\n return [];\n }\n\n if (isLoose) return [];\n\n const knownKeys = new Set<string>([\n ...Object.keys(properties),\n ...Object.keys(flags),\n ...Object.values(flags),\n ...Object.keys(aliases),\n ...Object.values(aliases),\n ]);\n const propertyNames = Object.keys(properties);\n const unknowns: { key: string; suggestion: string }[] = [];\n\n for (const key of Object.keys(data)) {\n if (!knownKeys.has(key) && !frameworkReservedKeys.has(key)) {\n const suggestion = suggestFn(key, propertyNames);\n unknowns.push({ key, suggestion });\n }\n }\n\n return unknowns;\n}\n"],"mappings":";;;;;;AA+JA,SAAgB,aAAa,KAA4B;AACvD,KAAI,CAAC,QAAQ,KAAK,IAAI,CAAE,QAAO;AAC/B,QAAO,IAAI,QAAQ,WAAW,OAAO,IAAI,GAAG,aAAa,GAAG;;;;;AAM9D,SAAgB,iBAAiB,OAA4B;AAC3D,QAAO;;;AAIT,MAAa,mBAAmB;CAAE,QAAQ;CAAiB,gBAAgB,EAAE,iBAAiB,OAAO;CAAE;AAEvG,SAAS,mBAAmB,QAA0C,OAAgD;AACpH,KAAI,CAAC,OAAQ,QAAO,KAAA;AACpB,KAAI;EACF,MAAM,aAAa,OAAO,aAAa,WAAW,MAAM,iBAAiB;AACzE,MAAI,WAAW,SAAS,YAAY,WAAW,WAAY,QAAO,WAAW,WAAW;SAClF;;;;;AAOV,SAAgB,aAAa,QAA0C,OAAwB;AAC7F,QAAO,mBAAmB,QAAQ,MAAM,EAAE,SAAS;;;;;;AAOrD,SAAgB,mBAAmB,QAA0C,OAA0D;CAErI,MAAM,gBADO,mBAAmB,QAAQ,MAAM,EAClB;AAC5B,KAAI,iBAAiB,oBAAoB,IAAI,cAAc,CAEzD,QAAO,EAAE,YADI,oBAAoB,IAAI,cAAc,EACxB,YAAY;AAGzC,QAAO;;;;;AAMT,SAAgB,sBAAsB,YAAsE;AAC1G,QAAO,WAAW,KAAK,MAAM;EAC3B,MAAM,aAAa,EAAE,WAAW,MAAM;AAEtC,SAAO;GAAE,MADI,aAAa,EAAE,MAAM,EAAE,GAAG;GACxB,UAAU;GAAY;GACrC;;AAaJ,SAAS,WAAW,QAAgC,KAAa,OAAmC,QAAoC;CACtI,MAAM,OAAO,OAAO,UAAU,WAAW,CAAC,MAAM,GAAG;AACnD,MAAK,MAAM,QAAQ,KACjB,KAAI,OAAO,SAAS,YAAY,QAAQ,SAAS,OAAO,EAAE,QAAQ,YAAY,CAAC,UAAU,OAAO,KAAK,EACnG,QAAO,QAAQ;;;;;;;AAUrB,SAAgB,sBACd,QACA,MACA,WACsB;CACtB,MAAM,QAAgC,EAAE;CACxC,MAAM,UAAkC,EAAE;AAG1C,KAAI,KACF,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;AAC/C,MAAI,CAAC,MAAO;AAEZ,MAAI,MAAM,MACR,YAAW,OAAO,KAAK,MAAM,QAAQ,SAAS,KAAK,WAAW,EAAE;AAElE,MAAI,MAAM,MACR,YAAW,SAAS,KAAK,MAAM,QAAQ,SAAS,KAAK,SAAS,EAAE;;AAMtE,KAAI;EACF,MAAM,aAAa,OAAO,aAAa,WAAW,MAAM,iBAAiB;AACzE,MAAI,WAAW,SAAS,YAAY,WAAW,WAC7C,MAAK,MAAM,CAAC,cAAc,mBAAmB,OAAO,QAAQ,WAAW,WAAkC,EAAE;AACzG,OAAI,CAAC,eAAgB;GAGrB,MAAM,YAAY,eAAe;AACjC,OAAI,UACF,YAAW,OAAO,cAAc,YAAY,SAAS,KAAK,WAAW,EAAE;GAIzE,MAAM,YAAY,eAAe;AACjC,OAAI,WAAW;IACb,MAAM,OAAO,OAAO,cAAc,WAAW,CAAC,UAAU,GAAG;AAC3D,QAAI,MAAM,QAAQ,KAAK,CACrB,YAAW,SAAS,cAAc,OAAO,SAAS,KAAK,SAAS,EAAE;;AAKtE,OAAI,cAAc,OAAO;IACvB,MAAM,QAAQ,aAAa,aAAa;AACxC,QAAI,SAAS,EAAE,SAAS,SACtB,SAAQ,SAAS;;;SAKnB;AAIR,QAAO;EAAE;EAAO;EAAS;;AAG3B,SAAS,mBAAmB,MAA+B,UAA2D;CACpH,MAAM,SAAS,EAAE,GAAG,MAAM;AAE1B,MAAK,MAAM,CAAC,WAAW,gBAAgB,OAAO,QAAQ,SAAS,CAC7D,KAAI,aAAa,QAAQ,cAAc,aAAa;EAClD,MAAM,cAAc,KAAK;AAEzB,MAAI,EAAE,eAAe,QAAS,QAAO,eAAe;AACpD,SAAO,OAAO;;AAIlB,QAAO;;;;;;AAeT,SAAS,YAAY,MAA+B,QAA0D;CAC5G,MAAM,SAAS,EAAE,GAAG,MAAM;AAE1B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;AAEjD,MAAI,OAAO,UAAU,OAAO,SAAS,KAAA,EAAW;AAChD,MAAI,UAAU,KAAA,EACZ,QAAO,OAAO;;AAIlB,QAAO;;;;;;AAOT,SAAgB,eAAe,MAA+B,KAAgD;CAC5G,IAAI,SAAS,EAAE,GAAG,MAAM;AAGxB,KAAI,IAAI,SAAS,OAAO,KAAK,IAAI,MAAM,CAAC,SAAS,EAC/C,UAAS,mBAAmB,QAAQ,IAAI,MAAM;AAEhD,KAAI,IAAI,WAAW,OAAO,KAAK,IAAI,QAAQ,CAAC,SAAS,EACnD,UAAS,mBAAmB,QAAQ,IAAI,QAAQ;AAKlD,KAAI,IAAI,UACN,UAAS,YAAY,QAAQ,IAAI,UAAU;AAK7C,KAAI,IAAI,QACN,UAAS,YAAY,QAAQ,IAAI,QAAQ;AAK3C,KAAI,IAAI,WACN,UAAS,YAAY,QAAQ,IAAI,WAAW;AAG9C,QAAO;;;;;;;AAQT,SAAgB,WAAW,MAA+B,QAAuD;CAC/G,IAAI;AACJ,KAAI;EACF,MAAM,aAAa,OAAO,aAAa,WAAW,MAAM,iBAAiB;AACzE,MAAI,WAAW,SAAS,YAAY,CAAC,WAAW,WAAY,QAAO;AACnE,eAAa,WAAW;SAClB;AACN,SAAO;;CAGT,MAAM,SAAS,EAAE,GAAG,MAAM;AAE1B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;EACjD,MAAM,OAAO,WAAW;AACxB,MAAI,CAAC,KAAM;EAEX,MAAM,aAAa,KAAK;AAExB,MAAI,eAAe,YAAY,eAAe;OACxC,OAAO,UAAU,UAAU;IAC7B,MAAM,MAAM,OAAO,MAAM;AACzB,QAAI,CAAC,OAAO,MAAM,IAAI,CAAE,QAAO,OAAO;;aAE/B,eAAe;OACpB,OAAO,UAAU,UAAU;IAC7B,MAAM,QAAQ,MAAM,aAAa;AACjC,QAAI,UAAU,UAAU,UAAU,OAAO,UAAU,SAAS,UAAU,KAAM,QAAO,OAAO;aACjF,UAAU,WAAW,UAAU,OAAO,UAAU,QAAQ,UAAU,MAAO,QAAO,OAAO;;aAEzF,eAAe,SAAS;GAEjC,MAAM,MAAM,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM;GAClD,MAAM,WAAW,KAAK,OAAO;AAC7B,OAAI,aAAa,YAAY,aAAa,UACxC,QAAO,OAAO,IAAI,KAAK,MAAM;AAC3B,QAAI,OAAO,MAAM,UAAU;KACzB,MAAM,MAAM,OAAO,EAAE;AACrB,YAAO,OAAO,MAAM,IAAI,GAAG,IAAI;;AAEjC,WAAO;KACP;YACO,aAAa,UACtB,QAAO,OAAO,IAAI,KAAK,MAAM;AAC3B,QAAI,OAAO,MAAM,UAAU;KACzB,MAAM,QAAQ,EAAE,aAAa;AAC7B,SAAI,UAAU,UAAU,UAAU,OAAO,UAAU,SAAS,UAAU,KAAM,QAAO;AACnF,SAAI,UAAU,WAAW,UAAU,OAAO,UAAU,QAAQ,UAAU,MAAO,QAAO;;AAEtF,WAAO;KACP;YACO,CAAC,MAAM,QAAQ,MAAM,CAC9B,QAAO,OAAO;;;AAKpB,QAAO;;;AAIT,MAAM,wBAAwB,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC;;;;;;AAOtD,SAAgB,kBACd,MACA,QACA,OACA,SACA,WACuC;CACvC,IAAI;CACJ,IAAI,UAAU;AACd,KAAI;EACF,MAAM,aAAa,OAAO,aAAa,WAAW,MAAM,iBAAiB;AACzE,MAAI,WAAW,SAAS,YAAY,CAAC,WAAW,WAAY,QAAO,EAAE;AACrE,eAAa,WAAW;AAExB,MAAI,WAAW,yBAAyB,KAAA,KAAa,WAAW,yBAAyB,MAAO,WAAU;SACpG;AACN,SAAO,EAAE;;AAGX,KAAI,QAAS,QAAO,EAAE;CAEtB,MAAM,YAAY,IAAI,IAAY;EAChC,GAAG,OAAO,KAAK,WAAW;EAC1B,GAAG,OAAO,KAAK,MAAM;EACrB,GAAG,OAAO,OAAO,MAAM;EACvB,GAAG,OAAO,KAAK,QAAQ;EACvB,GAAG,OAAO,OAAO,QAAQ;EAC1B,CAAC;CACF,MAAM,gBAAgB,OAAO,KAAK,WAAW;CAC7C,MAAM,WAAkD,EAAE;AAE1D,MAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CACjC,KAAI,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,sBAAsB,IAAI,IAAI,EAAE;EAC1D,MAAM,aAAa,UAAU,KAAK,cAAc;AAChD,WAAS,KAAK;GAAE;GAAK;GAAY,CAAC;;AAItC,QAAO"}
@@ -0,0 +1,15 @@
1
+ import { createRequire } from "node:module";
2
+ //#region \0rolldown/runtime.js
3
+ var __defProp = Object.defineProperty;
4
+ var __exportAll = (all, no_symbols) => {
5
+ let target = {};
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
11
+ return target;
12
+ };
13
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
14
+ //#endregion
15
+ export { __require as n, __exportAll as t };
@@ -149,11 +149,15 @@ type TemplateFunction = (text: string) => (data: Record<string, unknown>) => str
149
149
  declare function createCodeBuilder(): CodeBuilder;
150
150
  //#endregion
151
151
  //#region src/codegen/discovery.d.ts
152
- type DiscoverySource = 'help' | 'fish' | 'zsh';
152
+ type DiscoverySource = 'help' | 'completion' | 'bash' | 'fish' | 'zsh';
153
153
  interface DiscoveryOptions {
154
154
  /** The command to discover (e.g. 'gh', 'docker', 'kubectl'). */
155
155
  command: string;
156
- /** Which parsing sources to use. Default: ['help'] */
156
+ /**
157
+ * Which parsing sources to use. Default: ['help'].
158
+ * Use `'completion'` to auto-detect the best shell completion source
159
+ * by probing `<cmd> completion <shell>` (bash → fish → zsh).
160
+ */
157
161
  sources?: DiscoverySource[];
158
162
  /** Max subcommand depth. 0 = root only, undefined = unlimited. */
159
163
  depth?: number;
@@ -177,6 +181,12 @@ interface DiscoveryResult {
177
181
  * parsing shell completion scripts.
178
182
  */
179
183
  declare function discoverCli(options: DiscoveryOptions): Promise<DiscoveryResult>;
184
+ /**
185
+ * Detect the best shell for completion parsing by probing the command.
186
+ * Tries `<cmd> completion <shell>` for bash, fish, zsh (in that order).
187
+ * Returns the shell name if successful, or null if no completion command exists.
188
+ */
189
+ declare function detectCompletionShell(command: string, timeout?: number): Promise<'bash' | 'fish' | 'zsh' | null>;
180
190
  //#endregion
181
191
  //#region src/codegen/file-emitter.d.ts
182
192
  /**
@@ -224,6 +234,21 @@ interface CommandTreeOptions {
224
234
  */
225
235
  declare function generateCommandTree(root: CommandMeta, ctx: GeneratorContext, options?: CommandTreeOptions): void;
226
236
  //#endregion
237
+ //#region src/codegen/parsers/bash.d.ts
238
+ /**
239
+ * Parse bash completion scripts into CommandMeta.
240
+ *
241
+ * Bash completions typically use `complete -F <func> <command>` and define
242
+ * a function that sets COMPREPLY. Common patterns:
243
+ *
244
+ * local commands="init build deploy"
245
+ * local args="--verbose --output --format"
246
+ * case "$prev" in --format) COMPREPLY=($(compgen -W "json yaml toml" ...)) ;;
247
+ * COMPREPLY=($(compgen -W "$commands" ...))
248
+ * COMPREPLY=($(compgen -W "$args" ...))
249
+ */
250
+ declare function parseBashCompletions(text: string): CommandMeta;
251
+ //#endregion
227
252
  //#region src/codegen/parsers/fish.d.ts
228
253
  /**
229
254
  * Parse fish shell completion scripts into CommandMeta.
@@ -301,5 +326,5 @@ type TemplateRenderer = (data: Record<string, unknown>, partials?: Record<string
301
326
  */
302
327
  declare function template(text: string): TemplateRenderer;
303
328
  //#endregion
304
- export { type CodeBuildResult, type CodeBuilder, type CommandFileOptions, type CommandMeta, type CommandTreeOptions, type DiscoveryOptions, type DiscoveryResult, type DiscoverySource, type EmitResult, type FieldMeta, type FileEmitter, type FileEmitterOptions, type GeneratorContext, type GeneratorLogger, type TemplateFunction, createCodeBuilder, createFileEmitter, discoverCli, fieldMetaToCode, generateBarrelFile, generateCommandFile, generateCommandTree, mergeCommandMeta, parseFishCompletions, parseHelpOutput, parseZshCompletions, schemaToCode, template };
329
+ export { type CodeBuildResult, type CodeBuilder, type CommandFileOptions, type CommandMeta, type CommandTreeOptions, type DiscoveryOptions, type DiscoveryResult, type DiscoverySource, type EmitResult, type FieldMeta, type FileEmitter, type FileEmitterOptions, type GeneratorContext, type GeneratorLogger, type TemplateFunction, createCodeBuilder, createFileEmitter, detectCompletionShell, discoverCli, fieldMetaToCode, generateBarrelFile, generateCommandFile, generateCommandTree, mergeCommandMeta, parseBashCompletions, parseFishCompletions, parseHelpOutput, parseZshCompletions, schemaToCode, template };
305
330
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/codegen/types.ts","../../src/codegen/code-builder.ts","../../src/codegen/discovery.ts","../../src/codegen/file-emitter.ts","../../src/codegen/generators/barrel-file.ts","../../src/codegen/generators/command-file.ts","../../src/codegen/generators/command-tree.ts","../../src/codegen/parsers/fish.ts","../../src/codegen/parsers/help.ts","../../src/codegen/parsers/merge.ts","../../src/codegen/parsers/zsh.ts","../../src/codegen/schema-to-code.ts","../../src/codegen/template.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/codegen/types.ts","../../src/codegen/code-builder.ts","../../src/codegen/discovery.ts","../../src/codegen/file-emitter.ts","../../src/codegen/generators/barrel-file.ts","../../src/codegen/generators/command-file.ts","../../src/codegen/generators/command-tree.ts","../../src/codegen/parsers/bash.ts","../../src/codegen/parsers/fish.ts","../../src/codegen/parsers/help.ts","../../src/codegen/parsers/merge.ts","../../src/codegen/parsers/zsh.ts","../../src/codegen/schema-to-code.ts","../../src/codegen/template.ts"],"mappings":"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"}
@@ -1,5 +1,8 @@
1
+ import { t as JSON_SCHEMA_OPTS } from "../args-D5PNDyNu.mjs";
1
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
3
  import { dirname, join, resolve } from "node:path";
4
+ import { execFile } from "node:child_process";
5
+ import { readFile } from "node:fs/promises";
3
6
  //#region src/codegen/code-builder.ts
4
7
  var CodeBuilderImpl = class CodeBuilderImpl {
5
8
  imports = /* @__PURE__ */ new Map();
@@ -182,6 +185,115 @@ function createCodeBuilder() {
182
185
  return new CodeBuilderImpl();
183
186
  }
184
187
  //#endregion
188
+ //#region src/codegen/parsers/bash.ts
189
+ /**
190
+ * Parse bash completion scripts into CommandMeta.
191
+ *
192
+ * Bash completions typically use `complete -F <func> <command>` and define
193
+ * a function that sets COMPREPLY. Common patterns:
194
+ *
195
+ * local commands="init build deploy"
196
+ * local args="--verbose --output --format"
197
+ * case "$prev" in --format) COMPREPLY=($(compgen -W "json yaml toml" ...)) ;;
198
+ * COMPREPLY=($(compgen -W "$commands" ...))
199
+ * COMPREPLY=($(compgen -W "$args" ...))
200
+ */
201
+ function parseBashCompletions(text) {
202
+ const result = {
203
+ name: "",
204
+ arguments: [],
205
+ subcommands: []
206
+ };
207
+ const completeMatch = text.match(/complete\s+(?:[^-]|-[^F])*-F\s+\S+\s+(\S+)/);
208
+ if (completeMatch) result.name = completeMatch[1];
209
+ if (!result.name) {
210
+ const markerMatch = text.match(/###-begin-(\S+)-completion-###/);
211
+ if (markerMatch) result.name = markerMatch[1];
212
+ }
213
+ const joined = text.replace(/\\\n\s*/g, " ");
214
+ const variables = extractVariables(joined);
215
+ const commandWords = variables.get("commands") ?? variables.get("cmds") ?? variables.get("subcommands");
216
+ if (commandWords) for (const name of splitWords(commandWords)) result.subcommands.push({ name });
217
+ const argWords = variables.get("args") ?? variables.get("opts") ?? variables.get("options") ?? variables.get("flags");
218
+ const optionNames = /* @__PURE__ */ new Set();
219
+ if (argWords) {
220
+ for (const word of splitWords(argWords)) if (word.startsWith("-")) optionNames.add(word);
221
+ }
222
+ const compgenRegex = /compgen\s+-W\s+["']([^"']+)["']/g;
223
+ let compgenMatch;
224
+ while ((compgenMatch = compgenRegex.exec(joined)) !== null) {
225
+ if (compgenMatch[1].startsWith("$")) continue;
226
+ for (const word of splitWords(compgenMatch[1])) if (word.startsWith("-")) optionNames.add(word);
227
+ }
228
+ const enumMap = parseCaseStatement(joined);
229
+ const seenArgs = /* @__PURE__ */ new Set();
230
+ for (const rawName of optionNames) {
231
+ if (rawName === "--help" || rawName === "-h" || rawName === "--version" || rawName === "-V") continue;
232
+ const name = rawName.replace(/^-+/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
233
+ if (!name || seenArgs.has(name)) continue;
234
+ seenArgs.add(name);
235
+ const enumValues = enumMap.get(rawName);
236
+ const isShort = rawName.startsWith("-") && !rawName.startsWith("--") && rawName.length === 2;
237
+ const field = {
238
+ name,
239
+ type: enumValues ? "enum" : "string",
240
+ enumValues,
241
+ ambiguous: !enumValues
242
+ };
243
+ if (isShort) field.aliases = [rawName];
244
+ result.arguments.push(field);
245
+ }
246
+ if (result.arguments.length === 0) delete result.arguments;
247
+ if (result.subcommands.length === 0) delete result.subcommands;
248
+ return result;
249
+ }
250
+ /**
251
+ * Extract variable assignments: `local VAR="value"`, `VAR="value"`, `local VAR='value'`
252
+ */
253
+ function extractVariables(text) {
254
+ const vars = /* @__PURE__ */ new Map();
255
+ const regex = /(?:local\s+)?(\w+)=["']([^"']+)["']/g;
256
+ let match;
257
+ while ((match = regex.exec(text)) !== null) vars.set(match[1], match[2]);
258
+ return vars;
259
+ }
260
+ /**
261
+ * Parse case statements to find option → enum value mappings.
262
+ *
263
+ * Matches patterns like:
264
+ * case "$prev" in
265
+ * --format) COMPREPLY=($(compgen -W "json yaml toml" ...)) ;;
266
+ * --env|--environment) COMPREPLY=($(compgen -W "dev staging prod" ...)) ;;
267
+ */
268
+ function parseCaseStatement(text) {
269
+ const result = /* @__PURE__ */ new Map();
270
+ const caseRegex = /case\s+[^i]*\bin\b\s*([\s\S]*?)esac/g;
271
+ let caseMatch;
272
+ while ((caseMatch = caseRegex.exec(text)) !== null) {
273
+ const branches = caseMatch[1].split(";;");
274
+ for (const branch of branches) {
275
+ const patternMatch = branch.match(/^\s*([-\w|]+)\)/);
276
+ if (!patternMatch) continue;
277
+ const compgenMatch = branch.match(/compgen\s+-W\s+["']([^"']+)["']/);
278
+ if (!compgenMatch) continue;
279
+ const values = compgenMatch[1];
280
+ const words = splitWords(values).filter((w) => !w.startsWith("$"));
281
+ if (words.length === 0) continue;
282
+ for (const pattern of patternMatch[1].split("|")) {
283
+ const trimmed = pattern.trim();
284
+ if (trimmed.startsWith("-")) result.set(trimmed, words);
285
+ }
286
+ }
287
+ }
288
+ return result;
289
+ }
290
+ /**
291
+ * Split a space-separated word list, filtering empty strings.
292
+ */
293
+ function splitWords(text) {
294
+ return text.split(/\s+/).filter(Boolean);
295
+ }
296
+ //#endregion
185
297
  //#region src/codegen/parsers/fish.ts
186
298
  /**
187
299
  * Parse fish shell completion scripts into CommandMeta.
@@ -792,7 +904,8 @@ function findSubcommands(text) {
792
904
  * parsing shell completion scripts.
793
905
  */
794
906
  async function discoverCli(options) {
795
- const { command, sources = ["help"], depth, delay = 50, log, timeout = 1e4 } = options;
907
+ const { command, sources: rawSources = ["help"], depth, delay = 50, log, timeout = 1e4 } = options;
908
+ const sources = await resolveSources(rawSources, command, timeout, log);
796
909
  const warnings = [];
797
910
  let invocations = 0;
798
911
  const results = [];
@@ -812,6 +925,12 @@ async function discoverCli(options) {
812
925
  });
813
926
  results.push(helpResult);
814
927
  }
928
+ if (sources.includes("bash")) {
929
+ log?.info(`Parsing bash completions for ${command}...`);
930
+ const bashText = await getCompletionScript(command, "bash", timeout);
931
+ if (bashText) results.push(parseBashCompletions(bashText));
932
+ else warnings.push("Could not obtain bash completion script");
933
+ }
815
934
  if (sources.includes("fish")) {
816
935
  log?.info(`Parsing fish completions for ${command}...`);
817
936
  const fishText = await getCompletionScript(command, "fish", timeout);
@@ -875,20 +994,17 @@ async function runHelp(command, args, timeout) {
875
994
  */
876
995
  async function runCommand(command, args, timeout) {
877
996
  try {
878
- const proc = Bun.spawn([command, ...args], {
879
- stdout: "pipe",
880
- stderr: "pipe",
881
- stdin: "ignore"
997
+ const { stdout, stderr } = await new Promise((resolve) => {
998
+ execFile(command, args, {
999
+ timeout,
1000
+ maxBuffer: 10 * 1024 * 1024
1001
+ }, (_error, stdout, stderr) => {
1002
+ resolve({
1003
+ stdout: (stdout ?? "").trim(),
1004
+ stderr: (stderr ?? "").trim()
1005
+ });
1006
+ });
882
1007
  });
883
- const timer = setTimeout(() => proc.kill(), timeout);
884
- const [_exitCode, stdoutBuf, stderrBuf] = await Promise.all([
885
- proc.exited,
886
- new Response(proc.stdout).arrayBuffer(),
887
- new Response(proc.stderr).arrayBuffer()
888
- ]);
889
- clearTimeout(timer);
890
- const stdout = new TextDecoder().decode(stdoutBuf).trim();
891
- const stderr = new TextDecoder().decode(stderrBuf).trim();
892
1008
  const combined = stdout || stderr;
893
1009
  if (!combined) return null;
894
1010
  if (combined.length < 10) return null;
@@ -906,13 +1022,47 @@ async function getCompletionScript(command, shell, timeout) {
906
1022
  if (result) return result;
907
1023
  result = await runCommand(command, ["completions", shell], timeout);
908
1024
  if (result) return result;
909
- const paths = shell === "fish" ? [`/usr/share/fish/vendor_completions.d/${command}.fish`, `/usr/local/share/fish/vendor_completions.d/${command}.fish`] : [`/usr/share/zsh/site-functions/_${command}`, `/usr/local/share/zsh/site-functions/_${command}`];
1025
+ const paths = shell === "fish" ? [`/usr/share/fish/vendor_completions.d/${command}.fish`, `/usr/local/share/fish/vendor_completions.d/${command}.fish`] : shell === "bash" ? [
1026
+ `/usr/share/bash-completion/completions/${command}`,
1027
+ `/usr/local/share/bash-completion/completions/${command}`,
1028
+ `/etc/bash_completion.d/${command}`
1029
+ ] : [`/usr/share/zsh/site-functions/_${command}`, `/usr/local/share/zsh/site-functions/_${command}`];
910
1030
  for (const path of paths) try {
911
- const file = Bun.file(path);
912
- if (await file.exists()) return await file.text();
1031
+ return await readFile(path, "utf-8");
913
1032
  } catch {}
914
1033
  return null;
915
1034
  }
1035
+ /**
1036
+ * Detect the best shell for completion parsing by probing the command.
1037
+ * Tries `<cmd> completion <shell>` for bash, fish, zsh (in that order).
1038
+ * Returns the shell name if successful, or null if no completion command exists.
1039
+ */
1040
+ async function detectCompletionShell(command, timeout = 5e3) {
1041
+ for (const shell of [
1042
+ "bash",
1043
+ "fish",
1044
+ "zsh"
1045
+ ]) if (await getCompletionScript(command, shell, timeout)) return shell;
1046
+ return null;
1047
+ }
1048
+ /**
1049
+ * Resolve 'completion' entries in the sources array by probing for the best available shell.
1050
+ * Other sources are passed through unchanged.
1051
+ */
1052
+ async function resolveSources(sources, command, timeout, log) {
1053
+ if (!sources.includes("completion")) return sources;
1054
+ log?.info(`Probing ${command} for completion command...`);
1055
+ const shell = await detectCompletionShell(command, timeout);
1056
+ return sources.flatMap((s) => {
1057
+ if (s !== "completion") return s;
1058
+ if (shell) {
1059
+ log?.info(` Found ${shell} completion support`);
1060
+ return shell;
1061
+ }
1062
+ log?.info(" No completion command found, skipping");
1063
+ return [];
1064
+ });
1065
+ }
916
1066
  function sleep(ms) {
917
1067
  return new Promise((resolve) => setTimeout(resolve, ms));
918
1068
  }
@@ -1011,7 +1161,7 @@ function jsonSchemaPropertyToZod(prop, required, ambiguous) {
1011
1161
  */
1012
1162
  function schemaToCode(schema) {
1013
1163
  try {
1014
- return jsonSchemaToCode(schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }));
1164
+ return jsonSchemaToCode(schema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS));
1015
1165
  } catch {
1016
1166
  return {
1017
1167
  code: "z.unknown()",
@@ -1353,6 +1503,6 @@ function render(text, data, partials) {
1353
1503
  return result;
1354
1504
  }
1355
1505
  //#endregion
1356
- export { createCodeBuilder, createFileEmitter, discoverCli, fieldMetaToCode, generateBarrelFile, generateCommandFile, generateCommandTree, mergeCommandMeta, parseFishCompletions, parseHelpOutput, parseZshCompletions, schemaToCode, template };
1506
+ export { createCodeBuilder, createFileEmitter, detectCompletionShell, discoverCli, fieldMetaToCode, generateBarrelFile, generateCommandFile, generateCommandTree, mergeCommandMeta, parseBashCompletions, parseFishCompletions, parseHelpOutput, parseZshCompletions, schemaToCode, template };
1357
1507
 
1358
1508
  //# sourceMappingURL=index.mjs.map