politty 0.4.3 → 0.4.6

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 (60) hide show
  1. package/dist/{arg-registry-BUUhZ7JR.d.ts → arg-registry-2m40k1Et.d.ts} +1 -1
  2. package/dist/{arg-registry-BUUhZ7JR.d.ts.map → arg-registry-2m40k1Et.d.ts.map} +1 -1
  3. package/dist/augment.d.ts +1 -1
  4. package/dist/completion/index.cjs +16 -168
  5. package/dist/completion/index.d.cts +2 -77
  6. package/dist/completion/index.d.ts +2 -77
  7. package/dist/completion/index.js +3 -154
  8. package/dist/{zsh-CASZWn0o.cjs → completion-Df0eZ70u.cjs} +326 -37
  9. package/dist/completion-Df0eZ70u.cjs.map +1 -0
  10. package/dist/{zsh-hjvdI8uZ.js → completion-_AnQsWh9.js} +298 -27
  11. package/dist/completion-_AnQsWh9.js.map +1 -0
  12. package/dist/docs/index.cjs +276 -29
  13. package/dist/docs/index.cjs.map +1 -1
  14. package/dist/docs/index.d.cts +62 -7
  15. package/dist/docs/index.d.cts.map +1 -1
  16. package/dist/docs/index.d.ts +62 -7
  17. package/dist/docs/index.d.ts.map +1 -1
  18. package/dist/docs/index.js +271 -30
  19. package/dist/docs/index.js.map +1 -1
  20. package/dist/{value-completion-resolver-BQgHsX7b.d.cts → index-BZalbMeu.d.ts} +83 -4
  21. package/dist/index-BZalbMeu.d.ts.map +1 -0
  22. package/dist/{value-completion-resolver-C9LTGr0O.d.ts → index-C5-0RXiH.d.cts} +83 -4
  23. package/dist/index-C5-0RXiH.d.cts.map +1 -0
  24. package/dist/index.cjs +8 -7
  25. package/dist/index.d.cts +67 -13
  26. package/dist/index.d.cts.map +1 -1
  27. package/dist/index.d.ts +68 -14
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +4 -5
  30. package/dist/{lazy-BEDnSR0m.cjs → lazy-DHlvJiQQ.cjs} +44 -8
  31. package/dist/lazy-DHlvJiQQ.cjs.map +1 -0
  32. package/dist/{lazy-BrEg8SgI.js → lazy-DSyfzR-F.js} +38 -8
  33. package/dist/lazy-DSyfzR-F.js.map +1 -0
  34. package/dist/{runner-C4fSHJMe.cjs → runner-C1Aah5c5.cjs} +365 -23
  35. package/dist/runner-C1Aah5c5.cjs.map +1 -0
  36. package/dist/{runner-D6k4BgB4.js → runner-DjG0uBxQ.js} +365 -23
  37. package/dist/runner-DjG0uBxQ.js.map +1 -0
  38. package/dist/{schema-extractor-n9288WJ6.d.ts → schema-extractor-C9APqeSL.d.ts} +30 -3
  39. package/dist/schema-extractor-C9APqeSL.d.ts.map +1 -0
  40. package/dist/{schema-extractor-DFaAZzaY.d.cts → schema-extractor-CVf0J4An.d.cts} +29 -2
  41. package/dist/schema-extractor-CVf0J4An.d.cts.map +1 -0
  42. package/dist/{subcommand-router-CAzBsLSI.js → subcommand-router-CKuy6D2b.js} +2 -2
  43. package/dist/{subcommand-router-CAzBsLSI.js.map → subcommand-router-CKuy6D2b.js.map} +1 -1
  44. package/dist/{subcommand-router-ZjNjFaUL.cjs → subcommand-router-sZHhUP7b.cjs} +2 -2
  45. package/dist/{subcommand-router-ZjNjFaUL.cjs.map → subcommand-router-sZHhUP7b.cjs.map} +1 -1
  46. package/package.json +9 -9
  47. package/dist/completion/index.cjs.map +0 -1
  48. package/dist/completion/index.d.cts.map +0 -1
  49. package/dist/completion/index.d.ts.map +0 -1
  50. package/dist/completion/index.js.map +0 -1
  51. package/dist/lazy-BEDnSR0m.cjs.map +0 -1
  52. package/dist/lazy-BrEg8SgI.js.map +0 -1
  53. package/dist/runner-C4fSHJMe.cjs.map +0 -1
  54. package/dist/runner-D6k4BgB4.js.map +0 -1
  55. package/dist/schema-extractor-DFaAZzaY.d.cts.map +0 -1
  56. package/dist/schema-extractor-n9288WJ6.d.ts.map +0 -1
  57. package/dist/value-completion-resolver-BQgHsX7b.d.cts.map +0 -1
  58. package/dist/value-completion-resolver-C9LTGr0O.d.ts.map +0 -1
  59. package/dist/zsh-CASZWn0o.cjs.map +0 -1
  60. package/dist/zsh-hjvdI8uZ.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { c as arg, i as extractFields, r as resolveSubCommandMeta } from "./lazy-BrEg8SgI.js";
