argsbarg 5.0.2 → 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.
package/CHANGELOG.md CHANGED
@@ -7,11 +7,17 @@ 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
+
10
16
  ## [5.0.2] - 2026-07-04
11
17
 
12
18
  ### Changed
13
19
 
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.
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.
15
21
 
16
22
  ## [5.0.1] - 2026-07-04
17
23
 
@@ -573,7 +579,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
573
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`).
574
580
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
575
581
 
576
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.0.2...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
577
584
  [5.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.2
578
585
  [5.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.1
579
586
  [5.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.0
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.2",
3
+ "version": "5.0.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
@@ -37,7 +37,6 @@ const noMcp: CliProgram = {
37
37
 
38
38
  /** Tests for builtins help copy. */
39
39
  describe("builtins help copy", () => {
40
- /** Configure command includes Homebrew-oriented description. */
41
40
  test("configure command includes Homebrew-oriented description", () => {
42
41
  const configure = cliBuiltinConfigureCommand(fixture);
43
42
  expect(configure.description).toContain("agent skills");
@@ -51,7 +50,6 @@ describe("builtins help copy", () => {
51
50
  expect(yesOpt?.shortName).toBe("y");
52
51
  });
53
52
 
54
- /** Configure copy omits MCP when mcpServer unset. */
55
53
  test("configure copy omits MCP when mcpServer unset", () => {
56
54
  const caps = resolveCapabilities(noMcp);
57
55
  expect(configureCommandDescription(noMcp, caps)).toBe(
@@ -79,7 +77,6 @@ describe("builtins help copy", () => {
79
77
  expect(configureBuiltinOptions(withConfig).map((o) => o.name)).toContain("remove-config");
80
78
  });
81
79
 
82
- /** Configure -y parses as --yes. */
83
80
  test("configure -y parses as --yes", () => {
84
81
  const root = cliParseRoot(fixture);
85
82
  const pr = postParseValidate(root, parse(root, ["configure", "-y", "--sync"]));
@@ -90,7 +87,6 @@ describe("builtins help copy", () => {
90
87
  }
91
88
  });
92
89
 
93
- /** Mcp builtin description is user-facing. */
94
90
  test("mcp builtin description is user-facing", () => {
95
91
  const withDocs: CliProgram = {
96
92
  ...fixture,
@@ -105,7 +101,6 @@ describe("builtins help copy", () => {
105
101
 
106
102
  /** Tests for presentation root. */
107
103
  describe("presentation root", () => {
108
- /** Includes mcp and configure when enabled. */
109
104
  test("includes mcp and configure when enabled", () => {
110
105
  const root = cliPresentationRoot(fixture);
111
106
  const keys = root.commands?.map((c) => c.key) ?? [];
@@ -115,14 +110,12 @@ describe("presentation root", () => {
115
110
  expect(keys).not.toContain("install");
116
111
  });
117
112
 
118
- /** Omits configure when configure.enabled is false. */
119
113
  test("omits configure when configure.enabled is false", () => {
120
114
  const disabled: CliProgram = { ...fixture, configure: { enabled: false } };
121
115
  const root = cliPresentationRoot(disabled);
122
116
  expect(root.commands?.map((c) => c.key)).not.toContain("configure");
123
117
  });
124
118
 
125
- /** Includes version builtin. */
126
119
  test("includes version builtin", () => {
127
120
  const root = cliPresentationRoot(fixture);
128
121
  expect(root.commands?.map((c) => c.key)).toContain("version");
@@ -131,7 +124,6 @@ describe("presentation root", () => {
131
124
 
132
125
  /** Tests for completion emitters. */
133
126
  describe("completion emitters", () => {
134
- /** Tests that fish script references app key and subcommands. */
135
127
  test("fish script references app key and subcommands", () => {
136
128
  const schema = cliPresentationRoot(fixture);
137
129
  const fish = completionFishScript(schema);
@@ -140,7 +132,6 @@ describe("completion emitters", () => {
140
132
  expect(fish).toContain("configure");
141
133
  });
142
134
 
143
- /** Tests that bash script includes configure flags. */
144
135
  test("bash script includes configure flags", () => {
145
136
  const schema = cliPresentationRoot(fixture);
146
137
  const bash = completionBashScript(schema);
@@ -148,7 +139,6 @@ describe("completion emitters", () => {
148
139
  expect(bash).toContain("--sync");
149
140
  });
150
141
 
151
- /** Tests that zsh script registers compdef. */
152
142
  test("zsh script registers compdef", () => {
153
143
  const schema = cliPresentationRoot({
154
144
  key: "zapp",
@@ -179,7 +169,6 @@ describe("schema export builtins", () => {
179
169
  expect(builtins.map((b) => b.key)).toContain("configure");
180
170
  });
181
171
 
182
- /** ExportPresentationBuiltins omits hidden completion. */
183
172
  test("exportPresentationBuiltins omits hidden completion", () => {
184
173
  const builtins = exportPresentationBuiltins(fixture);
185
174
  expect(builtins.map((b) => b.key)).not.toContain("completion");
@@ -10,14 +10,12 @@ const main = join(import.meta.dir, "main.ts");
10
10
 
11
11
  /** Tests for argsbarg cli-tool. */
12
12
  describe("argsbarg cli-tool", () => {
13
- /** Tests that version subcommand prints version. */
14
13
  test("version subcommand prints version", () => {
15
14
  const proc = spawnSync("bun", [main, "version"], { encoding: "utf8" });
16
15
  expect(proc.status).toBe(0);
17
16
  expect(proc.stdout.trim().length).toBeGreaterThan(0);
18
17
  });
19
18
 
20
- /** Tests that help lists create and version only (no install, completion, mcp). */
21
19
  test("help lists create and version only (no install, completion, mcp)", () => {
22
20
  const proc = spawnSync("bun", [main, "--help"], { encoding: "utf8" });
23
21
  expect(proc.status).toBe(0);
@@ -28,7 +26,6 @@ describe("argsbarg cli-tool", () => {
28
26
  expect(proc.stdout).not.toContain("mcp");
29
27
  });
30
28
 
31
- /** Completion subcommand is disabled. */
32
29
  test("completion subcommand is disabled", () => {
33
30
  const proc = spawnSync("bun", [main, "completion", "bash"], { encoding: "utf8" });
34
31
  expect(proc.status).toBe(1);
@@ -44,7 +44,6 @@ describe("argsbarg create", () => {
44
44
  expect(out).not.toContain("{key}");
45
45
  });
46
46
 
47
- /** Tests that classNameFromKey. */
48
47
  test("classNameFromKey", () => {
49
48
  expect(classNameFromKey("sqsp-i18n")).toBe("SqspI18n");
50
49
  expect(classNameFromKey("at1")).toBe("At1");
@@ -67,7 +66,6 @@ describe("argsbarg create", () => {
67
66
  expect(opts.desc).toBe("At1 CLI");
68
67
  });
69
68
 
70
- /** ResolveCreateOptions requires release repo. */
71
69
  test("resolveCreateOptions requires release repo", () => {
72
70
  expect(() => resolveCreateOptions({ key: "at1" })).toThrow(/release repo/i);
73
71
  });
@@ -146,7 +144,6 @@ describe("argsbarg create", () => {
146
144
  }
147
145
  });
148
146
 
149
- /** Tests that --diff captures drift details. */
150
147
  test("--diff captures drift details", () => {
151
148
  const drifts = diffCreateDetails("/nonexistent", { key: "x", releaseRepo: "org/x" });
152
149
  expect(drifts.length).toBeGreaterThan(0);
@@ -59,13 +59,11 @@ describe("full-example template", () => {
59
59
  expect(programSource).toContain("appConfig:");
60
60
  });
61
61
 
62
- /** Status command defines outputSchema. */
63
62
  test("status command defines outputSchema", () => {
64
63
  const statusSource = readFileSync(join(exampleRoot, "src/commands/status/command.ts"), "utf8");
65
64
  expect(statusSource).toContain("outputSchema:");
66
65
  });
67
66
 
68
- /** ResolveCapabilities matches full sink shape. */
69
67
  test("resolveCapabilities matches full sink shape", () => {
70
68
  expect(resolveCapabilities(sinkProgram)).toEqual({
71
69
  completion: true,
@@ -72,7 +72,6 @@ describe("config/context", () => {
72
72
  expect(empty.dir).toBe(dirname(empty.path));
73
73
  });
74
74
 
75
- /** Tests that AppConfigSnapshot path uses OS default from program key. */
76
75
  test("AppConfigSnapshot path uses OS default from program key", () => {
77
76
  const ctx = createAppConfigSnapshot(program, {}, {});
78
77
  expect(ctx.path).toContain("ctx_test");
@@ -46,7 +46,6 @@ function withHome<T>(fn: (home: string) => T): T {
46
46
 
47
47
  /** Tests for config/file. */
48
48
  describe("config/file", () => {
49
- /** BuildProgramUserConfig from program.appConfig env entries. */
50
49
  test("buildProgramUserConfig from program.appConfig env entries", () => {
51
50
  const cfg = buildProgramUserConfig(program);
52
51
  expect(cfg?.api_token).toMatchObject({
@@ -57,7 +56,6 @@ describe("config/file", () => {
57
56
  expect(cfg?.port).toBeUndefined();
58
57
  });
59
58
 
60
- /** ResolveAppConfigPath uses config.json. */
61
59
  test("resolveAppConfigPath uses config.json", () => {
62
60
  withHome((home) => {
63
61
  expect(resolveAppConfigPath(program)).toBe(
@@ -100,7 +98,6 @@ describe("config/file", () => {
100
98
  }
101
99
  });
102
100
 
103
- /** Rejects unknown keys on read. */
104
101
  test("rejects unknown keys on read", () => {
105
102
  withHome(() => {
106
103
  const configPath = resolveAppConfigPath(program);
@@ -110,7 +107,6 @@ describe("config/file", () => {
110
107
  });
111
108
  });
112
109
 
113
- /** WriteAppConfigFile round-trip. */
114
110
  test("writeAppConfigFile round-trip", () => {
115
111
  withHome(() => {
116
112
  writeAppConfigFile(program, { apiToken: "saved" });
@@ -119,7 +115,6 @@ describe("config/file", () => {
119
115
  });
120
116
  });
121
117
 
122
- /** Tests that uninstallAppConfig removes config directory recursively. */
123
118
  test("uninstallAppConfig removes config directory recursively", () => {
124
119
  withHome(() => {
125
120
  writeAppConfigFile(program, { apiToken: "saved" });
@@ -31,7 +31,6 @@ const program: CliProgram = {
31
31
 
32
32
  /** Tests for config/resolve. */
33
33
  describe("config/resolve", () => {
34
- /** Tests that prefers env over file for mapped keys. */
35
34
  test("prefers env over file for mapped keys", () => {
36
35
  const prev = process.env.API_TOKEN;
37
36
  process.env.API_TOKEN = "from-env";
@@ -44,7 +43,6 @@ describe("config/resolve", () => {
44
43
  }
45
44
  });
46
45
 
47
- /** Tests that uses file when env empty. */
48
46
  test("uses file when env empty", () => {
49
47
  const prev = process.env.API_TOKEN;
50
48
  delete process.env.API_TOKEN;
@@ -56,7 +54,6 @@ describe("config/resolve", () => {
56
54
  }
57
55
  });
58
56
 
59
- /** Tests that empty string in env or file counts as missing. */
60
57
  test("empty string in env or file counts as missing", () => {
61
58
  const prev = process.env.API_TOKEN;
62
59
  process.env.API_TOKEN = "";
@@ -69,7 +66,6 @@ describe("config/resolve", () => {
69
66
  }
70
67
  });
71
68
 
72
- /** Applies jsonSchema default when file and env absent. */
73
69
  test("applies jsonSchema default when file and env absent", () => {
74
70
  const prev = process.env.API_TOKEN;
75
71
  delete process.env.API_TOKEN;
@@ -82,7 +78,6 @@ describe("config/resolve", () => {
82
78
  }
83
79
  });
84
80
 
85
- /** All-string mode uses entry.default. */
86
81
  test("all-string mode uses entry.default", () => {
87
82
  const stringProgram: CliProgram = {
88
83
  ...program,
@@ -96,7 +91,6 @@ describe("config/resolve", () => {
96
91
  expect(resolved.greeting).toBe("world");
97
92
  });
98
93
 
99
- /** Tests that prefers captured host env over file when process.env was exported from file. */
100
94
  test("prefers captured host env over file when process.env was exported from file", () => {
101
95
  const hostEnv = { API_TOKEN: "from-host" };
102
96
  process.env.API_TOKEN = "from-file-export";
@@ -108,7 +102,6 @@ describe("config/resolve", () => {
108
102
  }
109
103
  });
110
104
 
111
- /** ExportConfigToEnv does not overwrite host env. */
112
105
  test("exportConfigToEnv does not overwrite host env", () => {
113
106
  const prev = process.env.API_TOKEN;
114
107
  process.env.API_TOKEN = "from-host";
@@ -23,7 +23,6 @@ const rootSchema = {
23
23
 
24
24
  /** Tests for config/validate. */
25
25
  describe("config/validate", () => {
26
- /** Tests that accepts valid document. */
27
26
  test("accepts valid document", () => {
28
27
  const result = validateConfigDocument(
29
28
  { apiToken: "x", maxRetries: 3, prefs: { ttl: 3600 } },
@@ -33,14 +32,12 @@ describe("config/validate", () => {
33
32
  expect(result.errors).toEqual([]);
34
33
  });
35
34
 
36
- /** Rejects missing required property. */
37
35
  test("rejects missing required property", () => {
38
36
  const result = validateConfigDocument({ apiToken: "x" }, rootSchema);
39
37
  expect(result.valid).toBe(false);
40
38
  expect(result.errors.some((e) => e.includes("maxRetries"))).toBe(true);
41
39
  });
42
40
 
43
- /** Rejects unknown property when additionalProperties is false. */
44
41
  test("rejects unknown property when additionalProperties is false", () => {
45
42
  const result = validateConfigDocument(
46
43
  { apiToken: "x", maxRetries: 1, extra: true },
@@ -50,19 +47,16 @@ describe("config/validate", () => {
50
47
  expect(result.errors.some((e) => e.includes("extra"))).toBe(true);
51
48
  });
52
49
 
53
- /** Rejects type mismatch. */
54
50
  test("rejects type mismatch", () => {
55
51
  const result = validateConfigDocument({ apiToken: "x", maxRetries: "nope" }, rootSchema);
56
52
  expect(result.valid).toBe(false);
57
53
  });
58
54
 
59
- /** ParseConfigSetValue coerces number and boolean. */
60
55
  test("parseConfigSetValue coerces number and boolean", () => {
61
56
  expect(parseConfigSetValue("5", { type: "integer" }, rootSchema, false)).toBe(5);
62
57
  expect(parseConfigSetValue("true", { type: "boolean" }, rootSchema, false)).toBe(true);
63
58
  });
64
59
 
65
- /** ParseConfigSetValue requires --json for objects. */
66
60
  test("parseConfigSetValue requires --json for objects", () => {
67
61
  expect(() => parseConfigSetValue('{"ttl":1}', { type: "object" }, rootSchema, false)).toThrow(
68
62
  /--json/,
@@ -48,19 +48,16 @@ afterEach(() => {
48
48
 
49
49
  /** Tests for configure opts. */
50
50
  describe("configure opts", () => {
51
- /** Validate rejects multiple modes. */
52
51
  test("validate rejects multiple modes", () => {
53
52
  const opts = parseConfigureOpts({ sync: "1", status: "1" });
54
53
  expect(validateConfigureOpts(opts)).toContain("only one");
55
54
  });
56
55
 
57
- /** Sync requires --yes. */
58
56
  test("sync requires --yes", () => {
59
57
  const opts = parseConfigureOpts({ sync: "1" });
60
58
  expect(validateConfigureOpts(opts)).toContain("--yes");
61
59
  });
62
60
 
63
- /** Remove-all requires --yes. */
64
61
  test("remove-all requires --yes", () => {
65
62
  const opts = parseConfigureOpts({ "remove-all": "1" });
66
63
  expect(validateConfigureOpts(opts)).toContain("--yes");
@@ -69,14 +66,12 @@ describe("configure opts", () => {
69
66
 
70
67
  /** Tests for install paths. */
71
68
  describe("install paths", () => {
72
- /** ResolveInstallPaths includes skill and mcp paths. */
73
69
  test("resolveInstallPaths includes skill and mcp paths", () => {
74
70
  const paths = resolveInstallPaths(fixture);
75
71
  expect(paths.cursorSkillDir).toContain(".cursor/skills");
76
72
  expect(paths.cursorMcpPath).toContain("mcp.json");
77
73
  });
78
74
 
79
- /** Claude desktop mcp path on darwin. */
80
75
  test("claude desktop mcp path on darwin", () => {
81
76
  const prev = process.platform;
82
77
  Object.defineProperty(process, "platform", { value: "darwin" });
@@ -110,7 +105,6 @@ describe("detect installed", () => {
110
105
 
111
106
  /** Tests for sync plan. */
112
107
  describe("sync plan", () => {
113
- /** BuildUpdatePlan greenfield includes agent targets. */
114
108
  test("buildUpdatePlan greenfield includes agent targets", () => {
115
109
  const paths = resolveInstallPaths(fixture);
116
110
  const plan = buildUpdatePlan(fixture, paths, parseInstallOpts({ reinstall: "1", yes: "1" }));
@@ -118,7 +112,6 @@ describe("sync plan", () => {
118
112
  expect(plan.some((a) => a.kind === "app")).toBe(false);
119
113
  });
120
114
 
121
- /** BuildInstallPlan --all omits app self-install. */
122
115
  test("buildInstallPlan --all omits app self-install", () => {
123
116
  const paths = resolveInstallPaths(fixture);
124
117
  const plan = buildInstallPlan(fixture, paths, parseInstallOpts({ all: "1" }));
@@ -126,7 +119,6 @@ describe("sync plan", () => {
126
119
  expect(plan.some((a) => a.kind.endsWith("-mcp"))).toBe(true);
127
120
  });
128
121
 
129
- /** BuildInstallPlan respects configure.agentIntegration skill. */
130
122
  test("buildInstallPlan respects configure.agentIntegration skill", () => {
131
123
  const skillApp: CliProgram = {
132
124
  ...fixture,
@@ -142,7 +134,6 @@ describe("sync plan", () => {
142
134
 
143
135
  /** Tests for remove plan. */
144
136
  describe("remove plan", () => {
145
- /** BuildUninstallPlan --all with nothing installed is empty. */
146
137
  test("buildUninstallPlan --all with nothing installed is empty", () => {
147
138
  const paths = resolveInstallPaths(fixture);
148
139
  const plan = buildUninstallPlan(fixture, paths, parseInstallOpts({ uninstall: "1", all: "1" }));
@@ -152,7 +143,6 @@ describe("remove plan", () => {
152
143
 
153
144
  /** Tests for app config wizard. */
154
145
  describe("app config wizard", () => {
155
- /** Tests that appConfigHasEntries when entries exist. */
156
146
  test("appConfigHasEntries when entries exist", () => {
157
147
  expect(
158
148
  appConfigHasEntries({
@@ -162,7 +152,6 @@ describe("app config wizard", () => {
162
152
  ).toBe(true);
163
153
  });
164
154
 
165
- /** Tests that appConfigHasEntries false for empty entries or missing appConfig. */
166
155
  test("appConfigHasEntries false for empty entries or missing appConfig", () => {
167
156
  expect(appConfigHasEntries({ ...fixture, appConfig: { entries: {} } })).toBe(false);
168
157
  expect(appConfigHasEntries(fixture)).toBe(false);
@@ -49,7 +49,6 @@ const nestedFixture: CliProgram = {
49
49
  ],
50
50
  };
51
51
 
52
- /** Tests that generateApiGuideBody matches command section of full API guide. */
53
52
  test("generateApiGuideBody matches command section of full API guide", () => {
54
53
  const body = generateApiGuideBody(nestedFixture);
55
54
  const full = generateApiGuide(nestedFixture);
@@ -58,7 +57,6 @@ test("generateApiGuideBody matches command section of full API guide", () => {
58
57
  expect(body).not.toContain("CLI API reference");
59
58
  });
60
59
 
61
- /** Tests that generateApiGuide covers the same command keys as cliSchemaExport. */
62
60
  test("generateApiGuide covers the same command keys as cliSchemaExport", () => {
63
61
  const md = generateApiGuide(nestedFixture);
64
62
  const schema = cliSchemaExport(nestedFixture);
@@ -68,7 +66,6 @@ test("generateApiGuide covers the same command keys as cliSchemaExport", () => {
68
66
  expect(schema.commands?.map((c) => c.key)).toEqual(["stat"]);
69
67
  });
70
68
 
71
- /** Tests that generateApiGuide resolves program key in install notes. */
72
69
  test("generateApiGuide resolves program key in install notes", () => {
73
70
  const fixture: CliProgram = {
74
71
  key: "myapp",
@@ -82,7 +79,6 @@ test("generateApiGuide resolves program key in install notes", () => {
82
79
  expect(md).not.toContain("Upgrade to latest release");
83
80
  });
84
81
 
85
- /** Tests that generateApiGuide mentions Homebrew upgrade. */
86
82
  test("generateApiGuide mentions Homebrew upgrade", () => {
87
83
  const fixture: CliProgram = {
88
84
  key: "myapp",
@@ -83,26 +83,22 @@ test("docs rejects reserved topic keys", () => {
83
83
  expect(() => cliValidateProgram(root)).toThrow(/reserved/);
84
84
  });
85
85
 
86
- /** DocsEffectiveDefaultTopic uses first topic key. */
87
86
  test("docsEffectiveDefaultTopic uses first topic key", () => {
88
87
  expect(docsEffectiveDefaultTopic(docsFixture().docs!)).toBe("readme");
89
88
  });
90
89
 
91
- /** Bare docs prints first topic via Cli.invoke. */
92
90
  test("bare docs prints first topic via Cli.invoke", async () => {
93
91
  const result = await new Cli(docsFixture()).invoke(["docs"]);
94
92
  expect(result.exitCode).toBe(0);
95
93
  expect(result.stdout).toContain("Hello README");
96
94
  });
97
95
 
98
- /** Docs readme prints bundled text. */
99
96
  test("docs readme prints bundled text", async () => {
100
97
  const result = await new Cli(docsFixture()).invoke(["docs", "readme"]);
101
98
  expect(result.exitCode).toBe(0);
102
99
  expect(result.stdout).toContain("Hello README");
103
100
  });
104
101
 
105
- /** Docs defaultTopic override. */
106
102
  test("docs defaultTopic override", async () => {
107
103
  const root = docsFixture();
108
104
  root.docs!.defaultTopic = "arch";
@@ -110,7 +106,6 @@ test("docs defaultTopic override", async () => {
110
106
  expect(result.stdout).toContain("Architecture");
111
107
  });
112
108
 
113
- /** Docs mcp when MCP enabled. */
114
109
  test("docs mcp when MCP enabled", async () => {
115
110
  const result = await new Cli(docsFixture(true)).invoke(["docs", "mcp"]);
116
111
  expect(result.exitCode).toBe(0);
@@ -120,13 +115,11 @@ test("docs mcp when MCP enabled", async () => {
120
115
  expect(result.stdout).toContain("configure --sync --yes");
121
116
  });
122
117
 
123
- /** Docs rejects unknown subcommand. */
124
118
  test("docs rejects unknown subcommand", async () => {
125
119
  const result = await new Cli(docsFixture()).invoke(["docs", "all"]);
126
120
  expect(result.exitCode).not.toBe(0);
127
121
  });
128
122
 
129
- /** Docs mcp absent from router when MCP disabled. */
130
123
  test("docs mcp absent from router when MCP disabled", async () => {
131
124
  const root = docsFixture(false);
132
125
  const presentation = cliPresentationRoot(root);
@@ -139,7 +132,6 @@ test("docs mcp absent from router when MCP disabled", async () => {
139
132
  expect(result.exitCode).not.toBe(0);
140
133
  });
141
134
 
142
- /** Presentation includes docs subtree. */
143
135
  test("presentation includes docs subtree", () => {
144
136
  const presentation = cliPresentationRoot(docsFixture());
145
137
  const docsNode = presentation.commands.find((c) => c.key === "docs");
@@ -149,7 +141,6 @@ test("presentation includes docs subtree", () => {
149
141
  ).toBe(true);
150
142
  });
151
143
 
152
- /** Docs schema prints JSON. */
153
144
  test("docs schema prints JSON", async () => {
154
145
  const result = await new Cli(docsFixture()).invoke(["docs", "schema"]);
155
146
  expect(result.exitCode).toBe(0);
@@ -158,7 +149,6 @@ test("docs schema prints JSON", async () => {
158
149
  expect(schema.commands.some((c: { key: string }) => c.key === "run")).toBe(true);
159
150
  });
160
151
 
161
- /** Docs api prints markdown reference. */
162
152
  test("docs api prints markdown reference", async () => {
163
153
  const result = await new Cli(docsFixture()).invoke(["docs", "api"]);
164
154
  expect(result.exitCode).toBe(0);
@@ -168,7 +158,6 @@ test("docs api prints markdown reference", async () => {
168
158
  expect(result.stdout).toContain("myapp docs schema");
169
159
  });
170
160
 
171
- /** Tests that skipsRequiredAppConfigExit includes docs and config builtins. */
172
161
  test("skipsRequiredAppConfigExit includes docs and config builtins", () => {
173
162
  const program = {
174
163
  ...docsFixture(),
@@ -182,7 +171,6 @@ test("skipsRequiredAppConfigExit includes docs and config builtins", () => {
182
171
  expect(skipsRequiredAppConfigExit(["run"], caps)).toBe(false);
183
172
  });
184
173
 
185
- /** Docs skill prints Cursor SKILL.md. */
186
174
  test("docs skill prints Cursor SKILL.md", async () => {
187
175
  const result = await new Cli(docsFixture()).invoke(["docs", "skill"]);
188
176
  expect(result.exitCode).toBe(0);
@@ -209,7 +197,6 @@ test("docs skill help recommends configure", async () => {
209
197
  }
210
198
  });
211
199
 
212
- /** Presentation includes docs schema and skill. */
213
200
  test("presentation includes docs schema and skill", () => {
214
201
  const presentation = cliPresentationRoot(docsFixture());
215
202
  const docsNode = presentation.commands.find((c) => c.key === "docs");
@@ -221,7 +208,6 @@ test("presentation includes docs schema and skill", () => {
221
208
  }
222
209
  });
223
210
 
224
- /** Completions offer docs subcommands. */
225
211
  test("completions offer docs subcommands", () => {
226
212
  const bash = completionBashScript(cliPresentationRoot(docsFixture()));
227
213
  expect(bash).toContain("docs) echo");
@@ -231,7 +217,6 @@ test("completions offer docs subcommands", () => {
231
217
  expect(bash).toContain("skill) echo");
232
218
  });
233
219
 
234
- /** Tests that generateMcpGuide includes schema URI and configure sync. */
235
220
  test("generateMcpGuide includes schema URI and configure sync", () => {
236
221
  const guide = generateMcpGuide(docsFixture(true));
237
222
  expect(guide).toContain("myapp://schema");
@@ -245,7 +230,6 @@ test("generateMcpGuide includes schema URI and configure sync", () => {
245
230
  expect(guide).toContain("ChatGPT");
246
231
  });
247
232
 
248
- /** Docs --save writes topic file. */
249
233
  test("docs --save writes topic file", async () => {
250
234
  const result = await new Cli(docsFixture()).invoke(["docs", "readme", "--save"]);
251
235
  expect(result.exitCode).toBe(0);
@@ -255,7 +239,6 @@ test("docs --save writes topic file", async () => {
255
239
  expect(text).not.toContain("Generated by");
256
240
  });
257
241
 
258
- /** Docs api --save prepends generated hint. */
259
242
  test("docs api --save prepends generated hint", async () => {
260
243
  const result = await new Cli(docsFixture()).invoke(["docs", "api", "--save"]);
261
244
  expect(result.exitCode).toBe(0);
@@ -266,7 +249,6 @@ test("docs api --save prepends generated hint", async () => {
266
249
  expect(text).toContain("CLI API reference");
267
250
  });
268
251
 
269
- /** Docs skill --save keeps frontmatter first. */
270
252
  test("docs skill --save keeps frontmatter first", async () => {
271
253
  const result = await new Cli(docsFixture()).invoke(["docs", "skill", "--save"]);
272
254
  expect(result.exitCode).toBe(0);
@@ -277,7 +259,6 @@ test("docs skill --save keeps frontmatter first", async () => {
277
259
  expect(text.indexOf(hint)).toBeGreaterThan(text.indexOf("---\n", 4));
278
260
  });
279
261
 
280
- /** Docs schema --save writes JSON file. */
281
262
  test("docs schema --save writes JSON file", async () => {
282
263
  const result = await new Cli(docsFixture()).invoke(["docs", "schema", "--save"]);
283
264
  expect(result.exitCode).toBe(0);
@@ -288,7 +269,6 @@ test("docs schema --save writes JSON file", async () => {
288
269
  expect(schema.key).toBe("myapp");
289
270
  });
290
271
 
291
- /** Tests that saveDocsTopic returns relative path. */
292
272
  test("saveDocsTopic returns relative path", () => {
293
273
  const path = saveDocsTopic(docsFixture(), "api");
294
274
  expect(path).toBe("docs/api.md");
@@ -34,17 +34,14 @@ function fixture(opts?: { docs?: boolean; mcp?: boolean }): CliProgram {
34
34
  };
35
35
  }
36
36
 
37
- /** DefaultDocsTopicResourceUri. */
38
37
  test("defaultDocsTopicResourceUri", () => {
39
38
  expect(defaultDocsTopicResourceUri("my_app", "readme")).toBe("my_app://docs/readme");
40
39
  });
41
40
 
42
- /** ResolveDocsTopicResourceUri sanitizes program key. */
43
41
  test("resolveDocsTopicResourceUri sanitizes program key", () => {
44
42
  expect(resolveDocsTopicResourceUri(fixture(), "readme")).toBe("my_app://docs/readme");
45
43
  });
46
44
 
47
- /** DocsMcpResources when docs and MCP enabled. */
48
45
  test("docsMcpResources when docs and MCP enabled", () => {
49
46
  const resources = docsMcpResources(fixture());
50
47
  expect(resources.map((r) => r.uri)).toEqual(["my_app://docs/readme", "my_app://docs/arch"]);
@@ -54,17 +51,14 @@ test("docsMcpResources when docs and MCP enabled", () => {
54
51
  expect(resources[0]?.load()).toBe("# Readme\n");
55
52
  });
56
53
 
57
- /** DocsMcpResources empty when docs disabled. */
58
54
  test("docsMcpResources empty when docs disabled", () => {
59
55
  expect(docsMcpResources(fixture({ docs: false }))).toEqual([]);
60
56
  });
61
57
 
62
- /** DocsMcpResources empty when MCP disabled. */
63
58
  test("docsMcpResources empty when MCP disabled", () => {
64
59
  expect(docsMcpResources(fixture({ mcp: false }))).toEqual([]);
65
60
  });
66
61
 
67
- /** Tests that reservedDocsTopicResourceUris matches docsMcpResources URIs. */
68
62
  test("reservedDocsTopicResourceUris matches docsMcpResources URIs", () => {
69
63
  const program = fixture();
70
64
  expect(reservedDocsTopicResourceUris(program)).toEqual(