argsbarg 4.1.1 → 5.0.2

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 +34 -1
  2. package/README.md +5 -5
  3. package/docs/README.md +3 -3
  4. package/docs/ai-skills.md +9 -9
  5. package/docs/bundled-docs.md +4 -4
  6. package/docs/cli-program.md +8 -8
  7. package/docs/config-schema.md +2 -2
  8. package/docs/configure.md +177 -0
  9. package/docs/developing.md +1 -1
  10. package/docs/distribution-homebrew.md +12 -9
  11. package/docs/mcp.md +9 -9
  12. package/examples/full-example/README.md +3 -3
  13. package/examples/full-example/justfile +6 -6
  14. package/examples/full-example/scripts/formula-shared.ts +3 -2
  15. package/examples/full-example/src/program.ts +3 -3
  16. package/examples/nested.ts +1 -1
  17. package/index.d.ts +23 -16
  18. package/package.json +1 -1
  19. package/src/builtins/builtins.test.ts +105 -61
  20. package/src/builtins/completion-group.ts +4 -6
  21. package/src/builtins/config.test.ts +8 -0
  22. package/src/builtins/configure-copy.ts +86 -0
  23. package/src/builtins/configure.ts +70 -0
  24. package/src/builtins/dispatch.ts +13 -33
  25. package/src/builtins/index.ts +1 -1
  26. package/src/builtins/mcp.ts +2 -2
  27. package/src/builtins/registry.ts +6 -6
  28. package/src/capabilities.ts +22 -13
  29. package/src/cli-tool/cli-smoke.test.ts +21 -3
  30. package/src/cli-tool/create.test.ts +36 -1
  31. package/src/cli-tool/create.ts +26 -4
  32. package/src/cli-tool/full-example-capabilities.test.ts +10 -2
  33. package/src/cli-tool/main.ts +0 -0
  34. package/src/cli-tool/program.ts +20 -5
  35. package/src/cli-tool/run-create.ts +8 -19
  36. package/src/config/bootstrap.ts +11 -9
  37. package/src/config/context.test.ts +8 -0
  38. package/src/config/file.test.ts +13 -1
  39. package/src/config/resolve.test.ts +18 -0
  40. package/src/config/resolve.ts +2 -2
  41. package/src/config/validate.test.ts +11 -0
  42. package/src/config.integration.test.ts +5 -0
  43. package/src/configure/configure.test.ts +170 -0
  44. package/src/configure/index.ts +298 -0
  45. package/src/configure/prompt.ts +47 -0
  46. package/src/docs/api-guide.test.ts +10 -0
  47. package/src/docs/builtin.ts +3 -5
  48. package/src/docs/docs.test.ts +32 -5
  49. package/src/docs/mcp-guide.ts +4 -4
  50. package/src/docs/mcp-resources.test.ts +10 -0
  51. package/src/formats.test.ts +9 -0
  52. package/src/headless.test.ts +10 -0
  53. package/src/hidden-mcpb.test.ts +22 -0
  54. package/src/index.ts +2 -2
  55. package/src/install/binary-placement.test.ts +14 -0
  56. package/src/install/gh-release-update.test.ts +9 -0
  57. package/src/install/install-validate.test.ts +15 -5
  58. package/src/install/mcp-codex.test.ts +7 -0
  59. package/src/install/mcp-openclaw.test.ts +6 -0
  60. package/src/install/mcp-opencode.test.ts +10 -0
  61. package/src/install/opts.ts +17 -0
  62. package/src/install/status.test.ts +9 -0
  63. package/src/install/target-effective.ts +8 -8
  64. package/src/install/target-scope.ts +11 -8
  65. package/src/install/targets/configure.ts +1 -1
  66. package/src/install/targets.test.ts +23 -4
  67. package/src/invoke.test.ts +15 -1
  68. package/src/mcp/claude.test.ts +10 -0
  69. package/src/mcp/env.test.ts +12 -0
  70. package/src/mcp/tools.ts +1 -1
  71. package/src/mcp/zip.test.ts +5 -0
  72. package/src/mcp.integration.test.ts +43 -4
  73. package/src/parse.test.ts +82 -13
  74. package/src/schema.ts +1 -9
  75. package/src/skill/hint.ts +2 -2
  76. package/src/types.ts +22 -14
  77. package/src/validate.ts +17 -17
  78. package/docs/install.md +0 -206
  79. package/src/builtins/install.ts +0 -106
  80. package/src/builtins/uninstall.ts +0 -80
  81. package/src/install/index.ts +0 -409
  82. package/src/install/install.test.ts +0 -317
package/src/parse.test.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  } from "./test-fixtures.ts";
32
32
  import { cliValidateProgram } from "./validate.ts";
33
33
 