1
+ import { i as extractFields, l as arg, r as resolveSubCommandMeta } from "./lazy-DSyfzR-F.js";
2
2
  import { z } from "zod";
3
3
  import { execSync } from "node:child_process";
4
4
 
@@ -16,6 +16,30 @@ function defineCommand(config) {
16
16
  examples: config.examples
17
17
  };
18
18
  }
19
+ /**
20
+ * Create a typed defineCommand factory with pre-bound global args type.
21
+ * This is the recommended pattern for type-safe global options.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * // global-args.ts
26
+ * type GlobalArgsType = { verbose: boolean; config?: string };
27
+ * export const defineAppCommand = createDefineCommand<GlobalArgsType>();
28
+ *
29
+ * // commands/build.ts
30
+ * export const buildCommand = defineAppCommand({
31
+ * name: "build",
32
+ * args: z.object({ output: arg(z.string().default("dist")) }),
33
+ * run: (args) => {
34
+ * args.verbose; // typed via GlobalArgsType
35
+ * args.output; // typed via local args
36
+ * },
37
+ * });
38
+ * ```
39
+ */
40
+ function createDefineCommand() {
41
+ return defineCommand;
42
+ }
19
43
 
20
44
  //#endregion
21
45
  //#region src/completion/value-completion-resolver.ts
@@ -68,6 +92,13 @@ function resolveValueCompletion(field) {
68
92
  /**
69
93
  * Sanitize a name for use as a shell function/variable identifier.
70
94
  * Replaces any character that is not alphanumeric or underscore with underscore.
95
+ *
96
+ * Note: This is not injective -- distinct names may produce the same output
97
+ * (e.g., "foo-bar" and "foo_bar" both become "foo_bar"). When used for nested
98
+ * path encoding (`path.map(sanitize).join("_")`), cross-level collisions are
99
+ * theoretically possible (e.g., "foo-bar:baz" vs "foo:bar-baz") but extremely
100
+ * unlikely in real CLI designs. If collision-safety is needed, sanitize must be
101
+ * replaced with an injective encoding.
71
102
  */
72
103
  function sanitize(name) {
73
104
  return name.replace(/[^a-zA-Z0-9_]/g, "_");
@@ -147,29 +178,80 @@ function extractSubcommand(name, command) {
147
178
  positionals: extractCompletablePositionals(command)
148
179
  };
149
180
  }
181
+ /** Join parent and child with a separator, omitting separator when parent is empty. */
182
+ function joinPrefix(parent, child, sep) {
183
+ return parent ? `${parent}${sep}${child}` : child;
184
+ }
150
185
  /**
151
186
  * Collect opt-takes-value case entries for a subcommand tree.
152
- * Used by bash and zsh generators (identical case syntax: `subcmd:--opt) return 0 ;;`).
187
+ * Used by bash and zsh generators (identical case syntax: `path:--opt) return 0 ;;`).
188
+ * parentPath is a colon-delimited path (e.g., "" for root, "workspace:user" for nested).
153
189
  */
154
- function optTakesValueEntries(sub, subcmdName) {
190
+ function optTakesValueEntries(sub, parentPath) {
155
191
  const lines = [];
156
192
  for (const opt of sub.options) if (opt.takesValue) {
157
- const patterns = [`${subcmdName}:--${opt.cliName}`];
158
- if (opt.alias) patterns.push(`${subcmdName}:-${opt.alias}`);
193
+ const patterns = [`${parentPath}:--${opt.cliName}`];
194
+ if (opt.alias) patterns.push(`${parentPath}:-${opt.alias}`);
159
195
  lines.push(` ${patterns.join("|")}) return 0 ;;`);
160
196
  }
161
- for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueEntries(child, child.name));
197
+ for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueEntries(child, joinPrefix(parentPath, child.name, ":")));
162
198
  return lines;
163
199
  }
