argsbarg 5.0.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.
- package/CHANGELOG.md +8 -1
- package/docs/distribution-homebrew.md +2 -0
- package/examples/full-example/scripts/formula-shared.ts +1 -0
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +23 -0
- package/src/builtins/config.test.ts +8 -0
- package/src/cli-tool/cli-smoke.test.ts +8 -0
- package/src/cli-tool/create.test.ts +13 -0
- package/src/cli-tool/full-example-capabilities.test.ts +8 -0
- package/src/cli-tool/main.ts +0 -0
- package/src/config/context.test.ts +8 -0
- package/src/config/file.test.ts +12 -0
- package/src/config/resolve.test.ts +18 -0
- package/src/config/validate.test.ts +11 -0
- package/src/config.integration.test.ts +5 -0
- package/src/configure/configure.test.ts +22 -0
- package/src/configure/index.ts +14 -0
- package/src/configure/prompt.ts +7 -0
- package/src/docs/api-guide.test.ts +10 -0
- package/src/docs/docs.test.ts +27 -0
- package/src/docs/mcp-resources.test.ts +10 -0
- package/src/formats.test.ts +9 -0
- package/src/headless.test.ts +10 -0
- package/src/hidden-mcpb.test.ts +22 -0
- package/src/install/binary-placement.test.ts +14 -0
- package/src/install/gh-release-update.test.ts +9 -0
- package/src/install/install-validate.test.ts +10 -0
- package/src/install/mcp-codex.test.ts +7 -0
- package/src/install/mcp-openclaw.test.ts +6 -0
- package/src/install/mcp-opencode.test.ts +10 -0
- package/src/install/status.test.ts +9 -0
- package/src/install/targets.test.ts +19 -0
- package/src/invoke.test.ts +14 -0
- package/src/mcp/claude.test.ts +10 -0
- package/src/mcp/env.test.ts +12 -0
- package/src/mcp/zip.test.ts +5 -0
- package/src/mcp.integration.test.ts +39 -0
- package/src/parse.test.ts +71 -0
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);
|
|
@@ -532,12 +553,14 @@ test("docs schema exports JSON for leaf roots", async () => {
|
|
|
532
553
|
]);
|
|
533
554
|
});
|
|
534
555
|
|
|
556
|
+
/** Tests that version builtin prints program version. */
|
|
535
557
|
test("version builtin prints program version", async () => {
|
|
536
558
|
const { stdout, exitCode } = await $`bun run examples/nested.ts version`.nothrow().quiet();
|
|
537
559
|
expect(exitCode).toBe(0);
|
|
538
560
|
expect(stdout.toString().trim()).toMatch(/^\d+\.\d+\.\d+/);
|
|
539
561
|
});
|
|
540
562
|
|
|
563
|
+
/** Leaf root help omits hidden completion built-in. */
|
|
541
564
|
test("leaf root help omits hidden completion built-in", async () => {
|
|
542
565
|
const { stdout, exitCode } = await $`bun run examples/minimal.ts -h`.nothrow().quiet();
|
|
543
566
|
expect(exitCode).toBe(0);
|
|
@@ -545,6 +568,7 @@ test("leaf root help omits hidden completion built-in", async () => {
|
|
|
545
568
|
expect(stdout.toString()).toContain("configure");
|
|
546
569
|
});
|
|
547
570
|
|
|
571
|
+
/** Root --schema is no longer a flag. */
|
|
548
572
|
test("root --schema is no longer a flag", () => {
|
|
549
573
|
const root = testProgram({
|
|
550
574
|
key: "app",
|
|
@@ -567,6 +591,7 @@ test("root --schema is no longer a flag", () => {
|
|
|
567
591
|
expect(pr.kind).not.toBe(ParseKind.Ok);
|
|
568
592
|
});
|
|
569
593
|
|
|
594
|
+
/** CliSchemaJson omits handlers and completion built-ins. */
|
|
570
595
|
test("cliSchemaJson omits handlers and completion built-ins", () => {
|
|
571
596
|
const root = testProgram({
|
|
572
597
|
key: "app",
|
|
@@ -597,6 +622,7 @@ test("cliSchemaJson omits handlers and completion built-ins", () => {
|
|
|
597
622
|
expect(schema).not.toHaveProperty("handler");
|
|
598
623
|
});
|
|
599
624
|
|
|
625
|
+
/** CliSchemaExport resolves program key in install notes. */
|
|
600
626
|
test("cliSchemaExport resolves program key in install notes", () => {
|
|
601
627
|
const root = testProgram({
|
|
602
628
|
key: "myapp",
|
|
@@ -616,6 +642,7 @@ test("cliSchemaExport resolves program key in install notes", () => {
|
|
|
616
642
|
expect(json).toContain("brew install");
|
|
617
643
|
});
|
|
618
644
|
|
|
645
|
+
/** CliSchemaExport resolves {argsbarg:program} in consumer notes. */
|
|
619
646
|
test("cliSchemaExport resolves {argsbarg:program} in consumer notes", () => {
|
|
620
647
|
const root = testProgram({
|
|
621
648
|
key: "myapp",
|
|
@@ -635,6 +662,7 @@ test("cliSchemaExport resolves {argsbarg:program} in consumer notes", () => {
|
|
|
635
662
|
expect(schema.commands[0].notes).toBe("Run `myapp run` to start.");
|
|
636
663
|
});
|
|
637
664
|
|
|
665
|
+
/** Docs help lists schema, api, and skill subcommands. */
|
|
638
666
|
test("docs help lists schema, api, and skill subcommands", () => {
|
|
639
667
|
const root = testProgram({
|
|
640
668
|
key: "app",
|
|
@@ -661,6 +689,7 @@ test("docs help lists schema, api, and skill subcommands", () => {
|
|
|
661
689
|
expect(help).toContain("reference agent SKILL");
|
|
662
690
|
});
|
|
663
691
|
|
|
692
|
+
/** Root help omits legacy --schema flag. */
|
|
664
693
|
test("root help omits legacy --schema flag", () => {
|
|
665
694
|
const root = testProgram({
|
|
666
695
|
key: "app",
|
|
@@ -678,6 +707,7 @@ test("root help omits legacy --schema flag", () => {
|
|
|
678
707
|
expect(help).not.toContain("--schema");
|
|
679
708
|
});
|
|
680
709
|
|
|
710
|
+
/** Root help shows agent docs hint when docs enabled. */
|
|
681
711
|
test("root help shows agent docs hint when docs enabled", () => {
|
|
682
712
|
const root = testProgram({
|
|
683
713
|
key: "myapp",
|
|
@@ -694,6 +724,7 @@ test("root help shows agent docs hint when docs enabled", () => {
|
|
|
694
724
|
expect(help).not.toContain("install --skill");
|
|
695
725
|
});
|
|
696
726
|
|
|
727
|
+
/** Root help omits agent hint when docs disabled. */
|
|
697
728
|
test("root help omits agent hint when docs disabled", () => {
|
|
698
729
|
const root = testProgram({
|
|
699
730
|
key: "myapp",
|
|
@@ -706,6 +737,7 @@ test("root help omits agent hint when docs disabled", () => {
|
|
|
706
737
|
expect(help).not.toContain("docs skill");
|
|
707
738
|
});
|
|
708
739
|
|
|
740
|
+
/** Root help includes program notes and agent hint. */
|
|
709
741
|
test("root help includes program notes and agent hint", () => {
|
|
710
742
|
const root = testProgram({
|
|
711
743
|
key: "myapp",
|
|
@@ -723,6 +755,7 @@ test("root help includes program notes and agent hint", () => {
|
|
|
723
755
|
expect(help).toContain("myapp docs skill");
|
|
724
756
|
});
|
|
725
757
|
|
|
758
|
+
/** Enum option inputSchema includes enum array. */
|
|
726
759
|
test("Enum option inputSchema includes enum array", () => {
|
|
727
760
|
const tools = collectMcpTools(enumMcpFixture);
|
|
728
761
|
const run = tools.find((t) => t.name === "run")!;
|
|
@@ -730,6 +763,7 @@ test("Enum option inputSchema includes enum array", () => {
|
|
|
730
763
|
expect(schema.properties.mode.enum).toEqual(["dev", "prod"]);
|
|
731
764
|
});
|
|
732
765
|
|
|
766
|
+
/** CliValidateProgram rejects Enum with no choices. */
|
|
733
767
|
test("cliValidateProgram rejects Enum with no choices", () => {
|
|
734
768
|
const root = testProgram({
|
|
735
769
|
key: "app",
|
|
@@ -740,6 +774,7 @@ test("cliValidateProgram rejects Enum with no choices", () => {
|
|
|
740
774
|
expect(() => cliValidateProgram(root)).toThrow(/requires non-empty choices/);
|
|
741
775
|
});
|
|
742
776
|
|
|
777
|
+
/** CliValidateProgram rejects Enum with duplicate choices. */
|
|
743
778
|
test("cliValidateProgram rejects Enum with duplicate choices", () => {
|
|
744
779
|
const root = testProgram({
|
|
745
780
|
key: "app",
|
|
@@ -750,6 +785,7 @@ test("cliValidateProgram rejects Enum with duplicate choices", () => {
|
|
|
750
785
|
expect(() => cliValidateProgram(root)).toThrow(/choices must be distinct/);
|
|
751
786
|
});
|
|
752
787
|
|
|
788
|
+
/** McpTool.description override wins without env suffix. */
|
|
753
789
|
test("mcpTool.description override wins without env suffix", () => {
|
|
754
790
|
const root = testProgram({
|
|
755
791
|
key: "app",
|
|
@@ -768,6 +804,7 @@ test("mcpTool.description override wins without env suffix", () => {
|
|
|
768
804
|
expect(tools[0]?.description).toBe("custom");
|
|
769
805
|
});
|
|
770
806
|
|
|
807
|
+
/** CliValidateProgram requires program.appConfig description. */
|
|
771
808
|
test("cliValidateProgram requires program.appConfig description", () => {
|
|
772
809
|
const root = testProgram({
|
|
773
810
|
key: "app",
|
|
@@ -778,6 +815,7 @@ test("cliValidateProgram requires program.appConfig description", () => {
|
|
|
778
815
|
expect(() => cliValidateProgram(root)).toThrow(/description must be a non-empty string/);
|
|
779
816
|
});
|
|
780
817
|
|
|
818
|
+
/** CliValidateProgram rejects duplicate mcpResources URIs. */
|
|
781
819
|
test("cliValidateProgram rejects duplicate mcpResources URIs", () => {
|
|
782
820
|
const root = testProgram({
|
|
783
821
|
key: "app",
|
|
@@ -794,6 +832,7 @@ test("cliValidateProgram rejects duplicate mcpResources URIs", () => {
|
|
|
794
832
|
expect(() => cliValidateProgram(root)).toThrow(/URIs must be unique/);
|
|
795
833
|
});
|
|
796
834
|
|
|
835
|
+
/** CliValidateProgram rejects empty mcpServer. */
|
|
797
836
|
test("cliValidateProgram rejects empty mcpServer", () => {
|
|
798
837
|
const root = testProgram({
|
|
799
838
|
key: "app",
|
|
@@ -804,6 +843,7 @@ test("cliValidateProgram rejects empty mcpServer", () => {
|
|
|
804
843
|
expect(() => cliValidateProgram(root)).toThrow(/mcpServer requires enabled: true/);
|
|
805
844
|
});
|
|
806
845
|
|
|
846
|
+
/** ResolveMcpSchemaUri uses sanitized root key. */
|
|
807
847
|
test("resolveMcpSchemaUri uses sanitized root key", () => {
|
|
808
848
|
const root = testProgram({
|
|
809
849
|
key: "nested.ts",
|
|
@@ -814,6 +854,7 @@ test("resolveMcpSchemaUri uses sanitized root key", () => {
|
|
|
814
854
|
expect(resolveMcpSchemaUri(root)).toBe("nested_ts://schema");
|
|
815
855
|
});
|
|
816
856
|
|
|
857
|
+
/** ResolveMcpSchemaUri uses plain key when alphanumeric. */
|
|
817
858
|
test("resolveMcpSchemaUri uses plain key when alphanumeric", () => {
|
|
818
859
|
const root = testProgram({
|
|
819
860
|
key: "qa",
|
|
@@ -824,6 +865,7 @@ test("resolveMcpSchemaUri uses plain key when alphanumeric", () => {
|
|
|
824
865
|
expect(resolveMcpSchemaUri(root)).toBe("qa://schema");
|
|
825
866
|
});
|
|
826
867
|
|
|
868
|
+
/** CliValidateProgram rejects resource URI matching default schema URI. */
|
|
827
869
|
test("cliValidateProgram rejects resource URI matching default schema URI", () => {
|
|
828
870
|
const root = testProgram({
|
|
829
871
|
key: "app",
|
|
@@ -837,6 +879,7 @@ test("cliValidateProgram rejects resource URI matching default schema URI", () =
|
|
|
837
879
|
expect(() => cliValidateProgram(root)).toThrow(/conflicts with built-in schema resource/);
|
|
838
880
|
});
|
|
839
881
|
|
|
882
|
+
/** CliValidateProgram rejects resource URI matching schemaResourceUri. */
|
|
840
883
|
test("cliValidateProgram rejects resource URI matching schemaResourceUri", () => {
|
|
841
884
|
const root = testProgram({
|
|
842
885
|
key: "app",
|
|
@@ -851,6 +894,7 @@ test("cliValidateProgram rejects resource URI matching schemaResourceUri", () =>
|
|
|
851
894
|
expect(() => cliValidateProgram(root)).toThrow(/conflicts with built-in schema resource/);
|
|
852
895
|
});
|
|
853
896
|
|
|
897
|
+
/** CliValidateProgram rejects resource URI matching auto docs topic. */
|
|
854
898
|
test("cliValidateProgram rejects resource URI matching auto docs topic", () => {
|
|
855
899
|
const root = testProgram({
|
|
856
900
|
key: "app",
|
|
@@ -865,6 +909,7 @@ test("cliValidateProgram rejects resource URI matching auto docs topic", () => {
|
|
|
865
909
|
expect(() => cliValidateProgram(root)).toThrow(/conflicts with auto docs topic resource/);
|
|
866
910
|
});
|
|
867
911
|
|
|
912
|
+
/** AllMcpResources includes custom resources. */
|
|
868
913
|
test("allMcpResources includes custom resources", () => {
|
|
869
914
|
const root = testProgram({
|
|
870
915
|
key: "app",
|
|
@@ -880,6 +925,7 @@ test("allMcpResources includes custom resources", () => {
|
|
|
880
925
|
expect(resources.map((r) => r.uri)).toContain("test://x");
|
|
881
926
|
});
|
|
882
927
|
|
|
928
|
+
/** AllMcpResources includes docs topic resources. */
|
|
883
929
|
test("allMcpResources includes docs topic resources", () => {
|
|
884
930
|
const root = testProgram({
|
|
885
931
|
key: "app",
|
|
@@ -894,6 +940,7 @@ test("allMcpResources includes docs topic resources", () => {
|
|
|
894
940
|
expect(readme?.load()).toBe("# hi\n");
|
|
895
941
|
});
|
|
896
942
|
|
|
943
|
+
/** ApplyShellEnv merges PATH and preserves host vars. */
|
|
897
944
|
test("applyShellEnv merges PATH and preserves host vars", () => {
|
|
898
945
|
const origPath = process.env.PATH ?? "";
|
|
899
946
|
const origHome = process.env.HOME;
|
|
@@ -913,6 +960,7 @@ test("applyShellEnv merges PATH and preserves host vars", () => {
|
|
|
913
960
|
delete process.env.NEWVAR;
|
|
914
961
|
});
|
|
915
962
|
|
|
963
|
+
/** Enum completions list choices in bash script. */
|
|
916
964
|
test("Enum completions list choices in bash script", () => {
|
|
917
965
|
const root = testProgram({
|
|
918
966
|
key: "app",
|
|
@@ -934,6 +982,7 @@ test("Enum completions list choices in bash script", () => {
|
|
|
934
982
|
expect(bash).toContain("prod");
|
|
935
983
|
});
|
|
936
984
|
|
|
985
|
+
/** Nested fallback routes to default when argv exhausted at router. */
|
|
937
986
|
test("nested fallback routes to default when argv exhausted at router", () => {
|
|
938
987
|
const root = nestedDocsFallbackFixture();
|
|
939
988
|
cliValidateProgram(root);
|
|
@@ -942,6 +991,7 @@ test("nested fallback routes to default when argv exhausted at router", () => {
|
|
|
942
991
|
expect(pr.path).toEqual(["docs", "guide"]);
|
|
943
992
|
});
|
|
944
993
|
|
|
994
|
+
/** Nested fallback MissingOrUnknown routes unknown token to default. */
|
|
945
995
|
test("nested fallback MissingOrUnknown routes unknown token to default", () => {
|
|
946
996
|
const root = testProgram({
|
|
947
997
|
key: "app",
|
|
@@ -983,6 +1033,7 @@ test("nested fallback MissingOrUnknown routes unknown token to default", () => {
|
|
|
983
1033
|
expect(pr.args).toEqual(["extra-topic"]);
|
|
984
1034
|
});
|
|
985
1035
|
|
|
1036
|
+
/** Nested fallback MissingOnly errors on unknown subcommand. */
|
|
986
1037
|
test("nested fallback MissingOnly errors on unknown subcommand", () => {
|
|
987
1038
|
const root = nestedDocsFallbackFixture();
|
|
988
1039
|
cliValidateProgram(root);
|
|
@@ -991,6 +1042,7 @@ test("nested fallback MissingOnly errors on unknown subcommand", () => {
|
|
|
991
1042
|
expect(pr.errorMsg).toContain("Unknown subcommand");
|
|
992
1043
|
});
|
|
993
1044
|
|
|
1045
|
+
/** CliValidateProgram rejects invalid nested fallbackCommand. */
|
|
994
1046
|
test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
|
|
995
1047
|
const root = testProgram({
|
|
996
1048
|
key: "app",
|
|
@@ -1015,11 +1067,13 @@ test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
|
|
|
1015
1067
|
);
|
|
1016
1068
|
});
|
|
1017
1069
|
|
|
1070
|
+
/** CliValidateProgram accepts nested fallbackCommand when child exists. */
|
|
1018
1071
|
test("cliValidateProgram accepts nested fallbackCommand when child exists", () => {
|
|
1019
1072
|
const root = nestedDocsFallbackFixture();
|
|
1020
1073
|
expect(() => cliValidateProgram(root)).not.toThrow();
|
|
1021
1074
|
});
|
|
1022
1075
|
|
|
1076
|
+
/** Nested router scoped help does not route to fallback. */
|
|
1023
1077
|
test("nested router scoped help does not route to fallback", () => {
|
|
1024
1078
|
const root = nestedDocsFallbackFixture();
|
|
1025
1079
|
cliValidateProgram(root);
|
|
@@ -1032,6 +1086,7 @@ test("nested router scoped help does not route to fallback", () => {
|
|
|
1032
1086
|
expect(help).toContain("guide");
|
|
1033
1087
|
});
|
|
1034
1088
|
|
|
1089
|
+
/** Varargs trailing option after positionals via Cli.invoke. */
|
|
1035
1090
|
test("varargs trailing option after positionals via Cli.invoke", async () => {
|
|
1036
1091
|
const root = varargsReadFixture();
|
|
1037
1092
|
cliValidateProgram(root);
|
|
@@ -1041,6 +1096,7 @@ test("varargs trailing option after positionals via Cli.invoke", async () => {
|
|
|
1041
1096
|
expect(pr.opts.json).toBe("1");
|
|
1042
1097
|
});
|
|
1043
1098
|
|
|
1099
|
+
/** Varargs option before positionals. */
|
|
1044
1100
|
test("varargs option before positionals", () => {
|
|
1045
1101
|
const root = varargsReadFixture();
|
|
1046
1102
|
cliValidateProgram(root);
|
|
@@ -1050,6 +1106,7 @@ test("varargs option before positionals", () => {
|
|
|
1050
1106
|
expect(pr.opts.json).toBe("1");
|
|
1051
1107
|
});
|
|
1052
1108
|
|
|
1109
|
+
/** Varargs multiple files then trailing option. */
|
|
1053
1110
|
test("varargs multiple files then trailing option", () => {
|
|
1054
1111
|
const root = varargsReadFixture();
|
|
1055
1112
|
cliValidateProgram(root);
|
|
@@ -1059,6 +1116,7 @@ test("varargs multiple files then trailing option", () => {
|
|
|
1059
1116
|
expect(pr.opts.json).toBe("1");
|
|
1060
1117
|
});
|
|
1061
1118
|
|
|
1119
|
+
/** Varargs double dash forces positional. */
|
|
1062
1120
|
test("varargs double dash forces positional", () => {
|
|
1063
1121
|
const root = varargsReadFixture();
|
|
1064
1122
|
cliValidateProgram(root);
|
|
@@ -1068,6 +1126,7 @@ test("varargs double dash forces positional", () => {
|
|
|
1068
1126
|
expect(pr.opts.json).toBeUndefined();
|
|
1069
1127
|
});
|
|
1070
1128
|
|
|
1129
|
+
/** Varargs unknown flag errors. */
|
|
1071
1130
|
test("varargs unknown flag errors", async () => {
|
|
1072
1131
|
const root = varargsReadFixture();
|
|
1073
1132
|
cliValidateProgram(root);
|
|
@@ -1076,6 +1135,7 @@ test("varargs unknown flag errors", async () => {
|
|
|
1076
1135
|
expect(result.stderr).toContain("--unknown");
|
|
1077
1136
|
});
|
|
1078
1137
|
|
|
1138
|
+
/** McpToolCallToArgv rejects comma-separated string for varargs. */
|
|
1079
1139
|
test("mcpToolCallToArgv rejects comma-separated string for varargs", () => {
|
|
1080
1140
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
1081
1141
|
const read = tools.find((t) => t.name === "read")!;
|
|
@@ -1083,6 +1143,7 @@ test("mcpToolCallToArgv rejects comma-separated string for varargs", () => {
|
|
|
1083
1143
|
expect(argv).toEqual({ error: expect.stringContaining("JSON array") });
|
|
1084
1144
|
});
|
|
1085
1145
|
|
|
1146
|
+
/** McpToolCallToArgv rejects bare string for varargs. */
|
|
1086
1147
|
test("mcpToolCallToArgv rejects bare string for varargs", () => {
|
|
1087
1148
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
1088
1149
|
const read = tools.find((t) => t.name === "read")!;
|
|
@@ -1090,6 +1151,7 @@ test("mcpToolCallToArgv rejects bare string for varargs", () => {
|
|
|
1090
1151
|
expect(argv).toEqual({ error: expect.stringContaining("JSON array") });
|
|
1091
1152
|
});
|
|
1092
1153
|
|
|
1154
|
+
/** McpToolCallToArgv array varargs unchanged. */
|
|
1093
1155
|
test("mcpToolCallToArgv array varargs unchanged", () => {
|
|
1094
1156
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
1095
1157
|
const read = tools.find((t) => t.name === "read")!;
|
|
@@ -1097,6 +1159,7 @@ test("mcpToolCallToArgv array varargs unchanged", () => {
|
|
|
1097
1159
|
expect(argv).toEqual(["read", "a", "b"]);
|
|
1098
1160
|
});
|
|
1099
1161
|
|
|
1162
|
+
/** McpToolCallToArgv empty array varargs errors when required. */
|
|
1100
1163
|
test("mcpToolCallToArgv empty array varargs errors when required", () => {
|
|
1101
1164
|
const tools = collectMcpTools(nestedMcpFixture);
|
|
1102
1165
|
const read = tools.find((t) => t.name === "read")!;
|
|
@@ -1106,6 +1169,7 @@ test("mcpToolCallToArgv empty array varargs errors when required", () => {
|
|
|
1106
1169
|
|
|
1107
1170
|
// ── Skills ────────────────────────────────────────────────────────────────────
|
|
1108
1171
|
|
|
1172
|
+
/** Configure config on non-root node is rejected. */
|
|
1109
1173
|
test("configure config on non-root node is rejected", () => {
|
|
1110
1174
|
const root = {
|
|
1111
1175
|
key: "app",
|
|
@@ -1123,6 +1187,7 @@ test("configure config on non-root node is rejected", () => {
|
|
|
1123
1187
|
expect(() => cliValidateProgram(root)).toThrow(/configure is only supported on the program root/);
|
|
1124
1188
|
});
|
|
1125
1189
|
|
|
1190
|
+
/** Configure.prefix is rejected. */
|
|
1126
1191
|
test("configure.prefix is rejected", () => {
|
|
1127
1192
|
const root = {
|
|
1128
1193
|
key: "app",
|
|
@@ -1134,6 +1199,7 @@ test("configure.prefix is rejected", () => {
|
|
|
1134
1199
|
expect(() => cliValidateProgram(root)).toThrow(/configure\.prefix removed/);
|
|
1135
1200
|
});
|
|
1136
1201
|
|
|
1202
|
+
/** Tests that generateSkillBundle includes frontmatter and compact command index. */
|
|
1137
1203
|
test("generateSkillBundle includes frontmatter and compact command index", () => {
|
|
1138
1204
|
const bundle = generateSkillBundle(nestedMcpFixture, "cursor");
|
|
1139
1205
|
expect(bundle.dirName).toBe("nested_ts");
|
|
@@ -1154,6 +1220,7 @@ test("generateSkillBundle includes frontmatter and compact command index", () =>
|
|
|
1154
1220
|
expect(bundle.referenceMd).not.toContain("```json");
|
|
1155
1221
|
});
|
|
1156
1222
|
|
|
1223
|
+
/** Tests that generatePluginSkillBundle is MCP routing stub without shell catalog. */
|
|
1157
1224
|
test("generatePluginSkillBundle is MCP routing stub without shell catalog", () => {
|
|
1158
1225
|
const bundle = generatePluginSkillBundle(nestedMcpFixture);
|
|
1159
1226
|
expect(bundle.dirName).toBe("nested_ts");
|
|
@@ -1168,6 +1235,7 @@ test("generatePluginSkillBundle is MCP routing stub without shell catalog", () =
|
|
|
1168
1235
|
expect(bundle.skillMd).not.toContain("## Commands");
|
|
1169
1236
|
});
|
|
1170
1237
|
|
|
1238
|
+
/** CliSkillInstall writes project Cursor skill files. */
|
|
1171
1239
|
test("cliSkillInstall writes project Cursor skill files", () => {
|
|
1172
1240
|
const cwd = mkdtempSync(join(tmpdir(), "argsbarg-skill-"));
|
|
1173
1241
|
const prev = process.cwd();
|
|
@@ -1192,6 +1260,7 @@ test("cliSkillInstall writes project Cursor skill files", () => {
|
|
|
1192
1260
|
}
|
|
1193
1261
|
});
|
|
1194
1262
|
|
|
1263
|
+
/** CliSkillInstall global uses HOME skills directory. */
|
|
1195
1264
|
test("cliSkillInstall global uses HOME skills directory", () => {
|
|
1196
1265
|
const home = mkdtempSync(join(tmpdir(), "argsbarg-home-"));
|
|
1197
1266
|
const prevHome = process.env.HOME;
|
|
@@ -1210,6 +1279,7 @@ test("cliSkillInstall global uses HOME skills directory", () => {
|
|
|
1210
1279
|
}
|
|
1211
1280
|
});
|
|
1212
1281
|
|
|
1282
|
+
/** CliSkillInstall rimraf overwrites existing directory. */
|
|
1213
1283
|
test("cliSkillInstall rimraf overwrites existing directory", () => {
|
|
1214
1284
|
const cwd = mkdtempSync(join(tmpdir(), "argsbarg-skill-dup-"));
|
|
1215
1285
|
const prev = process.cwd();
|
|
@@ -1228,6 +1298,7 @@ test("cliSkillInstall rimraf overwrites existing directory", () => {
|
|
|
1228
1298
|
}
|
|
1229
1299
|
});
|
|
1230
1300
|
|
|
1301
|
+
/** CliSkillInstall claude target uses .claude/skills. */
|
|
1231
1302
|
test("cliSkillInstall claude target uses .claude/skills", () => {
|
|
1232
1303
|
const cwd = mkdtempSync(join(tmpdir(), "argsbarg-skill-claude-"));
|
|
1233
1304
|
const prev = process.cwd();
|