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.
Files changed (38) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/docs/distribution-homebrew.md +2 -0
  3. package/examples/full-example/scripts/formula-shared.ts +1 -0
  4. package/package.json +1 -1
  5. package/src/builtins/builtins.test.ts +23 -0
  6. package/src/builtins/config.test.ts +8 -0
  7. package/src/cli-tool/cli-smoke.test.ts +8 -0
  8. package/src/cli-tool/create.test.ts +13 -0
  9. package/src/cli-tool/full-example-capabilities.test.ts +8 -0
  10. package/src/cli-tool/main.ts +0 -0
  11. package/src/config/context.test.ts +8 -0
  12. package/src/config/file.test.ts +12 -0
  13. package/src/config/resolve.test.ts +18 -0
  14. package/src/config/validate.test.ts +11 -0
  15. package/src/config.integration.test.ts +5 -0
  16. package/src/configure/configure.test.ts +22 -0
  17. package/src/configure/index.ts +14 -0
  18. package/src/configure/prompt.ts +7 -0
  19. package/src/docs/api-guide.test.ts +10 -0
  20. package/src/docs/docs.test.ts +27 -0
  21. package/src/docs/mcp-resources.test.ts +10 -0
  22. package/src/formats.test.ts +9 -0
  23. package/src/headless.test.ts +10 -0
  24. package/src/hidden-mcpb.test.ts +22 -0
  25. package/src/install/binary-placement.test.ts +14 -0
  26. package/src/install/gh-release-update.test.ts +9 -0
  27. package/src/install/install-validate.test.ts +10 -0
  28. package/src/install/mcp-codex.test.ts +7 -0
  29. package/src/install/mcp-openclaw.test.ts +6 -0
  30. package/src/install/mcp-opencode.test.ts +10 -0
  31. package/src/install/status.test.ts +9 -0
  32. package/src/install/targets.test.ts +19 -0
  33. package/src/invoke.test.ts +14 -0
  34. package/src/mcp/claude.test.ts +10 -0
  35. package/src/mcp/env.test.ts +12 -0
  36. package/src/mcp/zip.test.ts +5 -0
  37. package/src/mcp.integration.test.ts +39 -0
  38. package/src/parse.test.ts +71 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.0.2] - 2026-07-04
11
+
12
+ ### Changed
13
+
14
+ - **JSDoc style** — every `describe` and `test` block in `src/**/*.test.ts` now has a human-readable JSDoc; `configure` modules brought in line with file headers and symbol docs.
15
+
10
16
  ## [5.0.1] - 2026-07-04
11
17
 
12
18
 
@@ -567,7 +573,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
567
573
  - Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
568
574
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
569
575
 