164
200
  /**
201
+ * Recursively collect all subcommand route entries.
202
+ * Returns entries used by all shell generators for both dispatch routing
203
+ * and subcommand lookup (is_subcmd) tables.
204
+ */
205
+ function collectRouteEntries(sub, parentPath = "", parentFunc = "") {
206
+ const entries = [];
207
+ for (const child of getVisibleSubs(sub.subcommands)) {
208
+ const pathStr = joinPrefix(parentPath, child.name, ":");
209
+ const funcSuffix = joinPrefix(parentFunc, sanitize(child.name), "_");
210
+ entries.push(...collectRouteEntries(child, pathStr, funcSuffix));
211
+ entries.push({
212
+ pathStr,
213
+ funcSuffix,
214
+ lookupPattern: `${parentPath}:${child.name}`
215
+ });
216
+ }
217
+ return entries;
218
+ }
219
+ /**
220
+ * Generate is_subcmd case/switch body lines (bash/zsh case syntax).
221
+ * Returns lines for the case statement body only (caller wraps in function).
222
+ */
223
+ function isSubcmdCaseLines(routeEntries) {
224
+ return routeEntries.map((r) => ` ${r.lookupPattern}) return 0 ;;`);
225
+ }
226
+ /**
227
+ * Recursively merge global options into a subcommand and all its descendants.
228
+ * Avoids duplicates by checking existing option names.
229
+ */
230
+ function propagateGlobalOptions(sub, globalOptions) {
231
+ const existingNames = new Set(sub.options.map((o) => o.name));
232
+ const newOpts = globalOptions.filter((o) => !existingNames.has(o.name));
233
+ sub.options = [...sub.options, ...newOpts];
234
+ for (const child of sub.subcommands) propagateGlobalOptions(child, globalOptions);
235
+ }
236
+ /**
165
237
  * Extract completion data from a command tree
238
+ *
239
+ * @param command - The root command
240
+ * @param programName - Program name for completion scripts
241
+ * @param globalArgsSchema - Optional global args schema. When provided, global options
242
+ * are derived from this schema instead of the root command's options.
166
243
  */
