argsbarg 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/completion.ts CHANGED
@@ -8,7 +8,7 @@ It keeps completion aligned with the runtime schema so the generated commands,
8
8
  options, and descriptions stay in sync with the CLI definition.
9
9
  */
10
10
 
11
- import { CliCommand, CliOption } from "./types.ts";
11
+ import { CliCommand, CliOption, CliOptionKind } from "./types.ts";
12
12
 
13
13
  // ── Shared Types ───────────────────────────────────────────────────────────────
14
14
 
@@ -216,6 +216,31 @@ function emitSimulate(ident: string): string {
216
216
  return o;
217
217
  }
218
218
 
219
+ /** Emits bash helper to complete Enum option values when previous token is --name. */
220
+ function emitEnumReplyBash(ident: string, scopes: ScopeRec[]): string {
221
+ let o = "_${ident}_nac_enum_reply() {\n".replace("${ident}", ident);
222
+ o += " local sid=\"$1\" prev=\"$2\" cur=\"$3\"\n";
223
+ o += " case $sid in\n";
224
+ for (const [i, sc] of scopes.entries()) {
225
+ const enumOpts = sc.opts.filter((op) => op.kind === CliOptionKind.Enum && (op.choices?.length ?? 0) > 0);
226
+ if (enumOpts.length === 0) {
227
+ continue;
228
+ }
229
+ o += " " + i + ")\n";
230
+ o += " case $prev in\n";
231
+ for (const op of enumOpts) {
232
+ const words = (op.choices ?? []).map((c) => escShellSingleQuoted(c)).join(" ");
233
+ o += " --" + op.name + ") COMPREPLY=( $(compgen -W '" + words + "' -- \"$cur\") ); return 0 ;;\n";
234
+ }
235
+ o += " esac\n";
236
+ o += " ;;\n";
237
+ }
238
+ o += " esac\n";
239
+ o += " return 1\n";
240
+ o += "}\n";
241
+ return o;
242
+ }
243
+
219
244
  /** Emits the main `COMPREPLY` driver and `complete -F` registration for bash. */
