argsbarg 4.1.1 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -1
- package/README.md +5 -5
- package/docs/README.md +3 -3
- package/docs/ai-skills.md +9 -9
- package/docs/bundled-docs.md +4 -4
- package/docs/cli-program.md +8 -8
- package/docs/config-schema.md +2 -2
- package/docs/configure.md +177 -0
- package/docs/developing.md +1 -1
- package/docs/distribution-homebrew.md +12 -9
- package/docs/mcp.md +9 -9
- package/examples/full-example/README.md +3 -3
- package/examples/full-example/justfile +6 -6
- package/examples/full-example/scripts/formula-shared.ts +3 -2
- package/examples/full-example/src/program.ts +3 -3
- package/examples/nested.ts +1 -1
- package/index.d.ts +23 -16
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +105 -61
- package/src/builtins/completion-group.ts +4 -6
- package/src/builtins/config.test.ts +8 -0
- package/src/builtins/configure-copy.ts +86 -0
- package/src/builtins/configure.ts +70 -0
- package/src/builtins/dispatch.ts +13 -33
- package/src/builtins/index.ts +1 -1
- package/src/builtins/mcp.ts +2 -2
- package/src/builtins/registry.ts +6 -6
- package/src/capabilities.ts +22 -13
- package/src/cli-tool/cli-smoke.test.ts +21 -3
- package/src/cli-tool/create.test.ts +36 -1
- package/src/cli-tool/create.ts +26 -4
- package/src/cli-tool/full-example-capabilities.test.ts +10 -2
- package/src/cli-tool/main.ts +0 -0
- package/src/cli-tool/program.ts +20 -5
- package/src/cli-tool/run-create.ts +8 -19
- package/src/config/bootstrap.ts +11 -9
- package/src/config/context.test.ts +8 -0
- package/src/config/file.test.ts +13 -1
- package/src/config/resolve.test.ts +18 -0
- package/src/config/resolve.ts +2 -2
- package/src/config/validate.test.ts +11 -0
- package/src/config.integration.test.ts +5 -0
- package/src/configure/configure.test.ts +170 -0
- package/src/configure/index.ts +298 -0
- package/src/configure/prompt.ts +47 -0
- package/src/docs/api-guide.test.ts +10 -0
- package/src/docs/builtin.ts +3 -5
- package/src/docs/docs.test.ts +32 -5
- package/src/docs/mcp-guide.ts +4 -4
- package/src/docs/mcp-resources.test.ts +10 -0
- package/src/formats.test.ts +9 -0
- package/src/headless.test.ts +10 -0
- package/src/hidden-mcpb.test.ts +22 -0
- package/src/index.ts +2 -2
- package/src/install/binary-placement.test.ts +14 -0
- package/src/install/gh-release-update.test.ts +9 -0
- package/src/install/install-validate.test.ts +15 -5
- package/src/install/mcp-codex.test.ts +7 -0
- package/src/install/mcp-openclaw.test.ts +6 -0
- package/src/install/mcp-opencode.test.ts +10 -0
- package/src/install/opts.ts +17 -0
- package/src/install/status.test.ts +9 -0
- package/src/install/target-effective.ts +8 -8
- package/src/install/target-scope.ts +11 -8
- package/src/install/targets/configure.ts +1 -1
- package/src/install/targets.test.ts +23 -4
- package/src/invoke.test.ts +15 -1
- package/src/mcp/claude.test.ts +10 -0
- package/src/mcp/env.test.ts +12 -0
- package/src/mcp/tools.ts +1 -1
- package/src/mcp/zip.test.ts +5 -0
- package/src/mcp.integration.test.ts +43 -4
- package/src/parse.test.ts +82 -13
- package/src/schema.ts +1 -9
- package/src/skill/hint.ts +2 -2
- package/src/types.ts +22 -14
- package/src/validate.ts +17 -17
- package/docs/install.md +0 -206
- package/src/builtins/install.ts +0 -106
- package/src/builtins/uninstall.ts +0 -80
- package/src/install/index.ts +0 -409
- package/src/install/install.test.ts +0 -317
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
2
|
import { appConfigInstalled } from "../config/file.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CliConfigureTargets, CliProgram } from "../types.ts";
|
|
4
4
|
import type { InstallPaths } from "./paths.ts";
|
|
5
5
|
import {
|
|
6
6
|
type InstallPlanMode,
|
|
@@ -78,7 +78,7 @@ export function buildDetectedSnapshot(root: CliProgram, paths: InstallPaths): De
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function targetExplicitlyConfigured(
|
|
81
|
-
user:
|
|
81
|
+
user: CliConfigureTargets | undefined,
|
|
82
82
|
key: CliInstallArtifactKey,
|
|
83
83
|
): boolean {
|
|
84
84
|
return user?.[key] !== undefined;
|
|
@@ -89,7 +89,7 @@ function agentCategoryInScope(
|
|
|
89
89
|
categoryKeys: readonly CliInstallArtifactKey[],
|
|
90
90
|
category: "skill" | "mcp",
|
|
91
91
|
effective: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>,
|
|
92
|
-
targets:
|
|
92
|
+
targets: CliConfigureTargets | undefined,
|
|
93
93
|
root: CliProgram,
|
|
94
94
|
): boolean {
|
|
95
95
|
if (!categoryKeys.includes(key)) return false;
|
|
@@ -105,7 +105,7 @@ export function isArtifactInScope(
|
|
|
105
105
|
effective: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>,
|
|
106
106
|
mode: InstallPlanMode,
|
|
107
107
|
root: CliProgram,
|
|
108
|
-
targets?:
|
|
108
|
+
targets?: CliConfigureTargets,
|
|
109
109
|
): boolean {
|
|
110
110
|
if (mode === "uninstall-all") {
|
|
111
111
|
return true;
|
|
@@ -150,7 +150,7 @@ export function shouldIncludeArtifact(
|
|
|
150
150
|
detected?: Partial<Record<CliInstallArtifactKey, boolean>>,
|
|
151
151
|
): boolean {
|
|
152
152
|
const target = installTargetForKey(key);
|
|
153
|
-
const targets = root.
|
|
153
|
+
const targets = root.configure?.targets;
|
|
154
154
|
|
|
155
155
|
if (key !== "configure" && target && !target.isAvailable(root, paths)) {
|
|
156
156
|
return false;
|
|
@@ -196,7 +196,7 @@ export function buildTargetPlanContext(
|
|
|
196
196
|
opts: InstallOpts,
|
|
197
197
|
detected: DetectedSnapshot,
|
|
198
198
|
): TargetPlanContext {
|
|
199
|
-
const effective = resolveEffectiveInstallTargets(root.
|
|
199
|
+
const effective = resolveEffectiveInstallTargets(root.configure, root);
|
|
200
200
|
const mode = resolveInstallPlanMode(opts);
|
|
201
201
|
const scope = resolveInstallScope(opts);
|
|
202
202
|
const detPartial = Object.fromEntries(
|
|
@@ -236,8 +236,11 @@ export function resolveInstallTargetPreview(
|
|
|
236
236
|
program: CliProgram,
|
|
237
237
|
paths: InstallPaths,
|
|
238
238
|
): InstallTargetPreview {
|
|
239
|
-
const effective = resolveEffectiveInstallTargets(program.
|
|
240
|
-
const integration = resolveAgentIntegration(
|
|
239
|
+
const effective = resolveEffectiveInstallTargets(program.configure, program);
|
|
240
|
+
const integration = resolveAgentIntegration(
|
|
241
|
+
program.configure,
|
|
242
|
+
program.mcpServer?.enabled === true,
|
|
243
|
+
);
|
|
241
244
|
|
|
242
245
|
const keysForScope = (scope: InstallScope, mode: InstallPlanMode): CliInstallArtifactKey[] =>
|
|
243
246
|
INSTALL_ARTIFACT_KEYS.filter((key) =>
|
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
UninstallAction,
|
|
12
12
|
} from "../target-types.ts";
|
|
13
13
|
|
|
14
|
-
/** App config file (
|
|
14
|
+
/** App config file (interactive configure / wizard). */
|
|
15
15
|
class ConfigureInstallTarget extends InstallTarget {
|
|
16
16
|
readonly key = "configure" as const;
|
|
17
17
|
readonly actionKind = "configure" as const;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for install/targets module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import { describe, expect, test } from "bun:test";
|
|
2
6
|
import type { CliProgram } from "../types.ts";
|
|
3
7
|
import { normalizeInstallRawOpts } from "./normalize.ts";
|
|
@@ -5,11 +9,14 @@ import { normalizeUninstallRawOpts } from "./normalize-uninstall.ts";
|
|
|
5
9
|
import { resolveAgentIntegration, resolveEffectiveInstallTargets } from "./target-effective.ts";
|
|
6
10
|
import { isArtifactInScope } from "./target-scope.ts";
|
|
7
11
|
|
|
12
|
+
/** Tests for normalizeInstallRawOpts. */
|
|
8
13
|
describe("normalizeInstallRawOpts", () => {
|
|
14
|
+
/** Bare install sets all. */
|
|
9
15
|
test("bare install sets all", () => {
|
|
10
16
|
expect(normalizeInstallRawOpts({})).toEqual({ all: "1" });
|
|
11
17
|
});
|
|
12
18
|
|
|
19
|
+
/** Bare uninstall sets all. */
|
|
13
20
|
test("bare uninstall sets all", () => {
|
|
14
21
|
expect(normalizeUninstallRawOpts({})).toEqual({
|
|
15
22
|
uninstall: "1",
|
|
@@ -17,28 +24,35 @@ describe("normalizeInstallRawOpts", () => {
|
|
|
17
24
|
});
|
|
18
25
|
});
|
|
19
26
|
|
|
27
|
+
/** Configure-only install unchanged. */
|
|
20
28
|
test("configure-only install unchanged", () => {
|
|
21
29
|
expect(normalizeInstallRawOpts({ configure: "1" })).toEqual({ configure: "1" });
|
|
22
30
|
});
|
|
23
31
|
});
|
|
24
32
|
|
|
33
|
+
/** Tests for resolveAgentIntegration. */
|
|
25
34
|
describe("resolveAgentIntegration", () => {
|
|
35
|
+
/** Defaults to skill without MCP. */
|
|
26
36
|
test("defaults to skill without MCP", () => {
|
|
27
37
|
expect(resolveAgentIntegration(undefined, false)).toBe("skill");
|
|
28
38
|
});
|
|
29
39
|
|
|
40
|
+
/** Defaults to mcp when MCP enabled. */
|
|
30
41
|
test("defaults to mcp when MCP enabled", () => {
|
|
31
42
|
expect(resolveAgentIntegration(undefined, true)).toBe("mcp");
|
|
32
43
|
});
|
|
33
44
|
});
|
|
34
45
|
|
|
46
|
+
/** Tests for resolveEffectiveInstallTargets. */
|
|
35
47
|
describe("resolveEffectiveInstallTargets", () => {
|
|
48
|
+
/** Defaults app and configure not in --all. */
|
|
36
49
|
test("defaults app and configure not in --all", () => {
|
|
37
50
|
const t = resolveEffectiveInstallTargets(undefined);
|
|
38
51
|
expect(t.app.includedInAll).toBe(false);
|
|
39
52
|
expect(t.configure.includedInAll).toBe(false);
|
|
40
53
|
});
|
|
41
54
|
|
|
55
|
+
/** Skill mode includes skills in --all not MCP pairs. */
|
|
42
56
|
test("skill mode includes skills in --all not MCP pairs", () => {
|
|
43
57
|
const program: Pick<CliProgram, "mcpServer"> = {};
|
|
44
58
|
const t = resolveEffectiveInstallTargets(undefined, program);
|
|
@@ -46,6 +60,7 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
46
60
|
expect(t.cursorMcp.includedInAll).toBe(false);
|
|
47
61
|
});
|
|
48
62
|
|
|
63
|
+
/** Mcp mode includes MCP in --all not paired skills. */
|
|
49
64
|
test("mcp mode includes MCP in --all not paired skills", () => {
|
|
50
65
|
const program: Pick<CliProgram, "mcpServer"> = { mcpServer: { enabled: true } };
|
|
51
66
|
const t = resolveEffectiveInstallTargets(undefined, program);
|
|
@@ -54,6 +69,7 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
54
69
|
expect(t.claudeDesktopMcp.includedInAll).toBe(true);
|
|
55
70
|
});
|
|
56
71
|
|
|
72
|
+
/** Tests that both mode includes MCP and skills. */
|
|
57
73
|
test("both mode includes MCP and skills", () => {
|
|
58
74
|
const program: Pick<CliProgram, "mcpServer"> = { mcpServer: { enabled: true } };
|
|
59
75
|
const t = resolveEffectiveInstallTargets({ agentIntegration: "both" }, program);
|
|
@@ -61,6 +77,7 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
61
77
|
expect(t.cursorSkill.includedInAll).toBe(true);
|
|
62
78
|
});
|
|
63
79
|
|
|
80
|
+
/** Tests that per-key override disables one skill. */
|
|
64
81
|
test("per-key override disables one skill", () => {
|
|
65
82
|
const t = resolveEffectiveInstallTargets({
|
|
66
83
|
targets: { cursorSkill: false },
|
|
@@ -69,16 +86,17 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
69
86
|
expect(t.cursorSkill.enabled).toBe(false);
|
|
70
87
|
});
|
|
71
88
|
|
|
89
|
+
/** Scoped --mcp uses effective targets not all MCP hosts. */
|
|
72
90
|
test("scoped --mcp uses effective targets not all MCP hosts", () => {
|
|
73
91
|
const program: CliProgram = {
|
|
74
92
|
key: "app",
|
|
75
93
|
version: "1",
|
|
76
94
|
description: "x",
|
|
77
95
|
mcpServer: { enabled: true },
|
|
78
|
-
|
|
96
|
+
configure: { agentIntegration: "skill" },
|
|
79
97
|
handler: () => {},
|
|
80
98
|
};
|
|
81
|
-
const effective = resolveEffectiveInstallTargets(program.
|
|
99
|
+
const effective = resolveEffectiveInstallTargets(program.configure, program);
|
|
82
100
|
const scope = { mcp: true };
|
|
83
101
|
expect(isArtifactInScope("cursorMcp", scope, effective, "install-scoped", program)).toBe(false);
|
|
84
102
|
expect(
|
|
@@ -95,16 +113,17 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
95
113
|
).toBe(true);
|
|
96
114
|
});
|
|
97
115
|
|
|
116
|
+
/** Scoped --mcp --skill --configure includes skill and mcp not only configure. */
|
|
98
117
|
test("scoped --mcp --skill --configure includes skill and mcp not only configure", () => {
|
|
99
118
|
const program: CliProgram = {
|
|
100
119
|
key: "app",
|
|
101
120
|
version: "1",
|
|
102
121
|
description: "x",
|
|
103
122
|
mcpServer: { enabled: true },
|
|
104
|
-
|
|
123
|
+
configure: { agentIntegration: "both" },
|
|
105
124
|
handler: () => {},
|
|
106
125
|
};
|
|
107
|
-
const effective = resolveEffectiveInstallTargets(program.
|
|
126
|
+
const effective = resolveEffectiveInstallTargets(program.configure, program);
|
|
108
127
|
const scope = { mcp: true, skill: true, configure: true };
|
|
109
128
|
expect(isArtifactInScope("cursorSkill", scope, effective, "install-scoped", program)).toBe(
|
|
110
129
|
true,
|
package/src/invoke.test.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { CliLeaf } from "./types.ts";
|
|
|
12
12
|
import { isCliRouter } from "./types.ts";
|
|
13
13
|
import { cliValidateProgram } from "./validate.ts";
|
|
14
14
|
|
|
15
|
+
/** Tests that ctx.invocation is cli via Cli.run. */
|
|
15
16
|
test("ctx.invocation is cli via Cli.run", async () => {
|
|
16
17
|
const indexPath = join(import.meta.dir, "index.ts");
|
|
17
18
|
const { stdout } = await $`bun -e ${`
|
|
@@ -20,7 +21,7 @@ const program = {
|
|
|
20
21
|
key: "t",
|
|
21
22
|
description: "d",
|
|
22
23
|
version: "0.0.0",
|
|
23
|
-
|
|
24
|
+
configure: { enabled: false },
|
|
24
25
|
handler: (ctx) => console.log(ctx.invocation),
|
|
25
26
|
};
|
|
26
27
|
await new Cli(program).run([]);
|
|
@@ -28,6 +29,7 @@ await new Cli(program).run([]);
|
|
|
28
29
|
expect(stdout.toString().trim()).toBe("cli");
|
|
29
30
|
});
|
|
30
31
|
|
|
32
|
+
/** Tests that ctx.invocation is mcp via Cli.invoke. */
|
|
31
33
|
test("ctx.invocation is mcp via Cli.invoke", async () => {
|
|
32
34
|
let seen = "";
|
|
33
35
|
const root = testProgram({
|
|
@@ -43,6 +45,7 @@ test("ctx.invocation is mcp via Cli.invoke", async () => {
|
|
|
43
45
|
expect(seen).toBe("mcp");
|
|
44
46
|
});
|
|
45
47
|
|
|
48
|
+
/** Cli.invoke rejects invalid Enum value. */
|
|
46
49
|
test("Cli.invoke rejects invalid Enum value", async () => {
|
|
47
50
|
const root = testProgram({
|
|
48
51
|
key: "app",
|
|
@@ -64,6 +67,7 @@ test("Cli.invoke rejects invalid Enum value", async () => {
|
|
|
64
67
|
expect(result.errorMsg).toContain("not one of");
|
|
65
68
|
});
|
|
66
69
|
|
|
70
|
+
/** Cli.invoke accepts valid Enum value. */
|
|
67
71
|
test("Cli.invoke accepts valid Enum value", async () => {
|
|
68
72
|
const root = testProgram({
|
|
69
73
|
key: "app",
|
|
@@ -87,6 +91,7 @@ test("Cli.invoke accepts valid Enum value", async () => {
|
|
|
87
91
|
expect(result.stdout.trim()).toBe("dev");
|
|
88
92
|
});
|
|
89
93
|
|
|
94
|
+
/** Varargs trailing option after positionals via Cli.invoke. */
|
|
90
95
|
test("varargs trailing option after positionals via Cli.invoke", async () => {
|
|
91
96
|
const root = varargsReadFixture();
|
|
92
97
|
cliValidateProgram(root);
|
|
@@ -96,6 +101,7 @@ test("varargs trailing option after positionals via Cli.invoke", async () => {
|
|
|
96
101
|
expect(pr.opts.json).toBe("1");
|
|
97
102
|
});
|
|
98
103
|
|
|
104
|
+
/** Varargs option before positionals. */
|
|
99
105
|
test("varargs option before positionals", () => {
|
|
100
106
|
const root = varargsReadFixture();
|
|
101
107
|
cliValidateProgram(root);
|
|
@@ -105,6 +111,7 @@ test("varargs option before positionals", () => {
|
|
|
105
111
|
expect(pr.opts.json).toBe("1");
|
|
106
112
|
});
|
|
107
113
|
|
|
114
|
+
/** Varargs multiple files then trailing option. */
|
|
108
115
|
test("varargs multiple files then trailing option", () => {
|
|
109
116
|
const root = varargsReadFixture();
|
|
110
117
|
cliValidateProgram(root);
|
|
@@ -114,6 +121,7 @@ test("varargs multiple files then trailing option", () => {
|
|
|
114
121
|
expect(pr.opts.json).toBe("1");
|
|
115
122
|
});
|
|
116
123
|
|
|
124
|
+
/** Varargs double dash forces positional. */
|
|
117
125
|
test("varargs double dash forces positional", () => {
|
|
118
126
|
const root = varargsReadFixture();
|
|
119
127
|
cliValidateProgram(root);
|
|
@@ -123,6 +131,7 @@ test("varargs double dash forces positional", () => {
|
|
|
123
131
|
expect(pr.opts.json).toBeUndefined();
|
|
124
132
|
});
|
|
125
133
|
|
|
134
|
+
/** Varargs unknown flag errors. */
|
|
126
135
|
test("varargs unknown flag errors", async () => {
|
|
127
136
|
const root = varargsReadFixture();
|
|
128
137
|
cliValidateProgram(root);
|
|
@@ -131,6 +140,7 @@ test("varargs unknown flag errors", async () => {
|
|
|
131
140
|
expect(result.stderr).toContain("--unknown");
|
|
132
141
|
});
|
|
133
142
|
|
|
143
|
+
/** Varargs scoped help in tail. */
|
|
134
144
|
test("varargs scoped help in tail", () => {
|
|
135
145
|
const root = varargsReadFixture();
|
|
136
146
|
cliValidateProgram(root);
|
|
@@ -140,6 +150,7 @@ test("varargs scoped help in tail", () => {
|
|
|
140
150
|
expect(pr.helpExplicit).toBe(true);
|
|
141
151
|
});
|
|
142
152
|
|
|
153
|
+
/** Tests that ctx.positional returns single slot value. */
|
|
143
154
|
test("ctx.positional returns single slot value", async () => {
|
|
144
155
|
const root = testProgram({
|
|
145
156
|
key: "app",
|
|
@@ -161,6 +172,7 @@ test("ctx.positional returns single slot value", async () => {
|
|
|
161
172
|
expect(captured).toBe("./file");
|
|
162
173
|
});
|
|
163
174
|
|
|
175
|
+
/** Tests that ctx.positional returns varargs array. */
|
|
164
176
|
test("ctx.positional returns varargs array", async () => {
|
|
165
177
|
const root = varargsReadFixture();
|
|
166
178
|
let captured: string | string[] | undefined;
|
|
@@ -174,6 +186,7 @@ test("ctx.positional returns varargs array", async () => {
|
|
|
174
186
|
expect(captured).toEqual(["a.txt", "b.txt"]);
|
|
175
187
|
});
|
|
176
188
|
|
|
189
|
+
/** Tests that ctx.positional returns undefined for absent optional slot. */
|
|
177
190
|
test("ctx.positional returns undefined for absent optional slot", async () => {
|
|
178
191
|
const root = testProgram({
|
|
179
192
|
key: "app",
|
|
@@ -197,6 +210,7 @@ test("ctx.positional returns undefined for absent optional slot", async () => {
|
|
|
197
210
|
expect(captured).toBeUndefined();
|
|
198
211
|
});
|
|
199
212
|
|
|
213
|
+
/** Tests that ctx.positional varargs matches ctx.args. */
|
|
200
214
|
test("ctx.positional varargs matches ctx.args", async () => {
|
|
201
215
|
const root = varargsReadFixture();
|
|
202
216
|
let positional: string | string[] | undefined;
|
package/src/mcp/claude.test.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for mcp/claude module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import { describe, expect, test } from "bun:test";
|
|
2
6
|
import { execSync } from "node:child_process";
|
|
3
7
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -29,11 +33,14 @@ const configFixture: CliProgram = {
|
|
|
29
33
|
commands: [{ key: "run", description: "Run.", handler: () => {} }],
|
|
30
34
|
};
|
|
31
35
|
|
|
36
|
+
/** Tests for claude plugin. */
|
|
32
37
|
describe("claude plugin", () => {
|
|
38
|
+
/** PluginName is kebab-case. */
|
|
33
39
|
test("pluginName is kebab-case", () => {
|
|
34
40
|
expect(pluginName({ ...configFixture, key: "MyApp" })).toBe("my-app");
|
|
35
41
|
});
|
|
36
42
|
|
|
43
|
+
/** Tests that generatePluginManifest includes userConfig from program.appConfig. */
|
|
37
44
|
test("generatePluginManifest includes userConfig from program.appConfig", () => {
|
|
38
45
|
const manifest = generatePluginManifest(configFixture, "myapp");
|
|
39
46
|
expect(manifest.name).toBe("myapp");
|
|
@@ -43,6 +50,7 @@ describe("claude plugin", () => {
|
|
|
43
50
|
expect(userConfig.api_token?.sensitive).toBe(true);
|
|
44
51
|
});
|
|
45
52
|
|
|
53
|
+
/** Tests that generatePluginMcpJson uses CLAUDE_PLUGIN_ROOT and env mapping. */
|
|
46
54
|
test("generatePluginMcpJson uses CLAUDE_PLUGIN_ROOT and env mapping", () => {
|
|
47
55
|
const json = generatePluginMcpJson(configFixture, "myapp");
|
|
48
56
|
const entry = json.myapp as { command: string; args: string[]; env: Record<string, string> };
|
|
@@ -51,12 +59,14 @@ describe("claude plugin", () => {
|
|
|
51
59
|
expect(entry.env.API_TOKEN).toBe("${user_config.api_token}");
|
|
52
60
|
});
|
|
53
61
|
|
|
62
|
+
/** DefaultClaudePluginPaths. */
|
|
54
63
|
test("defaultClaudePluginPaths", () => {
|
|
55
64
|
const cwd = "/tmp/work";
|
|
56
65
|
const paths = defaultClaudePluginPaths(configFixture, cwd);
|
|
57
66
|
expect(paths.pluginZipPath).toBe(join(cwd, "dist", "claude-plugin", "myapp.zip"));
|
|
58
67
|
});
|
|
59
68
|
|
|
69
|
+
/** Tests that packClaudePlugin writes zip with MCP pointer skill only. */
|
|
60
70
|
test("packClaudePlugin writes zip with MCP pointer skill only", () => {
|
|
61
71
|
const work = mkdtempSync(join(tmpdir(), "claude-plugin-test-"));
|
|
62
72
|
try {
|
package/src/mcp/env.test.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for mcp/env module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
6
|
import { chmodSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
3
7
|
import { tmpdir } from "node:os";
|
|
@@ -6,7 +10,9 @@ import { applyShellEnv, bootstrapMcpEnv } from "./env.ts";
|
|
|
6
10
|
|
|
7
11
|
const TEST_VAR = "ARGS_BARG_SHELL_ENV_TEST";
|
|
8
12
|
|
|
13
|
+
/** Tests for mcp/env. */
|
|
9
14
|
describe("mcp/env", () => {
|
|
15
|
+
/** ApplyShellEnv merges PATH and fills missing vars. */
|
|
10
16
|
test("applyShellEnv merges PATH and fills missing vars", () => {
|
|
11
17
|
const prevPath = process.env.PATH;
|
|
12
18
|
const prevTest = process.env[TEST_VAR];
|
|
@@ -27,6 +33,7 @@ describe("mcp/env", () => {
|
|
|
27
33
|
}
|
|
28
34
|
});
|
|
29
35
|
|
|
36
|
+
/** ApplyShellEnv does not overwrite host vars except PATH merge. */
|
|
30
37
|
test("applyShellEnv does not overwrite host vars except PATH merge", () => {
|
|
31
38
|
const prev = process.env.HOME;
|
|
32
39
|
process.env.HOME = "/host/home";
|
|
@@ -40,6 +47,7 @@ describe("mcp/env", () => {
|
|
|
40
47
|
});
|
|
41
48
|
});
|
|
42
49
|
|
|
50
|
+
/** Tests for bootstrapMcpEnv. */
|
|
43
51
|
describe("bootstrapMcpEnv", () => {
|
|
44
52
|
let fakeShell: string;
|
|
45
53
|
let prevShell: string | undefined;
|
|
@@ -70,21 +78,25 @@ fi
|
|
|
70
78
|
else process.env[TEST_VAR] = prevTest;
|
|
71
79
|
});
|
|
72
80
|
|
|
81
|
+
/** Defaults on when shellEnv is undefined. */
|
|
73
82
|
test("defaults on when shellEnv is undefined", () => {
|
|
74
83
|
bootstrapMcpEnv({});
|
|
75
84
|
expect(process.env[TEST_VAR]).toBe("from_fake_shell");
|
|
76
85
|
});
|
|
77
86
|
|
|
87
|
+
/** Runs when shellEnv is true. */
|
|
78
88
|
test("runs when shellEnv is true", () => {
|
|
79
89
|
bootstrapMcpEnv({ shellEnv: true });
|
|
80
90
|
expect(process.env[TEST_VAR]).toBe("from_fake_shell");
|
|
81
91
|
});
|
|
82
92
|
|
|
93
|
+
/** Tests that uses explicit shell path when shellEnv is a string. */
|
|
83
94
|
test("uses explicit shell path when shellEnv is a string", () => {
|
|
84
95
|
bootstrapMcpEnv({ shellEnv: fakeShell });
|
|
85
96
|
expect(process.env[TEST_VAR]).toBe("from_fake_shell");
|
|
86
97
|
});
|
|
87
98
|
|
|
99
|
+
/** Tests that skips capture when shellEnv is false. */
|
|
88
100
|
test("skips capture when shellEnv is false", () => {
|
|
89
101
|
bootstrapMcpEnv({ shellEnv: false });
|
|
90
102
|
expect(process.env[TEST_VAR]).toBeUndefined();
|
package/src/mcp/tools.ts
CHANGED
package/src/mcp/zip.test.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Tests for mcp/zip module behavior.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import { expect, test } from "bun:test";
|
|
2
6
|
import { execSync } from "node:child_process";
|
|
3
7
|
import { mkdtempSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -5,6 +9,7 @@ import { tmpdir } from "node:os";
|
|
|
5
9
|
import { join } from "node:path";
|
|
6
10
|
import { zipStore } from "./zip.ts";
|
|
7
11
|
|
|
12
|
+
/** ZipStore preserves unix executable mode on extract. */
|
|
8
13
|
test("zipStore preserves unix executable mode on extract", () => {
|
|
9
14
|
const work = mkdtempSync(join(tmpdir(), "zip-exec-"));
|
|
10
15
|
const data = Buffer.from("#!/bin/sh\necho hi\n");
|