167
- function extractCompletionData(command, programName) {
244
+ function extractCompletionData(command, programName, globalArgsSchema) {
168
245
  const rootSubcommand = extractSubcommand(programName, command);
246
+ let globalOptions;
247
+ if (globalArgsSchema) {
248
+ globalOptions = extractFields(globalArgsSchema).fields.filter((field) => !field.positional).map(fieldToOption);
249
+ propagateGlobalOptions(rootSubcommand, globalOptions);
250
+ } else globalOptions = rootSubcommand.options;
169
251
  return {
170
252
  command: rootSubcommand,
171
253
  programName,
172
- globalOptions: rootSubcommand.options
254
+ globalOptions
173
255
  };
174
256
  }
175
257
 
@@ -317,8 +399,9 @@ function generateSubHandler$2(sub, fn, path) {
317
399
  for (const child of visibleSubs) lines.push(...generateSubHandler$2(child, fn, fullPath));
318
400
  lines.push(`${funcName}() {`);
319
401
  lines.push(...valueCompletionBlocks(sub.options));
320
- lines.push(` if [[ -z "$_inline_prefix" ]] && __${fn}_opt_takes_value "${sub.name}" "$_prev"; then return; fi`);
321
- lines.push(` if [[ -n "$_inline_prefix" ]] && __${fn}_opt_takes_value "${sub.name}" "\${_inline_prefix%=}"; then return; fi`);
402
+ const fullPathStr = fullPath.join(":");
403
+ lines.push(` if [[ -z "$_inline_prefix" ]] && __${fn}_opt_takes_value "${fullPathStr}" "$_prev"; then return; fi`);
404
+ lines.push(` if [[ -n "$_inline_prefix" ]] && __${fn}_opt_takes_value "${fullPathStr}" "\${_inline_prefix%=}"; then return; fi`);
322
405
  if (sub.positionals.length > 0) {
323
406
  lines.push(` if (( _after_dd )); then`);
324
407
  lines.push(...positionalBlock$2(sub.positionals).map((l) => ` ${l}`));
@@ -343,7 +426,7 @@ function generateSubHandler$2(sub, fn, path) {
343
426
  }
344
427
  function generateBashCompletion(command, options) {
345
428
  const { programName } = options;
346
- const data = extractCompletionData(command, programName);
429
+ const data = extractCompletionData(command, programName, options.globalArgsSchema);
347
430
  const fn = sanitize(programName);
348
431
  const root = data.command;
349
432
  const visibleSubs = getVisibleSubs(root.subcommands);
@@ -367,6 +450,16 @@ function generateBashCompletion(command, options) {
367
450
  lines.push(` return 1`);
368
451
  lines.push(`}`);
369
452
  lines.push(``);
453
+ const routeEntries = collectRouteEntries(root);
454
+ if (routeEntries.length > 0) {
455
+ lines.push(`__${fn}_is_subcmd() {`);
456
+ lines.push(` case "$1:$2" in`);
457
+ lines.push(...isSubcmdCaseLines(routeEntries));
458
+ lines.push(` esac`);
459
+ lines.push(` return 1`);
460
+ lines.push(`}`);
461
+ lines.push(``);
462
+ }
370
463
  for (const sub of visibleSubs) lines.push(...generateSubHandler$2(sub, fn, []));
371
464
  lines.push(`__${fn}_complete_root() {`);
372
465
  lines.push(...valueCompletionBlocks(root.options));
@@ -395,7 +488,7 @@ function generateBashCompletion(command, options) {
395
488
  lines.push(` fi`);
396
489
  lines.push(`}`);
397
490
  lines.push(``);
398
- const subRouting = visibleSubs.map((s) => ` ${s.name}) __${fn}_complete_${sanitize(s.name)} ;;`).join("\n");
491
+ const subRouting = routeEntries.map((r) => ` ${r.pathStr}) __${fn}_complete_${r.funcSuffix} ;;`).join("\n");
399
492
  lines.push(`_${fn}_completions() {`);
400
493
  lines.push(` COMPREPLY=()`);
401
494
  lines.push(``);
@@ -439,7 +532,7 @@ function generateBashCompletion(command, options) {
439
532
  lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w" && _skip_next=1`);
440
533
  lines.push(` (( _j++ )); continue`);
441
534
  lines.push(` fi`);
442
- if (visibleSubs.length > 0) lines.push(` if [[ -z "$_subcmd" ]]; then _subcmd="$_w"; _used_opts=(); else (( _pos_count++ )); fi`);
535
+ if (routeEntries.length > 0) lines.push(` if __${fn}_is_subcmd "$_subcmd" "$_w"; then _subcmd="\${_subcmd:+\${_subcmd}:}$_w"; _used_opts=(); _pos_count=0; else (( _pos_count++ )); fi`);
443
536
  else lines.push(` (( _pos_count++ ))`);
444
537
  lines.push(` (( _j++ ))`);
445
538
  lines.push(` done`);
@@ -1112,7 +1205,8 @@ function generateSubHandler$1(sub, fn, path) {
1112
1205
  for (const child of visibleSubs) lines.push(...generateSubHandler$1(child, fn, fullPath));
1113
1206
  lines.push(`function ${funcName} --no-scope-shadowing`);
1114
1207
  lines.push(...valueCompletionBlock$1(sub.options));
1115
- lines.push(` if __${fn}_opt_takes_value "${sub.name}" "$_prev"; return; end`);
1208
+ const fullPathStr = fullPath.join(":");
1209
+ lines.push(` if __${fn}_opt_takes_value "${fullPathStr}" "$_prev"; return; end`);
1116
1210
  if (sub.positionals.length > 0) {
1117
1211
  lines.push(` if test $_after_dd -eq 1`);
1118
1212
  lines.push(...positionalBlock$1(sub.positionals).map((l) => ` ${l}`));
@@ -1133,20 +1227,23 @@ function generateSubHandler$1(sub, fn, path) {
1133
1227
  return lines;
1134
1228
  }
1135
1229
  /** Generate opt-takes-value entries for fish switch cases */
1136
- function optTakesValueCases(sub, subcmdName) {
1230
+ function optTakesValueCases(sub, parentPath) {
1137
1231
  const lines = [];
1138
1232
  for (const opt of sub.options) if (opt.takesValue) {
1139
- const patterns = [`"${subcmdName}:--${opt.cliName}"`];
1140
- if (opt.alias) patterns.push(`"${subcmdName}:-${opt.alias}"`);
1233
+ const patterns = [`"${parentPath}:--${opt.cliName}"`];
1234
+ if (opt.alias) patterns.push(`"${parentPath}:-${opt.alias}"`);
1141
1235
  lines.push(` case ${patterns.join(" ")}`);
1142
1236
  lines.push(` return 0`);
1143
1237
  }
1144
- for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueCases(child, child.name));
1238
+ for (const child of getVisibleSubs(sub.subcommands)) {
1239
+ const childPath = parentPath ? `${parentPath}:${child.name}` : child.name;
1240
+ lines.push(...optTakesValueCases(child, childPath));
1241
+ }
1145
1242
  return lines;
1146
1243
  }
1147
1244
  function generateFishCompletion(command, options) {
1148
1245
  const { programName } = options;
1149
- const data = extractCompletionData(command, programName);
1246
+ const data = extractCompletionData(command, programName, options.globalArgsSchema);
1150
1247
  const fn = sanitize(programName);
1151
1248
  const root = data.command;
1152
1249
  const visibleSubs = getVisibleSubs(root.subcommands);
@@ -1170,6 +1267,19 @@ function generateFishCompletion(command, options) {
1170
1267
  lines.push(` return 1`);
1171
1268
  lines.push(`end`);
1172
1269
  lines.push(``);
1270
+ const routeEntries = collectRouteEntries(root);
1271
+ if (routeEntries.length > 0) {
1272
+ lines.push(`function __${fn}_is_subcmd`);
1273
+ lines.push(` switch "$argv[1]:$argv[2]"`);
1274
+ for (const r of routeEntries) {
1275
+ lines.push(` case "${r.lookupPattern}"`);
1276
+ lines.push(` return 0`);
1277
+ }
1278
+ lines.push(` end`);
1279
+ lines.push(` return 1`);
1280
+ lines.push(`end`);
1281
+ lines.push(``);
1282
+ }
1173
1283
  for (const sub of visibleSubs) lines.push(...generateSubHandler$1(sub, fn, []));
1174
1284
  lines.push(`function __${fn}_complete_root --no-scope-shadowing`);
1175
1285
  lines.push(...valueCompletionBlock$1(root.options));
@@ -1232,13 +1342,13 @@ function generateFishCompletion(command, options) {
1232
1342
  lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w"; and set _skip_next 1`);
1233
1343
  lines.push(` set _j (math $_j + 1); continue`);
1234
1344
  lines.push(` end`);
1235
- if (visibleSubs.length > 0) lines.push(` if test -z "$_subcmd"; set _subcmd "$_w"; set _used_opts; else; set _pos_count (math $_pos_count + 1); end`);
1345
+ if (routeEntries.length > 0) lines.push(` if __${fn}_is_subcmd "$_subcmd" "$_w"; test -n "$_subcmd"; and set _subcmd "$_subcmd:$_w"; or set _subcmd "$_w"; set _used_opts; set _pos_count 0; else; set _pos_count (math $_pos_count + 1); end`);
1236
1346
  else lines.push(` set _pos_count (math $_pos_count + 1)`);
1237
1347
  lines.push(` set _j (math $_j + 1)`);
1238
1348
  lines.push(` end`);
1239
1349
  lines.push(``);
1240
1350
  lines.push(` switch "$_subcmd"`);
1241
- for (const s of visibleSubs) lines.push(` case "${s.name}"; __${fn}_complete_${sanitize(s.name)}`);
1351
+ for (const r of routeEntries) lines.push(` case "${r.pathStr}"; __${fn}_complete_${r.funcSuffix}`);
1242
1352
  lines.push(` case '*'; __${fn}_complete_root`);
1243
1353
  lines.push(` end`);
1244
1354
  lines.push(`end`);
@@ -1357,7 +1467,8 @@ function generateSubHandler(sub, fn, path) {
1357
1467
  lines.push(`${funcName}() {`);
1358
1468
  lines.push(` local -a _vals=()`);
1359
1469
  lines.push(...valueCompletionBlock(sub.options, fn));
1360
- lines.push(` if __${fn}_opt_takes_value "${sub.name}" "\${words[CURRENT-1]}"; then return 0; fi`);
1470
+ const fullPathStr = fullPath.join(":");
1471
+ lines.push(` if __${fn}_opt_takes_value "${fullPathStr}" "\${words[CURRENT-1]}"; then return 0; fi`);
1361
1472
  if (sub.positionals.length > 0) {
1362
1473
  lines.push(` if (( _after_dd )); then`);
1363
1474
  lines.push(...positionalBlock(sub.positionals, fn).map((l) => ` ${l}`));
@@ -1384,7 +1495,7 @@ function generateSubHandler(sub, fn, path) {
1384
1495
  }
1385
1496
  function generateZshCompletion(command, options) {
1386
1497
  const { programName } = options;
1387
- const data = extractCompletionData(command, programName);
1498
+ const data = extractCompletionData(command, programName, options.globalArgsSchema);
1388
1499
  const fn = sanitize(programName);
1389
1500
  const root = data.command;
1390
1501
  const visibleSubs = getVisibleSubs(root.subcommands);
@@ -1419,6 +1530,16 @@ function generateZshCompletion(command, options) {
1419
1530
  lines.push(` return 1`);
1420
1531
  lines.push(`}`);
1421
1532
  lines.push(``);
1533
+ const routeEntries = collectRouteEntries(root);
1534
+ if (routeEntries.length > 0) {
1535
+ lines.push(`__${fn}_is_subcmd() {`);
1536
+ lines.push(` case "$1:$2" in`);
1537
+ lines.push(...isSubcmdCaseLines(routeEntries));
1538
+ lines.push(` esac`);
1539
+ lines.push(` return 1`);
1540
+ lines.push(`}`);
1541
+ lines.push(``);
1542
+ }
1422
1543
  for (const sub of visibleSubs) lines.push(...generateSubHandler(sub, fn, []));
1423
1544
  lines.push(`__${fn}_complete_root() {`);
1424
1545
  lines.push(` local -a _vals=()`);
@@ -1449,7 +1570,7 @@ function generateZshCompletion(command, options) {
1449
1570
  lines.push(` fi`);
1450
1571
  lines.push(`}`);
1451
1572
  lines.push(``);
1452
- const subRouting = visibleSubs.map((s) => ` ${s.name}) __${fn}_complete_${sanitize(s.name)} ;;`).join("\n");
1573
+ const subRouting = routeEntries.map((r) => ` ${r.pathStr}) __${fn}_complete_${r.funcSuffix} ;;`).join("\n");
1453
1574
  lines.push(`_${fn}() {`);
1454
1575
  lines.push(` (( CURRENT )) || CURRENT=\${#words}`);
1455
1576
  lines.push(``);
@@ -1468,7 +1589,7 @@ function generateZshCompletion(command, options) {
1468
1589
  lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w" && _skip_next=1`);
1469
1590
  lines.push(` (( _j++ )); continue`);
1470
1591
  lines.push(` fi`);
1471
- if (visibleSubs.length > 0) lines.push(` if [[ -z "$_subcmd" ]]; then _subcmd="$_w"; _used_opts=(); else (( _pos_count++ )); fi`);
1592
+ if (routeEntries.length > 0) lines.push(` if __${fn}_is_subcmd "$_subcmd" "$_w"; then _subcmd="\${_subcmd:+\${_subcmd}:}$_w"; _used_opts=(); _pos_count=0; else (( _pos_count++ )); fi`);
1472
1593
  else lines.push(` (( _pos_count++ ))`);
1473
1594
  lines.push(` (( _j++ ))`);
1474
1595
  lines.push(` done`);
@@ -1504,5 +1625,155 @@ source ~/.zshrc`
1504
1625
  }
1505
1626
 
1506
1627
  //#endregion
1507
- export { formatForShell as a, generateCandidates as c, extractPositionals as d, resolveValueCompletion as f, hasCompleteCommand as i, generateBashCompletion as l, generateFishCompletion as n, parseCompletionContext as o, defineCommand as p, createDynamicCompleteCommand as r, CompletionDirective as s, generateZshCompletion as t, extractCompletionData as u };
1508
- //# sourceMappingURL=zsh-hjvdI8uZ.js.map
1628
+ //#region src/completion/index.ts
1629
+ /**
1630
+ * Shell completion generation module
1631
+ *
1632
+ * Provides utilities to generate shell completion scripts for bash, zsh, and fish.
1633
+ *
1634
+ * @example
1635
+ * ```typescript
1636
+ * import { generateCompletion, createCompletionCommand } from "politty/completion";
1637
+ *
1638
+ * // Generate completion script directly
1639
+ * const result = generateCompletion(myCommand, {
1640
+ * shell: "bash",
1641
+ * programName: "mycli"
1642
+ * });
1643
+ * console.log(result.script);
1644
+ *
1645
+ * // Or add a completion subcommand to your CLI
1646
+ * const mainCommand = withCompletionCommand(
1647
+ * defineCommand({
1648
+ * name: "mycli",
1649
+ * subCommands: { ... },
1650
+ * }),
1651
+ * );
1652
+ * ```
1653
+ */
1654
+ /**
1655
+ * Generate completion script for the specified shell
1656
+ */
1657
+ function generateCompletion(command, options) {
1658
+ switch (options.shell) {
1659
+ case "bash": return generateBashCompletion(command, options);
1660
+ case "zsh": return generateZshCompletion(command, options);
1661
+ case "fish": return generateFishCompletion(command, options);
1662
+ default: throw new Error(`Unsupported shell: ${options.shell}`);
1663
+ }
1664
+ }
1665
+ /**
1666
+ * Get the list of supported shells
1667
+ */
1668
+ function getSupportedShells() {
1669
+ return [
1670
+ "bash",
1671
+ "zsh",
1672
+ "fish"
1673
+ ];
1674
+ }
1675
+ /**
1676
+ * Detect the current shell from environment
1677
+ */
1678
+ function detectShell() {
1679
+ const shellName = (process.env.SHELL || "").split("/").pop()?.toLowerCase() || "";
1680
+ if (shellName.includes("bash")) return "bash";
1681
+ if (shellName.includes("zsh")) return "zsh";
1682
+ if (shellName.includes("fish")) return "fish";
1683
+ return null;
1684
+ }
1685
+ /**
1686
+ * Schema for the completion command arguments
1687
+ */
1688
+ const completionArgsSchema = z.object({
1689
+ shell: arg(z.enum([
1690
+ "bash",
1691
+ "zsh",
1692
+ "fish"
1693
+ ]).optional().describe("Shell type (auto-detected if not specified)"), {
1694
+ positional: true,
1695
+ description: "Shell type (bash, zsh, or fish)",
1696
+ placeholder: "SHELL"
1697
+ }),
1698
+ instructions: arg(z.boolean().default(false), {
1699
+ alias: "i",
1700
+ description: "Show installation instructions"
1701
+ })
1702
+ });
1703
+ /**
1704
+ * Create a completion subcommand for your CLI
1705
+ *
1706
+ * This creates a ready-to-use subcommand that generates completion scripts.
1707
+ *
1708
+ * @example
1709
+ * ```typescript
1710
+ * const mainCommand = defineCommand({
1711
+ * name: "mycli",
1712
+ * subCommands: {
1713
+ * completion: createCompletionCommand(mainCommand)
1714
+ * }
1715
+ * });
1716
+ * ```
1717
+ */
1718
+ function createCompletionCommand(rootCommand, programName, globalArgsSchema) {
1719
+ const resolvedProgramName = programName ?? rootCommand.name;
1720
+ if (!rootCommand.subCommands?.__complete) rootCommand.subCommands = {
1721
+ ...rootCommand.subCommands,
1722
+ __complete: createDynamicCompleteCommand(rootCommand, resolvedProgramName)
1723
+ };
1724
+ return defineCommand({
1725
+ name: "completion",
1726
+ description: "Generate shell completion script",
1727
+ args: completionArgsSchema,
1728
+ run(args) {
1729
+ const shellType = args.shell || detectShell();
1730
+ if (!shellType) {
1731
+ console.error("Could not detect shell type. Please specify one of: bash, zsh, fish");
1732
+ process.exitCode = 1;
1733
+ return;
1734
+ }
1735
+ const result = generateCompletion(rootCommand, {
1736
+ shell: shellType,
1737
+ programName: resolvedProgramName,
1738
+ includeDescriptions: true,
1739
+ ...globalArgsSchema !== void 0 && { globalArgsSchema }
1740
+ });
1741
+ if (args.instructions) console.log(result.installInstructions);
1742
+ else console.log(result.script);
1743
+ }
1744
+ });
1745
+ }
1746
+ /**
1747
+ * Wrap a command with a completion subcommand
1748
+ *
1749
+ * This avoids circular references that occur when a command references itself
1750
+ * in its subCommands (e.g., for completion generation).
1751
+ *
1752
+ * @param command - The command to wrap
1753
+ * @param options - Options including programName
1754
+ * @returns A new command with the completion subcommand added
1755
+ *
1756
+ * @example
1757
+ * ```typescript
1758
+ * const mainCommand = withCompletionCommand(
1759
+ * defineCommand({
1760
+ * name: "mycli",
1761
+ * subCommands: { ... },
1762
+ * }),
1763
+ * );
1764
+ * ```
1765
+ */
1766
+ function withCompletionCommand(command, options) {
1767
+ const { programName, globalArgsSchema } = typeof options === "string" ? { programName: options } : options ?? {};
1768
+ const wrappedCommand = { ...command };
1769
+ wrappedCommand.subCommands = {
1770
+ ...command.subCommands,
1771
+ completion: createCompletionCommand(wrappedCommand, programName, globalArgsSchema),
1772
+ __complete: createDynamicCompleteCommand(wrappedCommand, programName)
1773
+ };
1774
+ return wrappedCommand;
1775
+ }
1776
+
1777
+ //#endregion
1778
+ export { withCompletionCommand as a, formatForShell as c, generateCandidates as d, extractCompletionData as f, defineCommand as g, createDefineCommand as h, getSupportedShells as i, parseCompletionContext as l, resolveValueCompletion as m, detectShell as n, createDynamicCompleteCommand as o, extractPositionals as p, generateCompletion as r, hasCompleteCommand as s, createCompletionCommand as t, CompletionDirective as u };
1779
+ //# sourceMappingURL=completion-_AnQsWh9.js.map