34
+ /** Tests that bundled short presence flags. */
34
35
  test("bundled short presence flags", () => {
35
36
  const root = testProgram({
36
37
  key: "app",
@@ -64,6 +65,7 @@ test("bundled short presence flags", () => {
64
65
  expect(pr.opts.b).toBe("1");
65
66
  });
66
67
 
68
+ /** Tests that long option equals. */
67
69
  test("long option equals", () => {
68
70
  const root = testProgram({
69
71
  key: "app",
@@ -89,6 +91,7 @@ test("long option equals", () => {
89
91
  expect(pr.opts.name).toBe("pat");
90
92
  });
91
93
 
94
+ /** Tests that fallback missing or unknown root flags. */
92
95
  test("fallback missing or unknown root flags", () => {
93
96
  const root = testProgram({
94
97
  key: "app",
@@ -117,6 +120,7 @@ test("fallback missing or unknown root flags", () => {
117
120
  expect(pr.opts.name).toBe("bob");
118
121
  });
119
122
 
123
+ /** Tests that unknown command. */
120
124
  test("unknown command", () => {
121
125
  const root = testProgram({
122
126
  key: "app",
@@ -129,6 +133,7 @@ test("unknown command", () => {
129
133
  expect(pr.errorMsg).toContain("Unknown command");
130
134
  });
131
135
 
136
+ /** Tests that implicit help empty. */
132
137
  test("implicit help empty", () => {
133
138
  const root = testProgram({
134
139
  key: "app",
@@ -141,6 +146,7 @@ test("implicit help empty", () => {
141
146
  expect(pr.helpExplicit).toBe(false);
142
147
  });
143
148
 
149
+ /** Invalid number post validate. */
144
150
  test("invalid number post validate", () => {
145
151
  const root = testProgram({
146
152
  key: "app",
@@ -167,6 +173,7 @@ test("invalid number post validate", () => {
167
173
  expect(pr.errorMsg).toContain("Invalid number");
168
174
  });
169
175
 
176
+ /** Supports scientific notation in numbers. */
170
177
  test("supports scientific notation in numbers", () => {
171
178
  const root = testProgram({
172
179
  key: "app",
@@ -193,6 +200,7 @@ test("supports scientific notation in numbers", () => {
193
200
  expect(Number(pr.opts.n)).toBe(12300);
194
201
  });
195
202
 
203
+ /** Completion scripts contain app name. */
196
204
  test("completion scripts contain app name", () => {
197
205
  const root = testProgram({
198
206
  key: "myapp",
@@ -210,6 +218,7 @@ test("completion scripts contain app name", () => {
210
218
  expect(zsh).toContain("hello:Say hello.");
211
219
  });
212
220
 
221
+ /** Completion scripts do not emit invalid bash substitutions. */
213
222
  test("completion scripts do not emit invalid bash substitutions", () => {
214
223
  const root = testProgram({
215
224
  key: "app",
@@ -221,6 +230,7 @@ test("completion scripts do not emit invalid bash substitutions", () => {
221
230
  expect(bash).not.toContain("${${");
222
231
  });
223
232
 
233
+ /** Completion scripts escape shell-sensitive command text in zsh. */
224
234
  test("completion scripts escape shell-sensitive command text in zsh", () => {
225
235
  const root = testProgram({
226
236
  key: "app",
@@ -238,6 +248,7 @@ test("completion scripts escape shell-sensitive command text in zsh", () => {
238
248
  expect(zsh).toContain("quote'\\''cmd:Say '\\''hello'\\'' and keep going.");
239
249
  });
240
250
 
251
+ /** Completion scripts keep dotted app names in registration names. */
241
252
  test("completion scripts keep dotted app names in registration names", () => {
242
253
  const root = testProgram({
243
254
  key: "minimal.ts",
@@ -253,6 +264,7 @@ test("completion scripts keep dotted app names in registration names", () => {
253
264
  expect(zsh).toContain("compdef _minimal_ts minimal.ts");
254
265
  });
255
266
 
267
+ /** Tests that trailing options after bounded positionals. */
256
268
  test("trailing options after bounded positionals", () => {
257
269
  const root = testProgram({
258
270
  key: "app",
@@ -286,6 +298,7 @@ test("trailing options after bounded positionals", () => {
286
298
  expect(pr.opts.verbose).toBe("1");
287
299
  });
288
300
 
301
+ /** Tests that trailing options include parent-scoped flags. */
289
302
  test("trailing options include parent-scoped flags", () => {
290
303
  const root = testProgram({
291
304
  key: "app",
@@ -338,6 +351,7 @@ test("trailing options include parent-scoped flags", () => {
338
351
  expect(pr.opts.json).toBe("1");
339
352
  });
340
353
 
354
+ /** Varargs tail parses trailing options. */
341
355
  test("varargs tail parses trailing options", () => {
342
356
  const root = testProgram({
343
357
  key: "app",
@@ -373,6 +387,7 @@ test("varargs tail parses trailing options", () => {
373
387
  expect(pr.opts.json).toBe("1");
374
388
  });
375
389
 
390
+ /** Stops parsing options at --. */
376
391
  test("stops parsing options at --", () => {
377
392
  const root = testProgram({
378
393
  key: "app",
@@ -411,6 +426,7 @@ test("stops parsing options at --", () => {
411
426
  expect(pr.args).toEqual(["--name", "bob", "-x"]);
412
427
  });
413
428
 
429
+ /** Missing required option returns error. */
414
430
  test("missing required option returns error", () => {
415
431
  const root = testProgram({
416
432
  key: "app",
@@ -437,6 +453,7 @@ test("missing required option returns error", () => {
437
453
  expect(pr.errorMsg).toContain("Missing required option: --req");
438
454
  });
439
455
 
456
+ /** Provided required option parses ok. */
440
457
  test("provided required option parses ok", () => {
441
458
  const root = testProgram({
442
459
  key: "app",
@@ -463,6 +480,7 @@ test("provided required option parses ok", () => {
463
480
  expect(pr.opts.req).toBe("val");
464
481
  });
465
482
 
483
+ /** Tests that presence option cannot be required. */
466
484
  test("presence option cannot be required", () => {
467
485
  const root = testProgram({
468
486
  key: "app",
@@ -486,6 +504,7 @@ test("presence option cannot be required", () => {
486
504
  expect(() => cliValidateProgram(root)).toThrow(/Presence option cannot be required/);
487
505
  });
488
506
 
507
+ /** Leaf completion help prints correctly. */
489
508
  test("leaf completion help prints correctly", async () => {
490
509
  // Test the fix where `completion zsh -h` on a leaf root was incorrectly ignored.
491
510
  // We run this as a subprocess so we don't accidentally exit the test runner.
@@ -499,6 +518,7 @@ test("leaf completion help prints correctly", async () => {
499
518
  expect(stderr.toString()).toBe("");
500
519
  });
501
520
 
521
+ /** Docs schema exports JSON for nested CLIs. */
502
522
  test("docs schema exports JSON for nested CLIs", async () => {
503
523
  const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts docs schema`
504
524
  .nothrow()
@@ -517,6 +537,7 @@ test("docs schema exports JSON for nested CLIs", async () => {
517
537
  expect(lookup.positionals[0].name).toBe("path");
518
538
  });
519
539
 
540
+ /** Docs schema exports JSON for leaf roots. */
520
541
  test("docs schema exports JSON for leaf roots", async () => {
521
542
  const { stdout, exitCode } = await $`bun run examples/minimal.ts docs schema`.nothrow().quiet();
522
543
  expect(exitCode).toBe(0);
@@ -526,27 +547,28 @@ test("docs schema exports JSON for leaf roots", async () => {
526
547
  expect(schema.positionals[0].name).toBe("name");
527
548
  expect(schema.options[0].name).toBe("verbose");
528
549
  expect(schema.commands.map((c: { key: string }) => c.key)).toEqual([
529
- "completion",
530
550
  "version",
531
- "install",
532
- "uninstall",
551
+ "configure",
533
552
  "docs",
534
553
  ]);
535
554
  });
536
555
 
556
+ /** Tests that version builtin prints program version. */
537
557
  test("version builtin prints program version", async () => {
538
558
  const { stdout, exitCode } = await $`bun run examples/nested.ts version`.nothrow().quiet();
539
559
  expect(exitCode).toBe(0);
540
560
  expect(stdout.toString().trim()).toMatch(/^\d+\.\d+\.\d+/);
541
561
  });
542
562
 
543
- test("leaf root help lists completion built-in", async () => {
563
+ /** Leaf root help omits hidden completion built-in. */
564
+ test("leaf root help omits hidden completion built-in", async () => {
544
565
  const { stdout, exitCode } = await $`bun run examples/minimal.ts -h`.nothrow().quiet();
545
566
  expect(exitCode).toBe(0);
546
- expect(stdout.toString()).toContain("completion");
547
- expect(stdout.toString()).toContain("Generate the autocompletion script for shells.");
567
+ expect(stdout.toString()).not.toContain("completion");
568
+ expect(stdout.toString()).toContain("configure");
548
569
  });
549
570
 
571
+ /** Root --schema is no longer a flag. */
550
572
  test("root --schema is no longer a flag", () => {
551
573
  const root = testProgram({
552
574
  key: "app",
@@ -569,6 +591,7 @@ test("root --schema is no longer a flag", () => {
569
591
  expect(pr.kind).not.toBe(ParseKind.Ok);
570
592
  });
571
593
 
594
+ /** CliSchemaJson omits handlers and completion built-ins. */
572
595
  test("cliSchemaJson omits handlers and completion built-ins", () => {
573
596
  const root = testProgram({
574
597
  key: "app",
@@ -599,6 +622,7 @@ test("cliSchemaJson omits handlers and completion built-ins", () => {
599
622
  expect(schema).not.toHaveProperty("handler");
600
623
  });
601
624
 
625
+ /** CliSchemaExport resolves program key in install notes. */
602
626
  test("cliSchemaExport resolves program key in install notes", () => {
603
627
  const root = testProgram({
604
628
  key: "myapp",
@@ -618,6 +642,7 @@ test("cliSchemaExport resolves program key in install notes", () => {
618
642
  expect(json).toContain("brew install");
619
643
  });
620
644
 
645
+ /** CliSchemaExport resolves {argsbarg:program} in consumer notes. */
621
646
  test("cliSchemaExport resolves {argsbarg:program} in consumer notes", () => {
622
647
  const root = testProgram({
623
648
  key: "myapp",
@@ -637,6 +662,7 @@ test("cliSchemaExport resolves {argsbarg:program} in consumer notes", () => {
637
662
  expect(schema.commands[0].notes).toBe("Run `myapp run` to start.");
638
663
  });
639
664
 
665
+ /** Docs help lists schema, api, and skill subcommands. */
640
666
  test("docs help lists schema, api, and skill subcommands", () => {
641
667
  const root = testProgram({
642
668
  key: "app",
@@ -663,6 +689,7 @@ test("docs help lists schema, api, and skill subcommands", () => {
663
689
  expect(help).toContain("reference agent SKILL");
664
690
  });
665
691
 
692
+ /** Root help omits legacy --schema flag. */
666
693
  test("root help omits legacy --schema flag", () => {
667
694
  const root = testProgram({
668
695
  key: "app",
@@ -680,6 +707,7 @@ test("root help omits legacy --schema flag", () => {
680
707
  expect(help).not.toContain("--schema");
681
708
  });
682
709
 
710
+ /** Root help shows agent docs hint when docs enabled. */
683
711
  test("root help shows agent docs hint when docs enabled", () => {
684
712
  const root = testProgram({
685
713
  key: "myapp",
@@ -696,6 +724,7 @@ test("root help shows agent docs hint when docs enabled", () => {
696
724
  expect(help).not.toContain("install --skill");
697
725
  });
698
726
 
727
+ /** Root help omits agent hint when docs disabled. */
699
728
  test("root help omits agent hint when docs disabled", () => {
700
729
  const root = testProgram({
701
730
  key: "myapp",
@@ -708,6 +737,7 @@ test("root help omits agent hint when docs disabled", () => {
708
737
  expect(help).not.toContain("docs skill");
709
738
  });
710
739
 
740
+ /** Root help includes program notes and agent hint. */
711
741
  test("root help includes program notes and agent hint", () => {
712
742
  const root = testProgram({
713
743
  key: "myapp",
@@ -725,6 +755,7 @@ test("root help includes program notes and agent hint", () => {
725
755
  expect(help).toContain("myapp docs skill");
726
756
  });
727
757
 
758
+ /** Enum option inputSchema includes enum array. */
728
759
  test("Enum option inputSchema includes enum array", () => {
729
760
  const tools = collectMcpTools(enumMcpFixture);
730
761
  const run = tools.find((t) => t.name === "run")!;
@@ -732,6 +763,7 @@ test("Enum option inputSchema includes enum array", () => {
732
763
  expect(schema.properties.mode.enum).toEqual(["dev", "prod"]);
733
764
  });
734
765
 
766
+ /** CliValidateProgram rejects Enum with no choices. */
735
767
  test("cliValidateProgram rejects Enum with no choices", () => {
736
768
  const root = testProgram({
737
769
  key: "app",
@@ -742,6 +774,7 @@ test("cliValidateProgram rejects Enum with no choices", () => {
742
774
  expect(() => cliValidateProgram(root)).toThrow(/requires non-empty choices/);
743
775
  });
744
776
 
777
+ /** CliValidateProgram rejects Enum with duplicate choices. */
745
778
  test("cliValidateProgram rejects Enum with duplicate choices", () => {
746
779
  const root = testProgram({
747
780
  key: "app",
@@ -752,6 +785,7 @@ test("cliValidateProgram rejects Enum with duplicate choices", () => {
752
785
  expect(() => cliValidateProgram(root)).toThrow(/choices must be distinct/);
753
786
  });
754
787
 
788
+ /** McpTool.description override wins without env suffix. */
755
789
  test("mcpTool.description override wins without env suffix", () => {
756
790
  const root = testProgram({
757
791
  key: "app",
@@ -770,6 +804,7 @@ test("mcpTool.description override wins without env suffix", () => {
770
804
  expect(tools[0]?.description).toBe("custom");
771
805
  });
772
806
 
807
+ /** CliValidateProgram requires program.appConfig description. */
773
808
  test("cliValidateProgram requires program.appConfig description", () => {
774
809
  const root = testProgram({
775
810
  key: "app",
@@ -780,6 +815,7 @@ test("cliValidateProgram requires program.appConfig description", () => {
780
815
  expect(() => cliValidateProgram(root)).toThrow(/description must be a non-empty string/);
781
816
  });
782
817
 
818
+ /** CliValidateProgram rejects duplicate mcpResources URIs. */
783
819
  test("cliValidateProgram rejects duplicate mcpResources URIs", () => {
784
820
  const root = testProgram({
785
821
  key: "app",
@@ -796,6 +832,7 @@ test("cliValidateProgram rejects duplicate mcpResources URIs", () => {
796
832
  expect(() => cliValidateProgram(root)).toThrow(/URIs must be unique/);
797
833
  });
798
834
 
835
+ /** CliValidateProgram rejects empty mcpServer. */
799
836
  test("cliValidateProgram rejects empty mcpServer", () => {
800
837
  const root = testProgram({
801
838
  key: "app",
@@ -806,6 +843,7 @@ test("cliValidateProgram rejects empty mcpServer", () => {
806
843
  expect(() => cliValidateProgram(root)).toThrow(/mcpServer requires enabled: true/);
807
844
  });
808
845
 
846
+ /** ResolveMcpSchemaUri uses sanitized root key. */
809
847
  test("resolveMcpSchemaUri uses sanitized root key", () => {
810
848
  const root = testProgram({
811
849
  key: "nested.ts",
@@ -816,6 +854,7 @@ test("resolveMcpSchemaUri uses sanitized root key", () => {
816
854
  expect(resolveMcpSchemaUri(root)).toBe("nested_ts://schema");
817
855
  });
818
856
 
857
+ /** ResolveMcpSchemaUri uses plain key when alphanumeric. */
819
858
  test("resolveMcpSchemaUri uses plain key when alphanumeric", () => {
820
859
  const root = testProgram({
821
860
  key: "qa",
@@ -826,6 +865,7 @@ test("resolveMcpSchemaUri uses plain key when alphanumeric", () => {
826
865
  expect(resolveMcpSchemaUri(root)).toBe("qa://schema");
827
866
  });
828
867
 
868
+ /** CliValidateProgram rejects resource URI matching default schema URI. */
829
869
  test("cliValidateProgram rejects resource URI matching default schema URI", () => {
830
870
  const root = testProgram({
831
871
  key: "app",
@@ -839,6 +879,7 @@ test("cliValidateProgram rejects resource URI matching default schema URI", () =
839
879
  expect(() => cliValidateProgram(root)).toThrow(/conflicts with built-in schema resource/);
840
880
  });
841
881
 
882
+ /** CliValidateProgram rejects resource URI matching schemaResourceUri. */
842
883
  test("cliValidateProgram rejects resource URI matching schemaResourceUri", () => {
843
884
  const root = testProgram({
844
885
  key: "app",
@@ -853,6 +894,7 @@ test("cliValidateProgram rejects resource URI matching schemaResourceUri", () =>
853
894
  expect(() => cliValidateProgram(root)).toThrow(/conflicts with built-in schema resource/);
854
895
  });
855
896
 
897
+ /** CliValidateProgram rejects resource URI matching auto docs topic. */
856
898
  test("cliValidateProgram rejects resource URI matching auto docs topic", () => {
857
899
  const root = testProgram({
858
900
  key: "app",
@@ -867,6 +909,7 @@ test("cliValidateProgram rejects resource URI matching auto docs topic", () => {
867
909
  expect(() => cliValidateProgram(root)).toThrow(/conflicts with auto docs topic resource/);
868
910
  });
869
911
 
912
+ /** AllMcpResources includes custom resources. */
870
913
  test("allMcpResources includes custom resources", () => {
871
914
  const root = testProgram({
872
915
  key: "app",
@@ -882,6 +925,7 @@ test("allMcpResources includes custom resources", () => {
882
925
  expect(resources.map((r) => r.uri)).toContain("test://x");
883
926
  });
884
927
 
928
+ /** AllMcpResources includes docs topic resources. */
885
929
  test("allMcpResources includes docs topic resources", () => {
886
930
  const root = testProgram({
887
931
  key: "app",
@@ -896,6 +940,7 @@ test("allMcpResources includes docs topic resources", () => {
896
940
  expect(readme?.load()).toBe("# hi\n");
897
941
  });
898
942
 
943
+ /** ApplyShellEnv merges PATH and preserves host vars. */
899
944
  test("applyShellEnv merges PATH and preserves host vars", () => {
900
945
  const origPath = process.env.PATH ?? "";
901
946
  const origHome = process.env.HOME;
@@ -915,6 +960,7 @@ test("applyShellEnv merges PATH and preserves host vars", () => {
915
960
  delete process.env.NEWVAR;
916
961
  });
917
962
 
963
+ /** Enum completions list choices in bash script. */
918
964
  test("Enum completions list choices in bash script", () => {
919
965
  const root = testProgram({
920
966
  key: "app",
@@ -936,6 +982,7 @@ test("Enum completions list choices in bash script", () => {
936
982
  expect(bash).toContain("prod");
937
983
  });
938
984
 
985
+ /** Nested fallback routes to default when argv exhausted at router. */
939
986
  test("nested fallback routes to default when argv exhausted at router", () => {
940
987
  const root = nestedDocsFallbackFixture();
941
988
  cliValidateProgram(root);
@@ -944,6 +991,7 @@ test("nested fallback routes to default when argv exhausted at router", () => {
944
991
  expect(pr.path).toEqual(["docs", "guide"]);
945
992
  });
946
993
 
994
+ /** Nested fallback MissingOrUnknown routes unknown token to default. */
947
995
  test("nested fallback MissingOrUnknown routes unknown token to default", () => {
948
996
  const root = testProgram({
949
997
  key: "app",
@@ -985,6 +1033,7 @@ test("nested fallback MissingOrUnknown routes unknown token to default", () => {
985
1033
  expect(pr.args).toEqual(["extra-topic"]);
986
1034
  });
987
1035
 
1036
+ /** Nested fallback MissingOnly errors on unknown subcommand. */
988
1037
  test("nested fallback MissingOnly errors on unknown subcommand", () => {
989
1038
  const root = nestedDocsFallbackFixture();
990
1039
  cliValidateProgram(root);
@@ -993,6 +1042,7 @@ test("nested fallback MissingOnly errors on unknown subcommand", () => {
993
1042
  expect(pr.errorMsg).toContain("Unknown subcommand");
994
1043
  });
995
1044
 
1045
+ /** CliValidateProgram rejects invalid nested fallbackCommand. */
996
1046
  test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
997
1047
  const root = testProgram({
998
1048
  key: "app",
@@ -1017,11 +1067,13 @@ test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
1017
1067
  );
1018
1068
  });
1019
1069
 
1070
+ /** CliValidateProgram accepts nested fallbackCommand when child exists. */
1020
1071
  test("cliValidateProgram accepts nested fallbackCommand when child exists", () => {
1021
1072
  const root = nestedDocsFallbackFixture();
1022
1073
  expect(() => cliValidateProgram(root)).not.toThrow();
1023
1074
  });
1024
1075
 
1076
+ /** Nested router scoped help does not route to fallback. */
1025
1077
  test("nested router scoped help does not route to fallback", () => {
1026
1078
  const root = nestedDocsFallbackFixture();
1027
1079
  cliValidateProgram(root);
@@ -1034,6 +1086,7 @@ test("nested router scoped help does not route to fallback", () => {
1034
1086
  expect(help).toContain("guide");
1035
1087
  });
1036
1088
 
1089
+ /** Varargs trailing option after positionals via Cli.invoke. */
1037
1090
  test("varargs trailing option after positionals via Cli.invoke", async () => {
1038
1091
  const root = varargsReadFixture();
1039
1092
  cliValidateProgram(root);
@@ -1043,6 +1096,7 @@ test("varargs trailing option after positionals via Cli.invoke", async () => {
1043
1096
  expect(pr.opts.json).toBe("1");
1044
1097
  });
1045
1098
 
1099
+ /** Varargs option before positionals. */
1046
1100
  test("varargs option before positionals", () => {
1047
1101
  const root = varargsReadFixture();
1048
1102
  cliValidateProgram(root);
@@ -1052,6 +1106,7 @@ test("varargs option before positionals", () => {
1052
1106
  expect(pr.opts.json).toBe("1");
1053
1107
  });
1054
1108
 
1109
+ /** Varargs multiple files then trailing option. */
1055
1110
  test("varargs multiple files then trailing option", () => {
1056
1111
  const root = varargsReadFixture();
1057
1112
  cliValidateProgram(root);
@@ -1061,6 +1116,7 @@ test("varargs multiple files then trailing option", () => {
1061
1116
  expect(pr.opts.json).toBe("1");
1062
1117
  });
1063
1118
 
1119
+ /** Varargs double dash forces positional. */
1064
1120
  test("varargs double dash forces positional", () => {
1065
1121
  const root = varargsReadFixture();
1066
1122
  cliValidateProgram(root);
@@ -1070,6 +1126,7 @@ test("varargs double dash forces positional", () => {
1070
1126
  expect(pr.opts.json).toBeUndefined();
1071
1127
  });
1072
1128
 
1129
+ /** Varargs unknown flag errors. */
1073
1130
  test("varargs unknown flag errors", async () => {
1074
1131
  const root = varargsReadFixture();
1075
1132
  cliValidateProgram(root);
@@ -1078,6 +1135,7 @@ test("varargs unknown flag errors", async () => {
1078
1135
  expect(result.stderr).toContain("--unknown");
1079
1136
  });
1080
1137
 
1138
+ /** McpToolCallToArgv rejects comma-separated string for varargs. */
1081
1139
  test("mcpToolCallToArgv rejects comma-separated string for varargs", () => {
1082
1140
  const tools = collectMcpTools(nestedMcpFixture);
1083
1141
  const read = tools.find((t) => t.name === "read")!;
@@ -1085,6 +1143,7 @@ test("mcpToolCallToArgv rejects comma-separated string for varargs", () => {
1085
1143
  expect(argv).toEqual({ error: expect.stringContaining("JSON array") });
1086
1144
  });
1087
1145
 
1146
+ /** McpToolCallToArgv rejects bare string for varargs. */
1088
1147
  test("mcpToolCallToArgv rejects bare string for varargs", () => {
1089
1148
  const tools = collectMcpTools(nestedMcpFixture);
1090
1149
  const read = tools.find((t) => t.name === "read")!;
@@ -1092,6 +1151,7 @@ test("mcpToolCallToArgv rejects bare string for varargs", () => {
1092
1151
  expect(argv).toEqual({ error: expect.stringContaining("JSON array") });
1093
1152
  });
1094
1153
 
1154
+ /** McpToolCallToArgv array varargs unchanged. */
1095
1155
  test("mcpToolCallToArgv array varargs unchanged", () => {
1096
1156
  const tools = collectMcpTools(nestedMcpFixture);
1097
1157
  const read = tools.find((t) => t.name === "read")!;
@@ -1099,6 +1159,7 @@ test("mcpToolCallToArgv array varargs unchanged", () => {
1099
1159
  expect(argv).toEqual(["read", "a", "b"]);
1100
1160
  });
1101
1161
 
1162
+ /** McpToolCallToArgv empty array varargs errors when required. */
1102
1163
  test("mcpToolCallToArgv empty array varargs errors when required", () => {
1103
1164
  const tools = collectMcpTools(nestedMcpFixture);
1104
1165
  const read = tools.find((t) => t.name === "read")!;
@@ -1108,7 +1169,8 @@ test("mcpToolCallToArgv empty array varargs errors when required", () => {
1108
1169
 
1109
1170
  // ── Skills ────────────────────────────────────────────────────────────────────
1110
1171
 
1111
- test("install config on non-root node is rejected", () => {
1172
+ /** Configure config on non-root node is rejected. */
1173
+ test("configure config on non-root node is rejected", () => {
1112
1174
  const root = {
1113
1175
  key: "app",
1114
1176
  version: "0.0.0",
@@ -1117,25 +1179,27 @@ test("install config on non-root node is rejected", () => {
1117
1179
  {
1118
1180
  key: "x",
1119
1181
  description: "",
1120
- install: { enabled: false },
1182
+ configure: { enabled: false },
1121
1183
  handler: () => {},
1122
1184
  },
1123
1185
  ],
1124
1186
  } as unknown as CliProgram;
1125
- expect(() => cliValidateProgram(root)).toThrow(/install is only supported on the program root/);
1187
+ expect(() => cliValidateProgram(root)).toThrow(/configure is only supported on the program root/);
1126
1188
  });
1127
1189
 
1128
- test("install.prefix is rejected", () => {
1190
+ /** Configure.prefix is rejected. */
1191
+ test("configure.prefix is rejected", () => {
1129
1192
  const root = {
1130
1193
  key: "app",
1131
1194
  version: "0.0.0",
1132
1195
  description: "",
1133
- install: { prefix: "/opt/bin" },
1196
+ configure: { prefix: "/opt/bin" },
1134
1197
  handler: () => {},
1135
1198
  } as unknown as CliProgram;
1136
- expect(() => cliValidateProgram(root)).toThrow(/install\.prefix removed/);
1199
+ expect(() => cliValidateProgram(root)).toThrow(/configure\.prefix removed/);
1137
1200
  });
1138
1201
 
1202
+ /** Tests that generateSkillBundle includes frontmatter and compact command index. */
1139
1203
  test("generateSkillBundle includes frontmatter and compact command index", () => {
1140
1204
  const bundle = generateSkillBundle(nestedMcpFixture, "cursor");
1141
1205
  expect(bundle.dirName).toBe("nested_ts");
@@ -1156,6 +1220,7 @@ test("generateSkillBundle includes frontmatter and compact command index", () =>
1156
1220
  expect(bundle.referenceMd).not.toContain("```json");
1157
1221
  });
1158
1222
 
1223
+ /** Tests that generatePluginSkillBundle is MCP routing stub without shell catalog. */
1159
1224
  test("generatePluginSkillBundle is MCP routing stub without shell catalog", () => {
1160
1225
  const bundle = generatePluginSkillBundle(nestedMcpFixture);
1161
1226
  expect(bundle.dirName).toBe("nested_ts");
@@ -1170,6 +1235,7 @@ test("generatePluginSkillBundle is MCP routing stub without shell catalog", () =
1170
1235
  expect(bundle.skillMd).not.toContain("## Commands");
1171
1236
  });
1172
1237
 
1238
+ /** CliSkillInstall writes project Cursor skill files. */
1173
1239
  test("cliSkillInstall writes project Cursor skill files", () => {
1174
1240
  const cwd = mkdtempSync(join(tmpdir(), "argsbarg-skill-"));
1175
1241
  const prev = process.cwd();
@@ -1184,7 +1250,7 @@ test("cliSkillInstall writes project Cursor skill files", () => {
1184
1250
  expect(readFileSync(join(skillDir, "reference.md"), "utf8")).toContain("CLI API reference");
1185
1251
  const skillText = readFileSync(join(skillDir, "SKILL.md"), "utf8");
1186
1252
  expect(skillText.startsWith("---\n")).toBe(true);
1187
- const hint = "<!-- Generated by nested.ts install --skill; do not edit. -->";
1253
+ const hint = "<!-- Generated by nested.ts configure; do not edit. -->";
1188
1254
  expect(skillText.indexOf(hint)).toBeGreaterThan(skillText.indexOf("---\n", 4));
1189
1255
  const refText = readFileSync(join(skillDir, "reference.md"), "utf8");
1190
1256
  expect(refText.startsWith(hint)).toBe(true);
@@ -1194,6 +1260,7 @@ test("cliSkillInstall writes project Cursor skill files", () => {
1194
1260
  }
1195
1261
  });
1196
1262
 
1263
+ /** CliSkillInstall global uses HOME skills directory. */
1197
1264
  test("cliSkillInstall global uses HOME skills directory", () => {
1198
1265
  const home = mkdtempSync(join(tmpdir(), "argsbarg-home-"));
1199
1266
  const prevHome = process.env.HOME;
@@ -1212,6 +1279,7 @@ test("cliSkillInstall global uses HOME skills directory", () => {
1212
1279
  }
1213
1280
  });
1214
1281
 
1282
+ /** CliSkillInstall rimraf overwrites existing directory. */
1215
1283
  test("cliSkillInstall rimraf overwrites existing directory", () => {
1216
1284
  const cwd = mkdtempSync(join(tmpdir(), "argsbarg-skill-dup-"));
1217
1285
  const prev = process.cwd();
@@ -1230,6 +1298,7 @@ test("cliSkillInstall rimraf overwrites existing directory", () => {
1230
1298
  }
1231
1299
  });
1232
1300
 
1301
+ /** CliSkillInstall claude target uses .claude/skills. */
1233
1302
  test("cliSkillInstall claude target uses .claude/skills", () => {
1234
1303
  const cwd = mkdtempSync(join(tmpdir(), "argsbarg-skill-claude-"));
1235
1304
  const prev = process.cwd();
package/src/schema.ts CHANGED
@@ -13,15 +13,7 @@ import {
13
13
  leafOutputSchema,
14
14
  } from "./types.ts";
15
15
 
16
- const RESERVED = new Set([
17
- "completion",
18
- "install",
19
- "uninstall",
20
- "docs",
21
- "mcp",
22
- "version",
23
- "config",
24
- ]);
16
+ const RESERVED = new Set(["completion", "configure", "docs", "mcp", "version", "config"]);
25
17
 
26
18
  function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport | null {
27
19
  if (cmd.hidden) {
package/src/skill/hint.ts CHANGED
@@ -23,9 +23,9 @@ export function insertGeneratedHint(
23
23
  return `${hint}${content}`;
24
24
  }
25
25
 
26
- /** Hint for `install --skill` output files. */
26
+ /** Hint for `configure` skill output files. */
27
27
  export function skillInstallHint(program: CliProgram): string {
28
- return generatedFileHtmlComment(`${program.key} install --skill`);
28
+ return generatedFileHtmlComment(`${program.key} configure`);
29
29
  }
30
30
 
31
31
  /** Applies install hints to SKILL.md (after frontmatter) and reference.md. */