220
245
  function emitMainBodyBash(schema: CliCommand, ident: string): string {
221
246
  const main = mainName(schema.key);
@@ -224,6 +249,7 @@ function emitMainBodyBash(schema: CliCommand, ident: string): string {
224
249
  o += " local prev=\"${COMP_WORDS[COMP_CWORD-1]:-}\"\n";
225
250
  o += " _${ident}_nac_simulate\n".replace("${ident}", ident);
226
251
  o += " local sid=$REPLY_SID\n";
252
+ o += " if _${ident}_nac_enum_reply \"$sid\" \"$prev\" \"$cur\"; then return; fi\n".replace("${ident}", ident);
227
253
  o += " if [[ $cur == -* ]]; then\n";
228
254
  o += " local oname=\"A_${ident}_${sid}_opts\"\n".replace("${ident}", ident);
229
255
  o += " local -a optsarr\n";
@@ -289,6 +315,7 @@ export function completionBashScript(schema: CliCommand): string {
289
315
  out += emitConsumeShort(ident, scopes);
290
316
  out += emitMatchChild(ident, scopes, pathIndex);
291
317
  out += emitSimulate(ident);
318
+ out += emitEnumReplyBash(ident, scopes);
292
319
  out += emitMainBodyBash(schema, ident);
293
320
 
294
321
  return out;
@@ -470,6 +497,31 @@ function emitSimulateZsh(ident: string): string {
470
497
  return o;
471
498
  }
472
499
 
500
+ /** Emits zsh helper to complete Enum option values when previous token is --name. */
501
+ function emitEnumReplyZsh(ident: string, scopes: ScopeRec[]): string {
502
+ let o = "_${ident}_nac_enum_reply() {\n".replace("${ident}", ident);
503
+ o += " local sid=$1 prev=$2\n";
504
+ o += " case $sid in\n";
505
+ for (const [i, sc] of scopes.entries()) {
506
+ const enumOpts = sc.opts.filter((op) => op.kind === CliOptionKind.Enum && (op.choices?.length ?? 0) > 0);
507
+ if (enumOpts.length === 0) {
508
+ continue;
509
+ }
510
+ o += " " + i + ")\n";
511
+ o += " case $prev in\n";
512
+ for (const op of enumOpts) {
513
+ const vals = (op.choices ?? []).map((c) => escShellSingleQuoted(c)).join(" ");
514
+ o += " --" + op.name + ") _values " + vals + "; return 0 ;;\n";
515
+ }
516
+ o += " esac\n";
517
+ o += " ;;\n";
518
+ }
519
+ o += " esac\n";
520
+ o += " return 1\n";
521
+ o += "}\n";
522
+ return o;
523
+ }
524
+
473
525
  /** Zsh: `_main` completer and `compdef` registration. */
474
526
  function emitMainBodyZsh(schema: CliCommand, ident: string): string {
475
527
  const main = mainName(schema.key);
@@ -477,6 +529,7 @@ function emitMainBodyZsh(schema: CliCommand, ident: string): string {
477
529
  o += " local curcontext=\"$curcontext\" ret=1\n";
478
530
  o += " _${ident}_nac_simulate\n".replace("${ident}", ident);
479
531
  o += " local sid=$REPLY_SID\n";
532
+ o += " if _${ident}_nac_enum_reply \"$sid\" \"$words[CURRENT-1]\"; then return 0; fi\n".replace("${ident}", ident);
480
533
  o += " if [[ $PREFIX == -* ]]; then\n";
481
534
  o += " local -a optsarr\n";
482
535
  o += " local oname=\"A_${ident}_${sid}_opts\"\n".replace("${ident}", ident);
@@ -517,12 +570,13 @@ export function completionZshScript(schema: CliCommand): string {
517
570
  out += emitConsumeShortZsh(ident, scopes);
518
571
  out += emitMatchChildZsh(ident, scopes, pathIndex);
519
572
  out += emitSimulateZsh(ident);
573
+ out += emitEnumReplyZsh(ident, scopes);
520
574
  out += emitMainBodyZsh(schema, ident);
521
575
  return out;
522
576
  }
523
577
 
524
578
  /**
525
- * Returns a schema suitable for help display, including the reserved `completion` subtree.
579
+ * Returns a schema suitable for help display, including reserved built-in subtrees.
526
580
  * Routing roots get `completion` merged; leaf roots are wrapped as a tiny router.
527
581
  */
528
582
  export function cliPresentationRoot(root: CliCommand): CliCommand {
@@ -534,15 +588,33 @@ export function cliPresentationRoot(root: CliCommand): CliCommand {
534
588
  key: root.key,
535
589
  description: root.description,
536
590
  options: root.options,
537
- commands: [cliBuiltinCompletionGroup(root.key)],
591
+ commands: presentationBuiltins(root),
538
592
  } as CliCommand;
539
593
  }
540
594
  return {
541
595
  ...root,
542
- commands: [...(root.commands ?? []), cliBuiltinCompletionGroup(root.key)],
596
+ commands: [...(root.commands ?? []), ...presentationBuiltins(root)],
543
597
  } as CliCommand;
544
598
  }
545
599
 
600
+ /** Built-in commands shown in help and merged for routing CLIs. */
601
+ function presentationBuiltins(root: CliCommand): CliCommand[] {
602
+ const cmds: CliCommand[] = [cliBuiltinCompletionGroup(root.key)];
603
+ if (root.mcpServer !== undefined) {
604
+ cmds.push(cliBuiltinMcpCommand());
605
+ }
606
+ return cmds;
607
+ }
608
+
609
+ /** Builds the static `mcp` leaf command (merged when `root.mcpServer` is set). */
610
+ export function cliBuiltinMcpCommand(): CliCommand {
611
+ return {
612
+ key: "mcp",
613
+ description: "Run as an MCP server over stdio (for AI agents).",
614
+ handler: () => {},
615
+ };
616
+ }
617
+
546
618
  /**
547
619
  * Builds the static `completion` / `bash` / `zsh` command subtree (merged into the program root at runtime).
548
620
  */
package/src/context.ts CHANGED
@@ -7,7 +7,7 @@ It keeps handlers small with a typed read API for flags, strings, numbers, and c
7
7
  parsed values.
8
8
  */
9
9
 
10
- import type { CliCommand } from "./types.ts";
10
+ import type { CliCommand, CliInvocation } from "./types.ts";
11
11
  import { strictParseDouble } from "./utils.ts";
12
12
 
13
13
  /**
@@ -19,6 +19,7 @@ export class CliContext {
19
19
  readonly args: string[];
20
20
  readonly schema: CliCommand;
21
21
  readonly opts: Record<string, string>;
22
+ readonly invocation: CliInvocation;
22
23
 
23
24
  /** Captures the merged program root, routed path, positional words, and option map for a leaf handler. */
24
25
  constructor(
@@ -27,12 +28,14 @@ export class CliContext {
27
28
  args: string[],
28
29
  opts: Record<string, string>,
29
30
  schema: CliCommand,
31
+ invocation: CliInvocation = "cli",
30
32
  ) {
31
33
  this.appName = appName;
32
34
  this.commandPath = commandPath;
33
35
  this.args = args;
34
36
  this.opts = opts;
35
37
  this.schema = schema;
38
+ this.invocation = invocation;
36
39
  }
37
40
 
38
41
  /** Returns whether a presence flag was set (including implicit "1" for boolean options). */
package/src/help.ts CHANGED
@@ -151,7 +151,7 @@ function wrapText(text: string, width: number): string[] {
151
151
  // ── Option Label Formatting ───────────────────────────────────────────────────
152
152
 
153
153
  /** Suffix for `--name` in usage (e.g. ` <string>`) based on value kind. */
154
- function optKindLabel(k: CliOptionKind): string {
154
+ function optKindLabel(k: CliOptionKind, o?: CliOption): string {
155
155
  switch (k) {
156
156
  case CliOptionKind.Presence:
157
157
  return "";
@@ -159,12 +159,22 @@ function optKindLabel(k: CliOptionKind): string {
159
159
  return " <number>";
160
160
  case CliOptionKind.String:
161
161
  return " <string>";
162
+ case CliOptionKind.Enum: {
163
+ const choices = o?.choices ?? [];
164
+ if (choices.length === 0) {
165
+ return " <choice>";
166
+ }
167
+ if (choices.length <= 4) {
168
+ return " <" + choices.join("|") + ">";
169
+ }
170
+ return " <" + choices.slice(0, 3).join("|") + "|…>";
171
+ }
162
172
  }
163
173
  }
164
174
 
165
175
  /** Formats a flag/value option for help tables: `--name`, optional short, optional kind hint. */
166
176
  export function cliOptionLabel(o: CliOption, color: boolean): string {
167
- let r = "--" + o.name + optKindLabel(o.kind);
177
+ let r = "--" + o.name + optKindLabel(o.kind, o);
168
178
  if (o.shortName) r += ", -" + o.shortName;
169
179
  if (!color) return r;
170
180