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 +9 -2
- package/index.d.ts +10 -5
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +0 -11
- package/src/cli-tool/cli-smoke.test.ts +0 -3
- package/src/cli-tool/create.test.ts +0 -3
- package/src/cli-tool/full-example-capabilities.test.ts +0 -2
- package/src/config/context.test.ts +0 -1
- package/src/config/file.test.ts +0 -5
- package/src/config/resolve.test.ts +0 -7
- package/src/config/validate.test.ts +0 -6
- package/src/configure/configure.test.ts +0 -11
- package/src/docs/api-guide.test.ts +0 -4
- package/src/docs/docs.test.ts +0 -20
- package/src/docs/mcp-resources.test.ts +0 -6
- package/src/formats.test.ts +0 -5
- package/src/headless.test.ts +0 -4
- package/src/headless.ts +5 -4
- package/src/hidden-mcpb.test.ts +0 -12
- package/src/install/binary-placement.test.ts +0 -5
- package/src/install/gh-release-update.test.ts +0 -5
- package/src/install/install-validate.test.ts +0 -5
- package/src/install/mcp-opencode.test.ts +0 -4
- package/src/install/targets.test.ts +0 -10
- package/src/invoke.test.ts +0 -7
- package/src/mcp/claude.test.ts +0 -4
- package/src/mcp/env.test.ts +0 -5
- package/src/mcp/zip.test.ts +0 -1
- package/src/mcp.integration.test.ts +0 -22
- package/src/parse.test.ts +0 -28
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`
|
|
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.
|
|
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
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
@@ -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");
|
package/src/config/file.test.ts
CHANGED
|
@@ -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",
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -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(
|