argsbarg 5.0.1 → 5.0.3

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 (40) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/docs/distribution-homebrew.md +2 -0
  3. package/examples/full-example/scripts/formula-shared.ts +1 -0
  4. package/index.d.ts +10 -5
  5. package/package.json +1 -1
  6. package/src/builtins/builtins.test.ts +12 -0
  7. package/src/builtins/config.test.ts +8 -0
  8. package/src/cli-tool/cli-smoke.test.ts +5 -0
  9. package/src/cli-tool/create.test.ts +10 -0
  10. package/src/cli-tool/full-example-capabilities.test.ts +6 -0
  11. package/src/cli-tool/main.ts +0 -0
  12. package/src/config/context.test.ts +7 -0
  13. package/src/config/file.test.ts +7 -0
  14. package/src/config/resolve.test.ts +11 -0
  15. package/src/config/validate.test.ts +5 -0
  16. package/src/config.integration.test.ts +5 -0
  17. package/src/configure/configure.test.ts +11 -0
  18. package/src/configure/index.ts +14 -0
  19. package/src/configure/prompt.ts +7 -0
  20. package/src/docs/api-guide.test.ts +6 -0
  21. package/src/docs/docs.test.ts +7 -0
  22. package/src/docs/mcp-resources.test.ts +4 -0
  23. package/src/formats.test.ts +4 -0
  24. package/src/headless.test.ts +6 -0
  25. package/src/headless.ts +5 -4
  26. package/src/hidden-mcpb.test.ts +10 -0
  27. package/src/install/binary-placement.test.ts +9 -0
  28. package/src/install/gh-release-update.test.ts +4 -0
  29. package/src/install/install-validate.test.ts +5 -0
  30. package/src/install/mcp-codex.test.ts +7 -0
  31. package/src/install/mcp-openclaw.test.ts +6 -0
  32. package/src/install/mcp-opencode.test.ts +6 -0
  33. package/src/install/status.test.ts +9 -0
  34. package/src/install/targets.test.ts +9 -0
  35. package/src/invoke.test.ts +7 -0
  36. package/src/mcp/claude.test.ts +6 -0
  37. package/src/mcp/env.test.ts +7 -0
  38. package/src/mcp/zip.test.ts +4 -0
  39. package/src/mcp.integration.test.ts +17 -0
  40. package/src/parse.test.ts +43 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.0.3] - 2026-07-04
11
+
12
+ ### Changed
13
+
14
+ - **JSDoc style** — short `test()` callbacks no longer carry redundant one-liners; `describe` blocks still documented. `requireYesInNonTty` uses per-parameter JSDoc instead of `@param`. Pure re-export barrels stay comment-free.
15
+
16
+ ## [5.0.2] - 2026-07-04
17
+
18
+ ### Changed
19
+
20
+ - **JSDoc style** — every `describe` block in `src/**/*.test.ts` now has a human-readable JSDoc; `configure` modules brought in line with file headers and symbol docs.
21
+
10
22
  ## [5.0.1] - 2026-07-04
11
23
 
12
24
 
@@ -567,7 +579,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
567
579
  - 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
580
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
569
581
 