570
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.0.1...HEAD
576
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.0.2...HEAD
577
+ [5.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.2
571
578
  [5.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.1
572
579
  [5.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.0
573
580
  [4.1.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v4.1.1
@@ -60,6 +60,8 @@ Completions require users to configure their shell per [Homebrew Shell Completio
60
60
 
61
61
  **Why configure is separate from `post_install`:** the wizard is interactive (TTY + prompts for secrets). Formula `post_install` runs non-interactively during `brew install` and in CI (`brew test`). Apps with `appConfig` print a one-line configure hint in formula `caveats` instead.
62
62
 
63
+ **MCP hosts:** when `mcpServer.enabled` is true, add a caveats line that chat apps (Cursor, Claude Desktop, etc.) must be **restarted** after `brew install` / `brew upgrade` — `post_install` updates MCP config on disk, but hosts typically load it only at startup.
64
+
63
65
  ## Bootstrap CLI (`argsbarg create`)
64
66
 
65
67
  Copy the shipped `examples/full-example` template into a new directory with identity substitutions, then run install, schemagen, tests, and git init (when appropriate):
@@ -17,6 +17,7 @@ export const formulaPostInstallRuby = `def post_install
17
17
  export const formulaCaveatsRuby = `def caveats
18
18
  <<~EOS
19
19
  Run \`${key} configure\` to set up agent artifacts and app config (interactive).
20
+ Restart MCP chat apps (Cursor, Claude Desktop, etc.) after install or upgrade so they load the updated server.
20
21
  EOS
21
22
  end`;
22
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for builtins/builtins module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { resolveCapabilities } from "../capabilities.ts";
3
7
  import { cliBuiltinDocsGroup } from "../docs/builtin.ts";
@@ -31,7 +35,9 @@ const noMcp: CliProgram = {
31
35
  commands: [{ key: "ping", description: "Ping.", handler: () => {} }],
32
36
  };
33
37
 
38
+ /** Tests for builtins help copy. */
34
39
  describe("builtins help copy", () => {
40
+ /** Configure command includes Homebrew-oriented description. */
35
41
  test("configure command includes Homebrew-oriented description", () => {
36
42
  const configure = cliBuiltinConfigureCommand(fixture);
37
43
  expect(configure.description).toContain("agent skills");
@@ -45,6 +51,7 @@ describe("builtins help copy", () => {
45
51
  expect(yesOpt?.shortName).toBe("y");
46
52
  });
47
53
 
54
+ /** Configure copy omits MCP when mcpServer unset. */
48
55
  test("configure copy omits MCP when mcpServer unset", () => {
49
56
  const caps = resolveCapabilities(noMcp);
50
57
  expect(configureCommandDescription(noMcp, caps)).toBe(
@@ -56,6 +63,7 @@ describe("builtins help copy", () => {
56
63
  expect(configure.description).not.toContain("MCP");
57
64
  });
58
65
 
66
+ /** Configure notes mention brew upgrade and interactive configure. */
59
67
  test("configure notes mention brew upgrade and interactive configure", () => {
60
68
  const configure = cliBuiltinConfigureCommand(fixture);
61
69
  expect(configure.notes).toContain("brew upgrade");
@@ -71,6 +79,7 @@ describe("builtins help copy", () => {
71
79
  expect(configureBuiltinOptions(withConfig).map((o) => o.name)).toContain("remove-config");
72
80
  });
73
81
 
82
+ /** Configure -y parses as --yes. */
74
83
  test("configure -y parses as --yes", () => {
75
84
  const root = cliParseRoot(fixture);
76
85
  const pr = postParseValidate(root, parse(root, ["configure", "-y", "--sync"]));
@@ -81,6 +90,7 @@ describe("builtins help copy", () => {
81
90
  }
82
91
  });
83
92
 
93
+ /** Mcp builtin description is user-facing. */
84
94
  test("mcp builtin description is user-facing", () => {
85
95
  const withDocs: CliProgram = {
86
96
  ...fixture,
@@ -93,7 +103,9 @@ describe("builtins help copy", () => {
93
103
  });
94
104
  });
95
105
 
106
+ /** Tests for presentation root. */
96
107
  describe("presentation root", () => {
108
+ /** Includes mcp and configure when enabled. */
97
109
  test("includes mcp and configure when enabled", () => {
98
110
  const root = cliPresentationRoot(fixture);
99
111
  const keys = root.commands?.map((c) => c.key) ?? [];
@@ -103,19 +115,23 @@ describe("presentation root", () => {
103
115
  expect(keys).not.toContain("install");
104
116
  });
105
117
 
118
+ /** Omits configure when configure.enabled is false. */
106
119
  test("omits configure when configure.enabled is false", () => {
107
120
  const disabled: CliProgram = { ...fixture, configure: { enabled: false } };
108
121
  const root = cliPresentationRoot(disabled);
109
122
  expect(root.commands?.map((c) => c.key)).not.toContain("configure");
110
123
  });
111
124
 
125
+ /** Includes version builtin. */
112
126
  test("includes version builtin", () => {
113
127
  const root = cliPresentationRoot(fixture);
114
128
  expect(root.commands?.map((c) => c.key)).toContain("version");
115
129
  });
116
130
  });
117
131
 
132
+ /** Tests for completion emitters. */
118
133
  describe("completion emitters", () => {
134
+ /** Tests that fish script references app key and subcommands. */
119
135
  test("fish script references app key and subcommands", () => {
120
136
  const schema = cliPresentationRoot(fixture);
121
137
  const fish = completionFishScript(schema);
@@ -124,6 +140,7 @@ describe("completion emitters", () => {
124
140
  expect(fish).toContain("configure");
125
141
  });
126
142
 
143
+ /** Tests that bash script includes configure flags. */
127
144
  test("bash script includes configure flags", () => {
128
145
  const schema = cliPresentationRoot(fixture);
129
146
  const bash = completionBashScript(schema);
@@ -131,6 +148,7 @@ describe("completion emitters", () => {
131
148
  expect(bash).toContain("--sync");
132
149
  });
133
150
 
151
+ /** Tests that zsh script registers compdef. */
134
152
  test("zsh script registers compdef", () => {
135
153
  const schema = cliPresentationRoot({
136
154
  key: "zapp",
@@ -144,7 +162,9 @@ describe("completion emitters", () => {
144
162
  });
145
163
  });
146
164
 
165
+ /** Tests for schema export builtins. */
147
166
  describe("schema export builtins", () => {
167
+ /** ExportPresentationBuiltins includes config when appConfig set. */
148
168
  test("exportPresentationBuiltins includes config when appConfig set", () => {
149
169
  const withConfig: CliProgram = {
150
170
  ...fixture,
@@ -159,13 +179,16 @@ describe("schema export builtins", () => {
159
179
  expect(builtins.map((b) => b.key)).toContain("configure");
160
180
  });
161
181
 
182
+ /** ExportPresentationBuiltins omits hidden completion. */
162
183
  test("exportPresentationBuiltins omits hidden completion", () => {
163
184
  const builtins = exportPresentationBuiltins(fixture);
164
185
  expect(builtins.map((b) => b.key)).not.toContain("completion");
165
186
  });
166
187
  });
167
188
 
189
+ /** Tests for docs skill topic copy. */
168
190
  describe("docs skill topic copy", () => {
191
+ /** Tests that mentions configure when configure is enabled. */
169
192
  test("mentions configure when configure is enabled", () => {
170
193
  const withDocs: CliProgram = {
171
194
  ...noMcp,
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for builtins/config module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
7
  import { tmpdir } from "node:os";
@@ -20,7 +24,9 @@ function configFixture(): CliProgram {
20
24
  };
21
25
  }
22
26
 
27
+ /** Tests for builtins/config. */
23
28
  describe("builtins/config", () => {
29
+ /** Tests that config get redacts sensitive values. */
24
30
  test("config get redacts sensitive values", async () => {
25
31
  const dir = mkdtempSync(join(tmpdir(), "cfg-builtin-"));
26
32
  const prevHome = process.env.HOME;
@@ -43,6 +49,7 @@ describe("builtins/config", () => {
43
49
  }
44
50
  });
45
51
 
52
+ /** Tests that config get --json redacts sensitive as { set: true }. */
46
53
  test("config get --json redacts sensitive as { set: true }", async () => {
47
54
  const dir = mkdtempSync(join(tmpdir(), "cfg-builtin-"));
48
55
  const prevHome = process.env.HOME;
@@ -65,6 +72,7 @@ describe("builtins/config", () => {
65
72
  }
66
73
  });
67
74
 
75
+ /** Tests that config set writes and resolves without required exit. */
68
76
  test("config set writes and resolves without required exit", async () => {
69
77
  const dir = mkdtempSync(join(tmpdir(), "cfg-builtin-"));
70
78
  const prevHome = process.env.HOME;
@@ -1,16 +1,23 @@
1
+ /*
2
+ Tests for cli-tool/cli-smoke module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { spawnSync } from "node:child_process";
3
7
  import { join } from "node:path";
4
8
 
5
9
  const main = join(import.meta.dir, "main.ts");
6
10
 
11
+ /** Tests for argsbarg cli-tool. */
7
12
  describe("argsbarg cli-tool", () => {
13
+ /** Tests that version subcommand prints version. */
8
14
  test("version subcommand prints version", () => {
9
15
  const proc = spawnSync("bun", [main, "version"], { encoding: "utf8" });
10
16
  expect(proc.status).toBe(0);
11
17
  expect(proc.stdout.trim().length).toBeGreaterThan(0);
12
18
  });
13
19
 
20
+ /** Tests that help lists create and version only (no install, completion, mcp). */
14
21
  test("help lists create and version only (no install, completion, mcp)", () => {
15
22
  const proc = spawnSync("bun", [main, "--help"], { encoding: "utf8" });
16
23
  expect(proc.status).toBe(0);
@@ -21,6 +28,7 @@ describe("argsbarg cli-tool", () => {
21
28
  expect(proc.stdout).not.toContain("mcp");
22
29
  });
23
30
 
31
+ /** Completion subcommand is disabled. */
24
32
  test("completion subcommand is disabled", () => {
25
33
  const proc = spawnSync("bun", [main, "completion", "bash"], { encoding: "utf8" });
26
34
  expect(proc.status).toBe(1);
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for cli-tool/create module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { mkdtempSync, rmSync } from "node:fs";
3
7
  import { tmpdir } from "node:os";
@@ -12,7 +16,9 @@ import {
12
16
  substituteTemplateContent,
13
17
  } from "./create.ts";
14
18
 
19
+ /** Tests for argsbarg create. */
15
20
  describe("argsbarg create", () => {
21
+ /** Tests that substitutes {key} tokens. */
16
22
  test("substitutes {key} tokens", () => {
17
23
  const out = substituteTemplateContent(
18
24
  "key={key} class={className} env={envPrefix}_API_TOKEN tap={tap} org={tapOrg}",
@@ -38,12 +44,14 @@ describe("argsbarg create", () => {
38
44
  expect(out).not.toContain("{key}");
39
45
  });
40
46
 
47
+ /** Tests that classNameFromKey. */
41
48
  test("classNameFromKey", () => {
42
49
  expect(classNameFromKey("sqsp-i18n")).toBe("SqspI18n");
43
50
  expect(classNameFromKey("at1")).toBe("At1");
44
51
  expect(classNameFromKey("1password")).toBe("App1password");
45
52
  });
46
53
 
54
+ /** ResolveCreateOptions derives identity defaults from key. */
47
55
  test("resolveCreateOptions derives identity defaults from key", () => {
48
56
  expect(resolveCreateOptions({ key: "1password", releaseRepo: "org/1password" }).className).toBe(
49
57
  "App1password",
@@ -59,10 +67,12 @@ describe("argsbarg create", () => {
59
67
  expect(opts.desc).toBe("At1 CLI");
60
68
  });
61
69
 
70
+ /** ResolveCreateOptions requires release repo. */
62
71
  test("resolveCreateOptions requires release repo", () => {
63
72
  expect(() => resolveCreateOptions({ key: "at1" })).toThrow(/release repo/i);
64
73
  });
65
74
 
75
+ /** Tests that renderCreateTree includes justfile and create-identity. */
66
76
  test("renderCreateTree includes justfile and create-identity", () => {
67
77
  const tree = renderCreateTree({
68
78
  key: "testapp",
@@ -88,6 +98,7 @@ describe("argsbarg create", () => {
88
98
  expect(formula).toContain("create-identity.ts");
89
99
  });
90
100
 
101
+ /** Tests that --check detects drift. */
91
102
  test("--check detects drift", () => {
92
103
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-create-"));
93
104
  try {
@@ -111,6 +122,7 @@ describe("argsbarg create", () => {
111
122
  }
112
123
  });
113
124
 
125
+ /** Tests that --check infers options from create-identity.ts. */
114
126
  test("--check infers options from create-identity.ts", () => {
115
127
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-create-"));
116
128
  try {
@@ -134,6 +146,7 @@ describe("argsbarg create", () => {
134
146
  }
135
147
  });
136
148
 
149
+ /** Tests that --diff captures drift details. */
137
150
  test("--diff captures drift details", () => {
138
151
  const drifts = diffCreateDetails("/nonexistent", { key: "x", releaseRepo: "org/x" });
139
152
  expect(drifts.length).toBeGreaterThan(0);
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for cli-tool/full-example-capabilities module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { readFileSync } from "node:fs";
3
7
  import { join } from "node:path";
@@ -45,7 +49,9 @@ const sinkProgram = {
45
49
  ],
46
50
  } satisfies CliProgram;
47
51
 
52
+ /** Tests for full-example template. */
48
53
  describe("full-example template", () => {
54
+ /** Tests that program source enables every builtin flag. */
49
55
  test("program source enables every builtin flag", () => {
50
56
  expect(programSource).toContain("mcpServer: {");
51
57
  expect(programSource).toContain("enabled: true");
@@ -53,11 +59,13 @@ describe("full-example template", () => {
53
59
  expect(programSource).toContain("appConfig:");
54
60
  });
55
61
 
62
+ /** Status command defines outputSchema. */
56
63
  test("status command defines outputSchema", () => {
57
64
  const statusSource = readFileSync(join(exampleRoot, "src/commands/status/command.ts"), "utf8");
58
65
  expect(statusSource).toContain("outputSchema:");
59
66
  });
60
67
 
68
+ /** ResolveCapabilities matches full sink shape. */
61
69
  test("resolveCapabilities matches full sink shape", () => {
62
70
  expect(resolveCapabilities(sinkProgram)).toEqual({
63
71
  completion: true,
File without changes
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for config/context module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { mkdtempSync, rmSync } from "node:fs";
3
7
  import { tmpdir } from "node:os";
@@ -20,7 +24,9 @@ const program: CliProgram = {
20
24
  handler: () => {},
21
25
  };
22
26
 
27
+ /** Tests for config/context. */
23
28
  describe("config/context", () => {
29
+ /** Tests that AppConfigSnapshot get, require, read, set. */
24
30
  test("AppConfigSnapshot get, require, read, set", () => {
25
31
  const dir = mkdtempSync(join(tmpdir(), "ctx-test-"));
26
32
  const prevHome = process.env.HOME;
@@ -50,6 +56,7 @@ describe("config/context", () => {
50
56
  }
51
57
  });
52
58
 
59
+ /** Tests that EmptyAppConfigSnapshot when program.appConfig unset. */
53
60
  test("EmptyAppConfigSnapshot when program.appConfig unset", () => {
54
61
  const programWithoutConfig: CliProgram = {
55
62
  key: "x",
@@ -65,6 +72,7 @@ describe("config/context", () => {
65
72
  expect(empty.dir).toBe(dirname(empty.path));
66
73
  });
67
74
 
75
+ /** Tests that AppConfigSnapshot path uses OS default from program key. */
68
76
  test("AppConfigSnapshot path uses OS default from program key", () => {
69
77
  const ctx = createAppConfigSnapshot(program, {}, {});
70
78
  expect(ctx.path).toContain("ctx_test");
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for config/file module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
7
  import { tmpdir } from "node:os";
@@ -40,7 +44,9 @@ function withHome<T>(fn: (home: string) => T): T {
40
44
  }
41
45
  }
42
46
 
47
+ /** Tests for config/file. */
43
48
  describe("config/file", () => {
49
+ /** BuildProgramUserConfig from program.appConfig env entries. */
44
50
  test("buildProgramUserConfig from program.appConfig env entries", () => {
45
51
  const cfg = buildProgramUserConfig(program);
46
52
  expect(cfg?.api_token).toMatchObject({
@@ -51,6 +57,7 @@ describe("config/file", () => {
51
57
  expect(cfg?.port).toBeUndefined();
52
58
  });
53
59
 
60
+ /** ResolveAppConfigPath uses config.json. */
54
61
  test("resolveAppConfigPath uses config.json", () => {
55
62
  withHome((home) => {
56
63
  expect(resolveAppConfigPath(program)).toBe(
@@ -59,6 +66,7 @@ describe("config/file", () => {
59
66
  });
60
67
  });
61
68
 
69
+ /** ResolveAppConfig prefers host env over file. */
62
70
  test("resolveAppConfig prefers host env over file", () => {
63
71
  withHome((_home) => {
64
72
  const prevToken = process.env.API_TOKEN;
@@ -76,6 +84,7 @@ describe("config/file", () => {
76
84
  });
77
85
  });
78
86
 
87
+ /** MissingRequiredConfig and formatMissingConfigMessage. */
79
88
  test("missingRequiredConfig and formatMissingConfigMessage", () => {
80
89
  const prev = process.env.API_TOKEN;
81
90
  delete process.env.API_TOKEN;
@@ -91,6 +100,7 @@ describe("config/file", () => {
91
100
  }
92
101
  });
93
102
 
103
+ /** Rejects unknown keys on read. */
94
104
  test("rejects unknown keys on read", () => {
95
105
  withHome(() => {
96
106
  const configPath = resolveAppConfigPath(program);
@@ -100,6 +110,7 @@ describe("config/file", () => {
100
110
  });
101
111
  });
102
112
 
113
+ /** WriteAppConfigFile round-trip. */
103
114
  test("writeAppConfigFile round-trip", () => {
104
115
  withHome(() => {
105
116
  writeAppConfigFile(program, { apiToken: "saved" });
@@ -108,6 +119,7 @@ describe("config/file", () => {
108
119
  });
109
120
  });
110
121
 
122
+ /** Tests that uninstallAppConfig removes config directory recursively. */
111
123
  test("uninstallAppConfig removes config directory recursively", () => {
112
124
  withHome(() => {
113
125
  writeAppConfigFile(program, { apiToken: "saved" });
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for config/resolve module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import type { CliProgram } from "../types.ts";
3
7
  import { captureMappedHostEnv, exportConfigToEnv, resolveAppConfig } from "./resolve.ts";
@@ -25,7 +29,9 @@ const program: CliProgram = {
25
29
  handler: () => {},
26
30
  };
27
31
 
32
+ /** Tests for config/resolve. */
28
33
  describe("config/resolve", () => {
34
+ /** Tests that prefers env over file for mapped keys. */
29
35
  test("prefers env over file for mapped keys", () => {
30
36
  const prev = process.env.API_TOKEN;
31
37
  process.env.API_TOKEN = "from-env";
@@ -38,6 +44,7 @@ describe("config/resolve", () => {
38
44
  }
39
45
  });
40
46
 
47
+ /** Tests that uses file when env empty. */
41
48
  test("uses file when env empty", () => {
42
49
  const prev = process.env.API_TOKEN;
43
50
  delete process.env.API_TOKEN;
@@ -49,6 +56,7 @@ describe("config/resolve", () => {
49
56
  }
50
57
  });
51
58
 
59
+ /** Tests that empty string in env or file counts as missing. */
52
60
  test("empty string in env or file counts as missing", () => {
53
61
  const prev = process.env.API_TOKEN;
54
62
  process.env.API_TOKEN = "";
@@ -61,6 +69,7 @@ describe("config/resolve", () => {
61
69
  }
62
70
  });
63
71
 
72
+ /** Applies jsonSchema default when file and env absent. */
64
73
  test("applies jsonSchema default when file and env absent", () => {
65
74
  const prev = process.env.API_TOKEN;
66
75
  delete process.env.API_TOKEN;
@@ -73,6 +82,7 @@ describe("config/resolve", () => {
73
82
  }
74
83
  });
75
84
 
85
+ /** All-string mode uses entry.default. */
76
86
  test("all-string mode uses entry.default", () => {
77
87
  const stringProgram: CliProgram = {
78
88
  ...program,
@@ -86,6 +96,7 @@ describe("config/resolve", () => {
86
96
  expect(resolved.greeting).toBe("world");
87
97
  });
88
98
 
99
+ /** Tests that prefers captured host env over file when process.env was exported from file. */
89
100
  test("prefers captured host env over file when process.env was exported from file", () => {
90
101
  const hostEnv = { API_TOKEN: "from-host" };
91
102
  process.env.API_TOKEN = "from-file-export";
@@ -97,6 +108,7 @@ describe("config/resolve", () => {
97
108
  }
98
109
  });
99
110
 
111
+ /** ExportConfigToEnv does not overwrite host env. */
100
112
  test("exportConfigToEnv does not overwrite host env", () => {
101
113
  const prev = process.env.API_TOKEN;
102
114
  process.env.API_TOKEN = "from-host";
@@ -110,6 +122,7 @@ describe("config/resolve", () => {
110
122
  }
111
123
  });
112
124
 
125
+ /** Resolve callback supplies value when env and file are absent. */
113
126
  test("resolve callback supplies value when env and file are absent", () => {
114
127
  const resolveProgram: CliProgram = {
115
128
  ...program,
@@ -135,6 +148,7 @@ describe("config/resolve", () => {
135
148
  }
136
149
  });
137
150
 
151
+ /** Env overrides resolve callback. */
138
152
  test("env overrides resolve callback", () => {
139
153
  const resolveProgram: CliProgram = {
140
154
  ...program,
@@ -161,6 +175,7 @@ describe("config/resolve", () => {
161
175
  }
162
176
  });
163
177
 
178
+ /** File overrides resolve callback. */
164
179
  test("file overrides resolve callback", () => {
165
180
  const resolveProgram: CliProgram = {
166
181
  ...program,
@@ -186,6 +201,7 @@ describe("config/resolve", () => {
186
201
  }
187
202
  });
188
203
 
204
+ /** Tests that falls back to env when resolve returns undefined. */
189
205
  test("falls back to env when resolve returns undefined", () => {
190
206
  const resolveProgram: CliProgram = {
191
207
  ...program,
@@ -212,6 +228,7 @@ describe("config/resolve", () => {
212
228
  }
213
229
  });
214
230
 
231
+ /** Resolve callback is skipped when env is set. */
215
232
  test("resolve callback is skipped when env is set", () => {
216
233
  let resolveCalled = false;
217
234
  const resolveProgram: CliProgram = {
@@ -243,6 +260,7 @@ describe("config/resolve", () => {
243
260
  }
244
261
  });
245
262
 
263
+ /** Tests that async resolve is ignored with stderr warning. */
246
264
  test("async resolve is ignored with stderr warning", () => {
247
265
  const resolveProgram: CliProgram = {
248
266
  ...program,
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for config/validate module behavior.
3
+ */
4
+
1
5
  import { describe, expect, test } from "bun:test";
2
6
  import { parseConfigSetValue, validateConfigDocument } from "./validate.ts";
3
7
 
@@ -17,7 +21,9 @@ const rootSchema = {
17
21
  },
18
22
  };
19
23
 
24
+ /** Tests for config/validate. */
20
25
  describe("config/validate", () => {
26
+ /** Tests that accepts valid document. */
21
27
  test("accepts valid document", () => {
22
28
  const result = validateConfigDocument(
23
29
  { apiToken: "x", maxRetries: 3, prefs: { ttl: 3600 } },
@@ -27,12 +33,14 @@ describe("config/validate", () => {
27
33
  expect(result.errors).toEqual([]);
28
34
  });
29
35
 
36
+ /** Rejects missing required property. */
30
37
  test("rejects missing required property", () => {
31
38
  const result = validateConfigDocument({ apiToken: "x" }, rootSchema);
32
39
  expect(result.valid).toBe(false);
33
40
  expect(result.errors.some((e) => e.includes("maxRetries"))).toBe(true);
34
41
  });
35
42
 
43
+ /** Rejects unknown property when additionalProperties is false. */
36
44
  test("rejects unknown property when additionalProperties is false", () => {
37
45
  const result = validateConfigDocument(
38
46
  { apiToken: "x", maxRetries: 1, extra: true },
@@ -42,16 +50,19 @@ describe("config/validate", () => {
42
50
  expect(result.errors.some((e) => e.includes("extra"))).toBe(true);
43
51
  });
44
52
 
53
+ /** Rejects type mismatch. */
45
54
  test("rejects type mismatch", () => {
46
55
  const result = validateConfigDocument({ apiToken: "x", maxRetries: "nope" }, rootSchema);
47
56
  expect(result.valid).toBe(false);
48
57
  });
49
58
 
59
+ /** ParseConfigSetValue coerces number and boolean. */
50
60
  test("parseConfigSetValue coerces number and boolean", () => {
51
61
  expect(parseConfigSetValue("5", { type: "integer" }, rootSchema, false)).toBe(5);
52
62
  expect(parseConfigSetValue("true", { type: "boolean" }, rootSchema, false)).toBe(true);
53
63
  });
54
64
 
65
+ /** ParseConfigSetValue requires --json for objects. */
55
66
  test("parseConfigSetValue requires --json for objects", () => {
56
67
  expect(() => parseConfigSetValue('{"ttl":1}', { type: "object" }, rootSchema, false)).toThrow(
57
68
  /--json/,
@@ -10,6 +10,7 @@ import { bootstrapAppConfig } from "./config/bootstrap.ts";
10
10
  import { resolveAppConfigPath } from "./config/file.ts";
11
11
  import { mcpRequest, testProgram } from "./test-fixtures.ts";
12
12
 
13
+ /** Tests that bootstrapAppConfig prefers host env over config file. */
13
14
  test("bootstrapAppConfig prefers host env over config file", () => {
14
15
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-env-"));
15
16
  const prevHome = process.env.HOME;
@@ -43,6 +44,7 @@ test("bootstrapAppConfig prefers host env over config file", () => {
43
44
  }
44
45
  });
45
46
 
47
+ /** MCP program.appConfig fails when required config missing. */
46
48
  test("MCP program.appConfig fails when required config missing", async () => {
47
49
  const responses = await mcpRequest(
48
50
  [
@@ -60,6 +62,7 @@ test("MCP program.appConfig fails when required config missing", async () => {
60
62
  expect(res.result.content[0]?.text).toContain("argsTestSecret");
61
63
  });
62
64
 
65
+ /** MCP program.appConfig succeeds when env present. */
63
66
  test("MCP program.appConfig succeeds when env present", async () => {
64
67
  const responses = await mcpRequest(
65
68
  [
@@ -77,6 +80,7 @@ test("MCP program.appConfig succeeds when env present", async () => {
77
80
  expect(res.result.content[0]?.text.trim()).toBe("sekrit");
78
81
  });
79
82
 
83
+ /** MCP config file loads and exports vars for tool handlers. */
80
84
  test("MCP config file loads and exports vars for tool handlers", async () => {
81
85
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-mcp-"));
82
86
  const configFile = join(dir, ".local", "lib", "mcp_test", "config");
@@ -106,6 +110,7 @@ test("MCP config file loads and exports vars for tool handlers", async () => {
106
110
  rmSync(dir, { recursive: true, force: true });
107
111
  });
108
112
 
113
+ /** Cli.run docs api skips required appConfig exit. */
109
114
  test("Cli.run docs api skips required appConfig exit", async () => {
110
115
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-docs-skip-"));
111
116
  const configFile = join(dir, ".local", "lib", "docs_skip_test", "config");