570
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.0.1...HEAD
582
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.0.3...HEAD
583
+ [5.0.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.3
584
+ [5.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.2
571
585
  [5.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.1
572
586
  [5.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.0
573
587
  [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/index.d.ts CHANGED
@@ -551,11 +551,16 @@ export declare function shouldRunHeadlessWithYes(ctx: HeadlessContext, opts: {
551
551
  hasRequiredArgs: boolean;
552
552
  dryRun?: boolean;
553
553
  }, interactive?: boolean): boolean;
554
- /**
555
- * Exits when non-interactive mode is used without `--yes`.
556
- * @param hint - Command-specific guidance appended to the error
557
- */
558
- export declare function requireYesInNonTty(yes: boolean, hint: string, dryRun?: boolean, interactive?: boolean): void;
554
+ /** Exits when non-interactive mode is used without `--yes`. */
555
+ export declare function requireYesInNonTty(
556
+ /** True when `--yes` was passed on the command line. */
557
+ yes: boolean,
558
+ /** Command-specific guidance appended to the error message. */
559
+ hint: string,
560
+ /** When true, skip the check (dry-run preview). */
561
+ dryRun?: boolean,
562
+ /** Injectable TTY probe for tests. */
563
+ interactive?: boolean): void;
559
564
  /** Prefixes a success message when running in dry-run mode. */
560
565
  export declare function formatDryRunMessage(message: string, dryRun: boolean): string;
561
566
  /** Resolved paths for `mcp bundle`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
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,6 +35,7 @@ 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", () => {
35
40
  test("configure command includes Homebrew-oriented description", () => {
36
41
  const configure = cliBuiltinConfigureCommand(fixture);
@@ -56,6 +61,7 @@ describe("builtins help copy", () => {
56
61
  expect(configure.description).not.toContain("MCP");
57
62
  });
58
63
 
64
+ /** Configure notes mention brew upgrade and interactive configure. */
59
65
  test("configure notes mention brew upgrade and interactive configure", () => {
60
66
  const configure = cliBuiltinConfigureCommand(fixture);
61
67
  expect(configure.notes).toContain("brew upgrade");
@@ -93,6 +99,7 @@ describe("builtins help copy", () => {
93
99
  });
94
100
  });
95
101
 
102
+ /** Tests for presentation root. */
96
103
  describe("presentation root", () => {
97
104
  test("includes mcp and configure when enabled", () => {
98
105
  const root = cliPresentationRoot(fixture);
@@ -115,6 +122,7 @@ describe("presentation root", () => {
115
122
  });
116
123
  });
117
124
 
125
+ /** Tests for completion emitters. */
118
126
  describe("completion emitters", () => {
119
127
  test("fish script references app key and subcommands", () => {
120
128
  const schema = cliPresentationRoot(fixture);
@@ -144,7 +152,9 @@ describe("completion emitters", () => {
144
152
  });
145
153
  });
146
154
 
155
+ /** Tests for schema export builtins. */
147
156
  describe("schema export builtins", () => {
157
+ /** ExportPresentationBuiltins includes config when appConfig set. */
148
158
  test("exportPresentationBuiltins includes config when appConfig set", () => {
149
159
  const withConfig: CliProgram = {
150
160
  ...fixture,
@@ -165,7 +175,9 @@ describe("schema export builtins", () => {
165
175
  });
166
176
  });
167
177
 
178
+ /** Tests for docs skill topic copy. */
168
179
  describe("docs skill topic copy", () => {
180
+ /** Tests that mentions configure when configure is enabled. */
169
181
  test("mentions configure when configure is enabled", () => {
170
182
  const withDocs: CliProgram = {
171
183
  ...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,9 +1,14 @@
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", () => {
8
13
  test("version subcommand prints version", () => {
9
14
  const proc = spawnSync("bun", [main, "version"], { encoding: "utf8" });
@@ -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}",
@@ -44,6 +50,7 @@ describe("argsbarg create", () => {
44
50
  expect(classNameFromKey("1password")).toBe("App1password");
45
51
  });
46
52
 
53
+ /** ResolveCreateOptions derives identity defaults from key. */
47
54
  test("resolveCreateOptions derives identity defaults from key", () => {
48
55
  expect(resolveCreateOptions({ key: "1password", releaseRepo: "org/1password" }).className).toBe(
49
56
  "App1password",
@@ -63,6 +70,7 @@ describe("argsbarg create", () => {
63
70
  expect(() => resolveCreateOptions({ key: "at1" })).toThrow(/release repo/i);
64
71
  });
65
72
 
73
+ /** Tests that renderCreateTree includes justfile and create-identity. */
66
74
  test("renderCreateTree includes justfile and create-identity", () => {
67
75
  const tree = renderCreateTree({
68
76
  key: "testapp",
@@ -88,6 +96,7 @@ describe("argsbarg create", () => {
88
96
  expect(formula).toContain("create-identity.ts");
89
97
  });
90
98
 
99
+ /** Tests that --check detects drift. */
91
100
  test("--check detects drift", () => {
92
101
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-create-"));
93
102
  try {
@@ -111,6 +120,7 @@ describe("argsbarg create", () => {
111
120
  }
112
121
  });
113
122
 
123
+ /** Tests that --check infers options from create-identity.ts. */
114
124
  test("--check infers options from create-identity.ts", () => {
115
125
  const dir = mkdtempSync(join(tmpdir(), "argsbarg-create-"));
116
126
  try {
@@ -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");
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",
@@ -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,6 +44,7 @@ function withHome<T>(fn: (home: string) => T): T {
40
44
  }
41
45
  }
42
46
 
47
+ /** Tests for config/file. */
43
48
  describe("config/file", () => {
44
49
  test("buildProgramUserConfig from program.appConfig env entries", () => {
45
50
  const cfg = buildProgramUserConfig(program);
@@ -59,6 +64,7 @@ describe("config/file", () => {
59
64
  });
60
65
  });
61
66
 
67
+ /** ResolveAppConfig prefers host env over file. */
62
68
  test("resolveAppConfig prefers host env over file", () => {
63
69
  withHome((_home) => {
64
70
  const prevToken = process.env.API_TOKEN;
@@ -76,6 +82,7 @@ describe("config/file", () => {
76
82
  });
77
83
  });
78
84
 
85
+ /** MissingRequiredConfig and formatMissingConfigMessage. */
79
86
  test("missingRequiredConfig and formatMissingConfigMessage", () => {
80
87
  const prev = process.env.API_TOKEN;
81
88
  delete process.env.API_TOKEN;
@@ -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,6 +29,7 @@ const program: CliProgram = {
25
29
  handler: () => {},
26
30
  };
27
31
 
32
+ /** Tests for config/resolve. */
28
33
  describe("config/resolve", () => {
29
34
  test("prefers env over file for mapped keys", () => {
30
35
  const prev = process.env.API_TOKEN;
@@ -110,6 +115,7 @@ describe("config/resolve", () => {
110
115
  }
111
116
  });
112
117
 
118
+ /** Resolve callback supplies value when env and file are absent. */
113
119
  test("resolve callback supplies value when env and file are absent", () => {
114
120
  const resolveProgram: CliProgram = {
115
121
  ...program,
@@ -135,6 +141,7 @@ describe("config/resolve", () => {
135
141
  }
136
142
  });
137
143
 
144
+ /** Env overrides resolve callback. */
138
145
  test("env overrides resolve callback", () => {
139
146
  const resolveProgram: CliProgram = {
140
147
  ...program,
@@ -161,6 +168,7 @@ describe("config/resolve", () => {
161
168
  }
162
169
  });
163
170
 
171
+ /** File overrides resolve callback. */
164
172
  test("file overrides resolve callback", () => {
165
173
  const resolveProgram: CliProgram = {
166
174
  ...program,
@@ -186,6 +194,7 @@ describe("config/resolve", () => {
186
194
  }
187
195
  });
188
196
 
197
+ /** Tests that falls back to env when resolve returns undefined. */
189
198
  test("falls back to env when resolve returns undefined", () => {
190
199
  const resolveProgram: CliProgram = {
191
200
  ...program,
@@ -212,6 +221,7 @@ describe("config/resolve", () => {
212
221
  }
213
222
  });
214
223
 
224
+ /** Resolve callback is skipped when env is set. */
215
225
  test("resolve callback is skipped when env is set", () => {
216
226
  let resolveCalled = false;
217
227
  const resolveProgram: CliProgram = {
@@ -243,6 +253,7 @@ describe("config/resolve", () => {
243
253
  }
244
254
  });
245
255
 
256
+ /** Tests that async resolve is ignored with stderr warning. */
246
257
  test("async resolve is ignored with stderr warning", () => {
247
258
  const resolveProgram: CliProgram = {
248
259
  ...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,6 +21,7 @@ const rootSchema = {
17
21
  },
18
22
  };
19
23
 
24
+ /** Tests for config/validate. */
20
25
  describe("config/validate", () => {
21
26
  test("accepts valid document", () => {
22
27
  const result = validateConfigDocument(
@@ -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");
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for configure/configure module behavior.
3
+ */
4
+
1
5
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
6
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
7
  import { tmpdir } from "node:os";
@@ -42,6 +46,7 @@ afterEach(() => {
42
46
  rmSync(home, { recursive: true, force: true });
43
47
  });
44
48
 
49
+ /** Tests for configure opts. */
45
50
  describe("configure opts", () => {
46
51
  test("validate rejects multiple modes", () => {
47
52
  const opts = parseConfigureOpts({ sync: "1", status: "1" });
@@ -59,6 +64,7 @@ describe("configure opts", () => {
59
64
  });
60
65
  });
61
66
 
67
+ /** Tests for install paths. */
62
68
  describe("install paths", () => {
63
69
  test("resolveInstallPaths includes skill and mcp paths", () => {
64
70
  const paths = resolveInstallPaths(fixture);
@@ -79,7 +85,9 @@ describe("install paths", () => {
79
85
  });
80
86
  });
81
87
 
88
+ /** Tests for detect installed. */
82
89
  describe("detect installed", () => {
90
+ /** Detects cursor mcp when configured. */
83
91
  test("detects cursor mcp when configured", () => {
84
92
  const paths = resolveInstallPaths(fixture);
85
93
  mkdirSync(join(home, ".cursor"), { recursive: true });
@@ -95,6 +103,7 @@ describe("detect installed", () => {
95
103
  });
96
104
  });
97
105
 
106
+ /** Tests for sync plan. */
98
107
  describe("sync plan", () => {
99
108
  test("buildUpdatePlan greenfield includes agent targets", () => {
100
109
  const paths = resolveInstallPaths(fixture);
@@ -123,6 +132,7 @@ describe("sync plan", () => {
123
132
  });
124
133
  });
125
134
 
135
+ /** Tests for remove plan. */
126
136
  describe("remove plan", () => {
127
137
  test("buildUninstallPlan --all with nothing installed is empty", () => {
128
138
  const paths = resolveInstallPaths(fixture);
@@ -131,6 +141,7 @@ describe("remove plan", () => {
131
141
  });
132
142
  });
133
143
 
144
+ /** Tests for app config wizard. */
134
145
  describe("app config wizard", () => {
135
146
  test("appConfigHasEntries when entries exist", () => {
136
147
  expect(
@@ -1,3 +1,7 @@
1
+ /*
2
+ Interactive and automated `configure` command orchestration (agent artifacts and app config).
3
+ */
4
+
1
5
  import { resolveCapabilities } from "../capabilities.ts";
2
6
  import { displayAppConfigPath, runConfigure } from "../config/bootstrap.ts";
3
7
  import { resolveInstallPaths } from "../install/paths.ts";
@@ -36,6 +40,7 @@ export function appConfigHasEntries(program: CliProgram): boolean {
36
40
  return !!entries && Object.keys(entries).length > 0;
37
41
  }
38
42
 
43
+ /** Parsed flags for the top-level `configure` built-in. */
39
44
  export interface ConfigureOpts {
40
45
  sync?: boolean;
41
46
  removeAll?: boolean;
@@ -46,6 +51,7 @@ export interface ConfigureOpts {
46
51
  json?: boolean;
47
52
  }
48
53
 
54
+ /** Maps raw argv flags into {@link ConfigureOpts}. */
49
55
  export function parseConfigureOpts(raw: Record<string, string>): ConfigureOpts {
50
56
  const flag = (name: string) => raw[name] === "1";
51
57
  return {
@@ -59,6 +65,7 @@ export function parseConfigureOpts(raw: Record<string, string>): ConfigureOpts {
59
65
  };
60
66
  }
61
67
 
68
+ /** Returns an error message when configure flags are inconsistent; otherwise null. */
62
69
  export function validateConfigureOpts(opts: ConfigureOpts): string | null {
63
70
  const flags = [opts.sync, opts.removeAll, opts.removeConfig, opts.status].filter(Boolean);
64
71
  if (flags.length > 1) {
@@ -73,6 +80,7 @@ export function validateConfigureOpts(opts: ConfigureOpts): string | null {
73
80
  return null;
74
81
  }
75
82
 
83
+ /** Adapts configure flags to internal install-plan option shape. */
76
84
  function configureToInstallOpts(opts: ConfigureOpts): InstallOpts {
77
85
  if (opts.status) {
78
86
  return { status: true, yes: opts.yes, dry: opts.dry, json: opts.json };
@@ -89,6 +97,7 @@ function configureToInstallOpts(opts: ConfigureOpts): InstallOpts {
89
97
  return { dry: opts.dry, json: opts.json };
90
98
  }
91
99
 
100
+ /** Installs a skill target and returns changed paths. */
92
101
  function runSkillAction(root: CliProgram, kind: InstallActionKind, opts: InstallOpts): string[] {
93
102
  const target = skillTargetFromActionKind(kind);
94
103
  if (!target) return [];
@@ -99,6 +108,7 @@ function runSkillAction(root: CliProgram, kind: InstallActionKind, opts: Install
99
108
  });
100
109
  }
101
110
 
111
+ /** Runs install or uninstall actions and collects changed paths. */
102
112
  function executePlan(
103
113
  root: CliProgram,
104
114
  actions: Array<InstallAction | UninstallAction>,
@@ -130,6 +140,7 @@ function executePlan(
130
140
  return changed;
131
141
  }
132
142
 
143
+ /** Builds plan context limited to a single artifact key. */
133
144
  function buildSingleTargetContext(
134
145
  root: CliProgram,
135
146
  paths: ReturnType<typeof resolveInstallPaths>,
@@ -147,6 +158,7 @@ function buildSingleTargetContext(
147
158
  };
148
159
  }
149
160
 
161
+ /** Resolves install or uninstall actions for one artifact target. */
150
162
  function actionsForTarget(
151
163
  root: CliProgram,
152
164
  paths: ReturnType<typeof resolveInstallPaths>,
@@ -164,6 +176,7 @@ function actionsForTarget(
164
176
  return target.planUninstall(ctx);
165
177
  }
166
178
 
179
+ /** Walks enabled targets with per-target prompts (TTY required). */
167
180
  async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): Promise<string[]> {
168
181
  if (!process.stdin.isTTY) {
169
182
  throw new Error("Interactive configure requires a TTY. Use flags such as --sync --yes.");
@@ -218,6 +231,7 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
218
231
  return changed;
219
232
  }
220
233
 
234
+ /** Runs sync, remove, or status modes without per-target prompts. */
221
235
  async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Promise<string[]> {
222
236
  const installOpts = configureToInstallOpts(opts);
223
237
  const paths = resolveInstallPaths(root);
@@ -1,6 +1,11 @@
1
+ /*
2
+ TTY prompts for per-target install, skip, or uninstall during interactive `configure`.
3
+ */
4
+
1
5
  import { readSync } from "node:fs";
2
6
  import type { CliInstallArtifactKey } from "../install/target-types.ts";
3
7
 
8
+ /** Human-readable labels for each install artifact key in interactive prompts. */
4
9
  const LABELS: Record<CliInstallArtifactKey, string> = {
5
10
  app: "App binary",
6
11
  cursorSkill: "Cursor skill",
@@ -18,10 +23,12 @@ const LABELS: Record<CliInstallArtifactKey, string> = {
18
23
  configure: "App config",
19
24
  };
20
25
 
26
+ /** Returns the prompt label for an install artifact key. */
21
27
  export function artifactPromptLabel(key: CliInstallArtifactKey): string {
22
28
  return LABELS[key];
23
29
  }
24
30
 
31
+ /** User choice from a per-target Y/n or y/N prompt. */
25
32
  export type TargetPromptAction = "install" | "skip" | "uninstall";
26
33
 
27
34
  /** Prompt per target: Y/n when not installed, y/N when installed. */
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for docs/api-guide module behavior.
3
+ */
4
+
1
5
  import { expect, test } from "bun:test";
2
6
  import { cliSchemaExport } from "../schema.ts";
3
7
  import type { CliProgram } from "../types.ts";
@@ -87,6 +91,7 @@ test("generateApiGuide mentions Homebrew upgrade", () => {
87
91
  expect(md).not.toContain("install --update");
88
92
  });
89
93
 
94
+ /** Tests that generateApiGuide resolves {argsbarg:program} in consumer notes. */
90
95
  test("generateApiGuide resolves {argsbarg:program} in consumer notes", () => {
91
96
  const fixture: CliProgram = {
92
97
  key: "myapp",
@@ -105,6 +110,7 @@ test("generateApiGuide resolves {argsbarg:program} in consumer notes", () => {
105
110
  expect(md).toContain("Invoke `myapp run`.");
106
111
  });
107
112
 
113
+ /** Tests that generateApiGuide and cliSchemaExport include leaf outputSchema. */
108
114
  test("generateApiGuide and cliSchemaExport include leaf outputSchema", () => {
109
115
  const fixture: CliProgram = {
110
116
  key: "myapp",
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for docs/docs module behavior.
3
+ */
4
+
1
5
  import { afterEach, beforeEach, expect, test } from "bun:test";
2
6
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
7
  import { tmpdir } from "node:os";
@@ -49,6 +53,7 @@ function docsFixture(mcp = true): CliProgram {
49
53
  };
50
54
  }
51
55
 
56
+ /** Docs reserved when enabled. */
52
57
  test("docs reserved when enabled", () => {
53
58
  const root: CliProgram = {
54
59
  ...docsFixture(),
@@ -63,6 +68,7 @@ test("docs reserved when enabled", () => {
63
68
  expect(() => cliValidateProgram(root)).toThrow(/Reserved command name: docs/);
64
69
  });
65
70
 
71
+ /** Docs rejects reserved topic keys. */
66
72
  test("docs rejects reserved topic keys", () => {
67
73
  const root = docsFixture();
68
74
  const docs = root.docs;
@@ -176,6 +182,7 @@ test("docs skill prints Cursor SKILL.md", async () => {
176
182
  expect(result.stdout).not.toContain("mcp.json");
177
183
  });
178
184
 
185
+ /** Docs skill help recommends configure. */
179
186
  test("docs skill help recommends configure", async () => {
180
187
  const presentation = cliPresentationRoot(docsFixture());
181
188
  const docsNode = presentation.commands.find((c) => c.key === "docs");
@@ -1,3 +1,7 @@
1
+ /*
2
+ Tests for docs/mcp-resources module behavior.
3
+ */
4
+
1
5
  import { expect, test } from "bun:test";
2
6
  import type { CliProgram } from "../types.ts";
3
7
  import {