argsbarg 5.1.8 → 5.1.10
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 +29 -1
- package/docs/cli-program.md +3 -1
- package/docs/config-schema.md +5 -3
- package/docs/configure.md +13 -7
- package/docs/distribution-homebrew.md +17 -3
- package/examples/full-example/justfile +6 -5
- package/examples/full-example/scripts/formula-shared.test.ts +68 -0
- package/examples/full-example/scripts/formula-shared.ts +39 -1
- package/examples/full-example/scripts/release.ts +212 -0
- package/index.d.ts +10 -1
- package/package.json +2 -1
- package/src/builtins/builtins.test.ts +1 -3
- package/src/builtins/completion-bash.ts +6 -27
- package/src/builtins/completion-group.ts +1 -2
- package/src/builtins/completion-simulate-shared.ts +2 -10
- package/src/builtins/completion-zsh.ts +7 -33
- package/src/builtins/config.test.ts +28 -0
- package/src/builtins/config.ts +40 -10
- package/src/builtins/configure-copy.ts +3 -6
- package/src/builtins/dispatch.ts +2 -10
- package/src/builtins/export.ts +1 -3
- package/src/builtins/presentation.ts +2 -7
- package/src/builtins/registry.ts +1 -5
- package/src/cli-tool/create.test.ts +3 -6
- package/src/cli-tool/create.ts +5 -22
- package/src/cli-tool/post-create.ts +1 -3
- package/src/cli-tool/program.ts +1 -2
- package/src/cli-tool/run-create.ts +2 -8
- package/src/cli.ts +6 -27
- package/src/config/bindings.test.ts +72 -0
- package/src/config/bindings.ts +115 -0
- package/src/config/bootstrap.ts +80 -65
- package/src/config/context.test.ts +45 -0
- package/src/config/context.ts +74 -14
- package/src/config/entry.ts +1 -3
- package/src/config/file.test.ts +46 -3
- package/src/config/file.ts +59 -19
- package/src/config/resolve.ts +1 -4
- package/src/config/schema.ts +1 -3
- package/src/config/validate.test.ts +3 -11
- package/src/config/validate.ts +30 -30
- package/src/config.integration.test.ts +1 -5
- package/src/configure/configure.test.ts +19 -5
- package/src/configure/index.ts +17 -15
- package/src/docs/api-guide.ts +1 -6
- package/src/docs/builtin.ts +1 -5
- package/src/docs/docs.test.ts +2 -6
- package/src/docs/mcp-guide.ts +3 -13
- package/src/docs/mcp-resources.test.ts +1 -3
- package/src/docs/mcp-resources.ts +1 -6
- package/src/docs/save.ts +2 -10
- package/src/formats.test.ts +1 -7
- package/src/formats.ts +1 -5
- package/src/headless.test.ts +5 -17
- package/src/help.ts +9 -51
- package/src/hidden-mcpb.test.ts +3 -12
- package/src/hidden.ts +1 -3
- package/src/install/binary-placement.test.ts +2 -6
- package/src/install/binary-placement.ts +1 -4
- package/src/install/gh-release-update.ts +5 -22
- package/src/install/mcp-codex.ts +1 -2
- package/src/install/mcp-config.ts +2 -12
- package/src/install/mcp-opencode.test.ts +1 -3
- package/src/install/mcp-opencode.ts +3 -15
- package/src/install/plan.ts +2 -10
- package/src/install/status.ts +3 -5
- package/src/install/target-base.ts +2 -11
- package/src/install/target-detect.ts +1 -5
- package/src/install/target-effective.ts +3 -13
- package/src/install/target-mcp-cli.ts +4 -26
- package/src/install/target-mcp-json.ts +3 -14
- package/src/install/target-plan-build.ts +1 -5
- package/src/install/target-registry.ts +13 -21
- package/src/install/target-scope.ts +8 -25
- package/src/install/target-types.ts +1 -6
- package/src/install/targets/app.ts +2 -2
- package/src/install/targets/opencode-mcp.ts +1 -6
- package/src/install/targets.test.ts +3 -10
- package/src/install/uninstall.ts +2 -9
- package/src/invoke.test.ts +1 -3
- package/src/mcp/bundle.ts +4 -19
- package/src/mcp/claude.test.ts +3 -3
- package/src/mcp/claude.ts +4 -17
- package/src/mcp/env.ts +1 -3
- package/src/mcp/server.ts +3 -12
- package/src/mcp/tools.ts +2 -11
- package/src/mcp.integration.test.ts +4 -10
- package/src/parse.test.ts +9 -34
- package/src/parse.ts +10 -44
- package/src/schema.ts +2 -10
- package/src/skill/hint.ts +1 -5
- package/src/skill/install.ts +1 -5
- package/src/test-fixtures.ts +1 -3
- package/src/validate.ts +29 -90
|
@@ -7,28 +7,23 @@ export { INSTALL_TARGETS } from "./targets/index.ts";
|
|
|
7
7
|
|
|
8
8
|
export const INSTALL_ARTIFACT_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.map((t) => t.key);
|
|
9
9
|
|
|
10
|
-
export const SKILL_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter(
|
|
11
|
-
(t) => t.
|
|
12
|
-
)
|
|
10
|
+
export const SKILL_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter((t) => t.category === "skill").map(
|
|
11
|
+
(t) => t.key,
|
|
12
|
+
);
|
|
13
13
|
|
|
14
|
-
export const MCP_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter(
|
|
15
|
-
(t) => t.category === "mcp",
|
|
16
|
-
).map((t) => t.key);
|
|
14
|
+
export const MCP_KEYS: CliInstallArtifactKey[] = INSTALL_TARGETS.filter((t) => t.category === "mcp").map((t) => t.key);
|
|
17
15
|
|
|
18
16
|
/** Maps plan action kinds to install.targets artifact keys. */
|
|
19
|
-
export const ACTION_KIND_TO_ARTIFACT: Record<InstallActionKind, CliInstallArtifactKey> =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
CliInstallArtifactKey
|
|
23
|
-
>;
|
|
17
|
+
export const ACTION_KIND_TO_ARTIFACT: Record<InstallActionKind, CliInstallArtifactKey> = Object.fromEntries(
|
|
18
|
+
INSTALL_TARGETS.map((t) => [t.actionKind, t.key]),
|
|
19
|
+
) as Record<InstallActionKind, CliInstallArtifactKey>;
|
|
24
20
|
|
|
25
21
|
/** Hosts with both MCP config and shell skill install targets. */
|
|
26
|
-
export const AGENT_PAIRS: [CliInstallArtifactKey, CliInstallArtifactKey][] =
|
|
27
|
-
|
|
28
|
-
t.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
22
|
+
export const AGENT_PAIRS: [CliInstallArtifactKey, CliInstallArtifactKey][] = INSTALL_TARGETS.flatMap((t) =>
|
|
23
|
+
t.category === "mcp" && t.pairedKey !== undefined
|
|
24
|
+
? [[t.key, t.pairedKey] as [CliInstallArtifactKey, CliInstallArtifactKey]]
|
|
25
|
+
: [],
|
|
26
|
+
);
|
|
32
27
|
|
|
33
28
|
const targetByKey = new Map(INSTALL_TARGETS.map((t) => [t.key, t]));
|
|
34
29
|
|
|
@@ -47,10 +42,7 @@ export function isMcpArtifactKey(key: CliInstallArtifactKey): boolean {
|
|
|
47
42
|
* Defaults: artifactDefaults in target-effective.ts.
|
|
48
43
|
* Explicit spec rejection: validateConfigureConfig in validate.ts.
|
|
49
44
|
*/
|
|
50
|
-
export function mcpServerRequiredForArtifact(
|
|
51
|
-
key: CliInstallArtifactKey,
|
|
52
|
-
mcpServerEnabled: boolean,
|
|
53
|
-
): boolean {
|
|
45
|
+
export function mcpServerRequiredForArtifact(key: CliInstallArtifactKey, mcpServerEnabled: boolean): boolean {
|
|
54
46
|
return !isMcpArtifactKey(key) || mcpServerEnabled;
|
|
55
47
|
}
|
|
56
48
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { appConfigFileExists } from "../config/file.ts";
|
|
3
3
|
import type { CliConfigureTargets, CliProgram } from "../types.ts";
|
|
4
4
|
import type { InstallPaths } from "./paths.ts";
|
|
5
5
|
import {
|
|
@@ -46,10 +46,7 @@ function emptyInstalledArtifacts(): InstalledArtifacts {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** Detects which install artifacts are currently present. */
|
|
49
|
-
export function detectInstalledArtifacts(
|
|
50
|
-
paths: InstallPaths,
|
|
51
|
-
root: CliProgram,
|
|
52
|
-
): InstalledArtifacts {
|
|
49
|
+
export function detectInstalledArtifacts(paths: InstallPaths, root: CliProgram): InstalledArtifacts {
|
|
53
50
|
const out = emptyInstalledArtifacts();
|
|
54
51
|
for (const target of INSTALL_TARGETS) {
|
|
55
52
|
target.applyDetected(paths, root, out);
|
|
@@ -73,14 +70,11 @@ export function buildDetectedSnapshot(root: CliProgram, paths: InstallPaths): De
|
|
|
73
70
|
const base = detectInstalledArtifacts(paths, root);
|
|
74
71
|
return {
|
|
75
72
|
...base,
|
|
76
|
-
appConfig:
|
|
73
|
+
appConfig: appConfigFileExists(root),
|
|
77
74
|
};
|
|
78
75
|
}
|
|
79
76
|
|
|
80
|
-
function targetExplicitlyConfigured(
|
|
81
|
-
user: CliConfigureTargets | undefined,
|
|
82
|
-
key: CliInstallArtifactKey,
|
|
83
|
-
): boolean {
|
|
77
|
+
function targetExplicitlyConfigured(user: CliConfigureTargets | undefined, key: CliInstallArtifactKey): boolean {
|
|
84
78
|
return user?.[key] !== undefined;
|
|
85
79
|
}
|
|
86
80
|
|
|
@@ -220,10 +214,7 @@ export function buildTargetPlanContext(
|
|
|
220
214
|
}
|
|
221
215
|
|
|
222
216
|
/** Maps detected snapshot to artifact key (for tests and legacy callers). */
|
|
223
|
-
export function detectedForArtifact(
|
|
224
|
-
key: CliInstallArtifactKey,
|
|
225
|
-
detected: DetectedSnapshot,
|
|
226
|
-
): boolean {
|
|
217
|
+
export function detectedForArtifact(key: CliInstallArtifactKey, detected: DetectedSnapshot): boolean {
|
|
227
218
|
const target = installTargetForKey(key);
|
|
228
219
|
if (key === "configure") {
|
|
229
220
|
return detected.appConfig ?? false;
|
|
@@ -232,20 +223,12 @@ export function detectedForArtifact(
|
|
|
232
223
|
}
|
|
233
224
|
|
|
234
225
|
/** Resolved artifact keys for `--all`, `--mcp`, and `--skill` (availability-gated). */
|
|
235
|
-
export function resolveInstallTargetPreview(
|
|
236
|
-
program: CliProgram,
|
|
237
|
-
paths: InstallPaths,
|
|
238
|
-
): InstallTargetPreview {
|
|
226
|
+
export function resolveInstallTargetPreview(program: CliProgram, paths: InstallPaths): InstallTargetPreview {
|
|
239
227
|
const effective = resolveEffectiveInstallTargets(program.configure, program);
|
|
240
|
-
const integration = resolveAgentIntegration(
|
|
241
|
-
program.configure,
|
|
242
|
-
program.mcpServer?.enabled === true,
|
|
243
|
-
);
|
|
228
|
+
const integration = resolveAgentIntegration(program.configure, program.mcpServer?.enabled === true);
|
|
244
229
|
|
|
245
230
|
const keysForScope = (scope: InstallScope, mode: InstallPlanMode): CliInstallArtifactKey[] =>
|
|
246
|
-
INSTALL_ARTIFACT_KEYS.filter((key) =>
|
|
247
|
-
shouldIncludeArtifact(key, program, paths, scope, mode, effective),
|
|
248
|
-
);
|
|
231
|
+
INSTALL_ARTIFACT_KEYS.filter((key) => shouldIncludeArtifact(key, program, paths, scope, mode, effective));
|
|
249
232
|
|
|
250
233
|
return {
|
|
251
234
|
agentIntegration: integration,
|
|
@@ -3,12 +3,7 @@ import type { InstallPaths } from "./paths.ts";
|
|
|
3
3
|
|
|
4
4
|
export type { InstallTargetSpec, ResolvedInstallTarget } from "../types.ts";
|
|
5
5
|
|
|
6
|
-
export type InstallPlanMode =
|
|
7
|
-
| "install-all"
|
|
8
|
-
| "install-scoped"
|
|
9
|
-
| "uninstall-all"
|
|
10
|
-
| "uninstall-scoped"
|
|
11
|
-
| "refresh";
|
|
6
|
+
export type InstallPlanMode = "install-all" | "install-scoped" | "uninstall-all" | "uninstall-scoped" | "refresh";
|
|
12
7
|
|
|
13
8
|
export interface InstallScope {
|
|
14
9
|
all?: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CliProgram } from "../../types.ts";
|
|
2
2
|
import { isExternallyManagedBinary } from "../binary-placement.ts";
|
|
3
|
-
import {
|
|
3
|
+
import type { InstallPaths } from "../paths.ts";
|
|
4
4
|
import { InstallTarget } from "../target-base.ts";
|
|
5
5
|
import type {
|
|
6
6
|
DetectedSnapshot,
|
|
@@ -25,7 +25,7 @@ class AppInstallTarget extends InstallTarget {
|
|
|
25
25
|
return true;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
isDetected(
|
|
28
|
+
isDetected(_paths: InstallPaths, root: CliProgram): boolean {
|
|
29
29
|
return isExternallyManagedBinary(root.key) || false;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -57,12 +57,7 @@ class OpenCodeMcpInstallTarget extends InstallTarget {
|
|
|
57
57
|
|
|
58
58
|
preflight(ctx: TargetPlanContext): string | null {
|
|
59
59
|
const entry = expectedOpenCodeMcpEntry(ctx.root);
|
|
60
|
-
return checkOpenCodeMcpConflict(
|
|
61
|
-
ctx.paths.opencodeMcpPath,
|
|
62
|
-
ctx.paths.mcpName,
|
|
63
|
-
entry,
|
|
64
|
-
!!ctx.opts.yes,
|
|
65
|
-
);
|
|
60
|
+
return checkOpenCodeMcpConflict(ctx.paths.opencodeMcpPath, ctx.paths.mcpName, entry, !!ctx.opts.yes);
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
protected buildInstallActions(ctx: TargetPlanContext): InstallAction[] {
|
|
@@ -95,10 +95,7 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
95
95
|
isArtifactInScope(
|
|
96
96
|
"cursorMcp",
|
|
97
97
|
scope,
|
|
98
|
-
resolveEffectiveInstallTargets(
|
|
99
|
-
{ targets: { cursorMcp: true }, agentIntegration: "both" },
|
|
100
|
-
program,
|
|
101
|
-
),
|
|
98
|
+
resolveEffectiveInstallTargets({ targets: { cursorMcp: true }, agentIntegration: "both" }, program),
|
|
102
99
|
"install-scoped",
|
|
103
100
|
program,
|
|
104
101
|
),
|
|
@@ -117,13 +114,9 @@ describe("resolveEffectiveInstallTargets", () => {
|
|
|
117
114
|
};
|
|
118
115
|
const effective = resolveEffectiveInstallTargets(program.configure, program);
|
|
119
116
|
const scope = { mcp: true, skill: true, configure: true };
|
|
120
|
-
expect(isArtifactInScope("cursorSkill", scope, effective, "install-scoped", program)).toBe(
|
|
121
|
-
true,
|
|
122
|
-
);
|
|
117
|
+
expect(isArtifactInScope("cursorSkill", scope, effective, "install-scoped", program)).toBe(true);
|
|
123
118
|
expect(isArtifactInScope("cursorMcp", scope, effective, "install-scoped", program)).toBe(true);
|
|
124
119
|
expect(isArtifactInScope("configure", scope, effective, "install-scoped", program)).toBe(true);
|
|
125
|
-
expect(isArtifactInScope("claudeSkill", scope, effective, "install-scoped", program)).toBe(
|
|
126
|
-
true,
|
|
127
|
-
);
|
|
120
|
+
expect(isArtifactInScope("claudeSkill", scope, effective, "install-scoped", program)).toBe(true);
|
|
128
121
|
});
|
|
129
122
|
});
|
package/src/install/uninstall.ts
CHANGED
|
@@ -8,11 +8,7 @@ import type { InstallOpts, UninstallAction } from "./target-types.ts";
|
|
|
8
8
|
export type { InstallActionKind, InstallOpts, UninstallAction } from "./target-types.ts";
|
|
9
9
|
|
|
10
10
|
/** Builds uninstall actions for scoped targets or --all (ignores install.targets on --all). */
|
|
11
|
-
export function buildUninstallPlan(
|
|
12
|
-
root: CliProgram,
|
|
13
|
-
paths: InstallPaths,
|
|
14
|
-
opts: InstallOpts,
|
|
15
|
-
): UninstallAction[] {
|
|
11
|
+
export function buildUninstallPlan(root: CliProgram, paths: InstallPaths, opts: InstallOpts): UninstallAction[] {
|
|
16
12
|
return buildUninstallPlanFromTargets(root, paths, opts);
|
|
17
13
|
}
|
|
18
14
|
|
|
@@ -24,10 +20,7 @@ export function uninstallSkillDir(dir: string, dry: boolean): string[] {
|
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
/** Skill directory for uninstall action summary prefix. */
|
|
27
|
-
export function skillDirFromUninstallSummary(
|
|
28
|
-
summary: string,
|
|
29
|
-
paths: InstallPaths,
|
|
30
|
-
): string | undefined {
|
|
23
|
+
export function skillDirFromUninstallSummary(summary: string, paths: InstallPaths): string | undefined {
|
|
31
24
|
const target = skillTargetForUninstallSummary(summary);
|
|
32
25
|
return target?.skillDir(paths);
|
|
33
26
|
}
|
package/src/invoke.test.ts
CHANGED
|
@@ -188,9 +188,7 @@ test("ctx.positional returns undefined for absent optional slot", async () => {
|
|
|
188
188
|
{
|
|
189
189
|
key: "x",
|
|
190
190
|
description: "",
|
|
191
|
-
positionals: [
|
|
192
|
-
{ name: "opt", description: "", kind: CliOptionKind.String, argMin: 0, argMax: 1 },
|
|
193
|
-
],
|
|
191
|
+
positionals: [{ name: "opt", description: "", kind: CliOptionKind.String, argMin: 0, argMax: 1 }],
|
|
194
192
|
handler: (ctx: CliContext) => {
|
|
195
193
|
captured = ctx.positional("opt");
|
|
196
194
|
},
|
package/src/mcp/bundle.ts
CHANGED
|
@@ -3,15 +3,7 @@ Packs a CLI program into an MCP Bundle (`.mcpb`) for Claude Desktop.
|
|
|
3
3
|
Expects `dist/<program.key>` as the compiled binary input.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
cpSync,
|
|
8
|
-
existsSync,
|
|
9
|
-
mkdirSync,
|
|
10
|
-
mkdtempSync,
|
|
11
|
-
readFileSync,
|
|
12
|
-
rmSync,
|
|
13
|
-
writeFileSync,
|
|
14
|
-
} from "node:fs";
|
|
6
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
15
7
|
import { tmpdir } from "node:os";
|
|
16
8
|
import { basename, join, resolve } from "node:path";
|
|
17
9
|
import { buildProgramUserConfig } from "../config/manifest.ts";
|
|
@@ -56,10 +48,7 @@ function defaultAuthor(bundle?: CliMcpBundleConfig): {
|
|
|
56
48
|
}
|
|
57
49
|
|
|
58
50
|
/** Generates MCPB `manifest.json` object from program schema and MCP tools. */
|
|
59
|
-
export function generateMcpManifest(
|
|
60
|
-
program: CliProgram,
|
|
61
|
-
binaryName: string,
|
|
62
|
-
): Record<string, unknown> {
|
|
51
|
+
export function generateMcpManifest(program: CliProgram, binaryName: string): Record<string, unknown> {
|
|
63
52
|
const bundle = program.mcpServer?.bundle;
|
|
64
53
|
const tools = collectMcpTools(program).map((t) => ({
|
|
65
54
|
name: t.name,
|
|
@@ -123,9 +112,7 @@ export function packMcpBundle(program: CliProgram, opts: PackMcpBundleOpts = {})
|
|
|
123
112
|
const binaryName = basename(binaryPath);
|
|
124
113
|
|
|
125
114
|
if (!existsSync(binaryPath)) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
`Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`,
|
|
128
|
-
);
|
|
115
|
+
throw new Error(`Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`);
|
|
129
116
|
}
|
|
130
117
|
|
|
131
118
|
const staging = mkdtempSync(join(tmpdir(), "mcpb-"));
|
|
@@ -164,9 +151,7 @@ export function packMcpBundle(program: CliProgram, opts: PackMcpBundleOpts = {})
|
|
|
164
151
|
export function runMcpBundle(program: CliProgram): void {
|
|
165
152
|
const mcp = program.mcpServer;
|
|
166
153
|
if (!mcp?.mcpd && !mcp?.claudePlugin) {
|
|
167
|
-
throw new Error(
|
|
168
|
-
"mcp bundle: enable mcpServer.mcpd and/or mcpServer.claudePlugin on the program root.",
|
|
169
|
-
);
|
|
154
|
+
throw new Error("mcp bundle: enable mcpServer.mcpd and/or mcpServer.claudePlugin on the program root.");
|
|
170
155
|
}
|
|
171
156
|
|
|
172
157
|
const lines: string[] = [];
|
package/src/mcp/claude.test.ts
CHANGED
|
@@ -87,9 +87,9 @@ describe("claude plugin", () => {
|
|
|
87
87
|
execSync("unzip -o -q ../out.zip", { cwd: extract });
|
|
88
88
|
const binMode = statSync(join(extract, "bin", "myapp")).mode & 0o777;
|
|
89
89
|
expect(binMode & 0o111).not.toBe(0);
|
|
90
|
-
const pluginJson = JSON.parse(
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
const pluginJson = JSON.parse(readFileSync(join(extract, ".claude-plugin", "plugin.json"), "utf8")) as {
|
|
91
|
+
mcpServers: string;
|
|
92
|
+
};
|
|
93
93
|
expect(pluginJson.mcpServers).toBe(".mcp.json");
|
|
94
94
|
} finally {
|
|
95
95
|
rmSync(work, { recursive: true, force: true });
|
package/src/mcp/claude.ts
CHANGED
|
@@ -58,10 +58,7 @@ export function defaultClaudePluginPaths(program: CliProgram, cwd = process.cwd(
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/** Generates `.claude-plugin/plugin.json` object. */
|
|
61
|
-
export function generatePluginManifest(
|
|
62
|
-
program: CliProgram,
|
|
63
|
-
_binaryName: string,
|
|
64
|
-
): Record<string, unknown> {
|
|
61
|
+
export function generatePluginManifest(program: CliProgram, _binaryName: string): Record<string, unknown> {
|
|
65
62
|
const bundle = program.mcpServer?.bundle;
|
|
66
63
|
const manifest: Record<string, unknown> = {
|
|
67
64
|
name: pluginName(program),
|
|
@@ -78,10 +75,7 @@ export function generatePluginManifest(
|
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
/** Generates plugin `.mcp.json` stdio server config. */
|
|
81
|
-
export function generatePluginMcpJson(
|
|
82
|
-
program: CliProgram,
|
|
83
|
-
binaryName: string,
|
|
84
|
-
): Record<string, unknown> {
|
|
78
|
+
export function generatePluginMcpJson(program: CliProgram, binaryName: string): Record<string, unknown> {
|
|
85
79
|
const mcp: Record<string, unknown> = {
|
|
86
80
|
command: `\${CLAUDE_PLUGIN_ROOT}/bin/${binaryName}`,
|
|
87
81
|
args: ["mcp"],
|
|
@@ -117,12 +111,7 @@ function collectZipEntries(rootDir: string, dir = rootDir): ZipFileEntry[] {
|
|
|
117
111
|
return entries;
|
|
118
112
|
}
|
|
119
113
|
|
|
120
|
-
function writePluginTree(
|
|
121
|
-
pluginRoot: string,
|
|
122
|
-
program: CliProgram,
|
|
123
|
-
binaryPath: string,
|
|
124
|
-
binaryName: string,
|
|
125
|
-
): void {
|
|
114
|
+
function writePluginTree(pluginRoot: string, program: CliProgram, binaryPath: string, binaryName: string): void {
|
|
126
115
|
const bundle = generatePluginSkillBundle(program);
|
|
127
116
|
const skillMd = applyPluginSkillHint(program, bundle.skillMd);
|
|
128
117
|
|
|
@@ -155,9 +144,7 @@ export function packClaudePlugin(program: CliProgram, opts: PackMcpBundleOpts =
|
|
|
155
144
|
const binaryName = basename(binaryPath);
|
|
156
145
|
|
|
157
146
|
if (!existsSync(binaryPath)) {
|
|
158
|
-
throw new Error(
|
|
159
|
-
`Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`,
|
|
160
|
-
);
|
|
147
|
+
throw new Error(`Binary not found: ${binaryPath}. Build with compile first (expected dist/${program.key}).`);
|
|
161
148
|
}
|
|
162
149
|
|
|
163
150
|
const staging = mkdtempSync(join(tmpdir(), "claude-plugin-"));
|
package/src/mcp/env.ts
CHANGED
|
@@ -51,9 +51,7 @@ export function bootstrapMcpEnv(config: { shellEnv?: boolean | string }): void {
|
|
|
51
51
|
: (process.env.SHELL ?? (process.platform === "darwin" ? "/bin/zsh" : "/bin/bash"));
|
|
52
52
|
const captured = captureShellEnv(shell);
|
|
53
53
|
if (Object.keys(captured).length === 0) {
|
|
54
|
-
process.stderr.write(
|
|
55
|
-
`[argsbarg] shellEnv: failed to capture shell environment from ${shell}\n`,
|
|
56
|
-
);
|
|
54
|
+
process.stderr.write(`[argsbarg] shellEnv: failed to capture shell environment from ${shell}\n`);
|
|
57
55
|
} else {
|
|
58
56
|
applyShellEnv(captured);
|
|
59
57
|
}
|
package/src/mcp/server.ts
CHANGED
|
@@ -7,12 +7,7 @@ import type { Cli } from "../cli.ts";
|
|
|
7
7
|
import { bootstrapAppConfig } from "../config/bootstrap.ts";
|
|
8
8
|
import { formatMcpMissingConfigMessage, missingRequiredConfig } from "../config/resolve.ts";
|
|
9
9
|
import { buildToolCallSuccess } from "./result.ts";
|
|
10
|
-
import {
|
|
11
|
-
allMcpResources,
|
|
12
|
-
collectMcpTools,
|
|
13
|
-
mcpToolCallToArgv,
|
|
14
|
-
resolveMcpServerInfo,
|
|
15
|
-
} from "./tools.ts";
|
|
10
|
+
import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpServerInfo } from "./tools.ts";
|
|
16
11
|
|
|
17
12
|
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
18
13
|
|
|
@@ -110,10 +105,7 @@ async function handleRequestLine(cli: Cli, line: string): Promise<void> {
|
|
|
110
105
|
return;
|
|
111
106
|
}
|
|
112
107
|
const rawArgs = params.arguments;
|
|
113
|
-
if (
|
|
114
|
-
rawArgs !== undefined &&
|
|
115
|
-
(typeof rawArgs !== "object" || rawArgs === null || Array.isArray(rawArgs))
|
|
116
|
-
) {
|
|
108
|
+
if (rawArgs !== undefined && (typeof rawArgs !== "object" || rawArgs === null || Array.isArray(rawArgs))) {
|
|
117
109
|
writeError(id, -32602, "Invalid params: arguments must be an object");
|
|
118
110
|
return;
|
|
119
111
|
}
|
|
@@ -162,8 +154,7 @@ async function handleRequestLine(cli: Cli, line: string): Promise<void> {
|
|
|
162
154
|
});
|
|
163
155
|
return;
|
|
164
156
|
}
|
|
165
|
-
const errText =
|
|
166
|
-
invokeResult.stderr.trim() || invokeResult.errorMsg || `Exit code ${invokeResult.exitCode}`;
|
|
157
|
+
const errText = invokeResult.stderr.trim() || invokeResult.errorMsg || `Exit code ${invokeResult.exitCode}`;
|
|
167
158
|
writeResponse({
|
|
168
159
|
jsonrpc: "2.0",
|
|
169
160
|
id,
|
package/src/mcp/tools.ts
CHANGED
|
@@ -137,11 +137,7 @@ function positionalProperty(p: CliPositional): Record<string, unknown> {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
/** Builds inputSchema for a leaf command. */
|
|
140
|
-
function buildInputSchema(
|
|
141
|
-
root: CliProgram,
|
|
142
|
-
path: string[],
|
|
143
|
-
leaf: CliLeaf,
|
|
144
|
-
): Record<string, unknown> {
|
|
140
|
+
function buildInputSchema(root: CliProgram, path: string[], leaf: CliLeaf): Record<string, unknown> {
|
|
145
141
|
const properties: Record<string, unknown> = {};
|
|
146
142
|
const required: string[] = [];
|
|
147
143
|
|
|
@@ -222,12 +218,7 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
|
|
|
222
218
|
/** Walks the command tree and appends leaf tools. */
|
|
223
219
|
function walk(cmd: CliNode, path: string[]): void {
|
|
224
220
|
if (isCliLeaf(cmd)) {
|
|
225
|
-
if (
|
|
226
|
-
cmd.key === "completion" ||
|
|
227
|
-
cmd.key === "configure" ||
|
|
228
|
-
cmd.key === "mcp" ||
|
|
229
|
-
cmd.key === "version"
|
|
230
|
-
) {
|
|
221
|
+
if (cmd.key === "completion" || cmd.key === "configure" || cmd.key === "mcp" || cmd.key === "version") {
|
|
231
222
|
return;
|
|
232
223
|
}
|
|
233
224
|
if (cmd.hidden || cmd.mcpTool?.enabled === false) {
|
|
@@ -7,12 +7,7 @@ import { join } from "node:path";
|
|
|
7
7
|
import { $ } from "bun";
|
|
8
8
|
import type { CliProgram } from "./index.ts";
|
|
9
9
|
import { buildToolCallSuccess } from "./mcp/result.ts";
|
|
10
|
-
import {
|
|
11
|
-
collectMcpTools,
|
|
12
|
-
mcpToolCallToArgv,
|
|
13
|
-
mcpToolDescription,
|
|
14
|
-
sanitizeToolSegment,
|
|
15
|
-
} from "./mcp/tools.ts";
|
|
10
|
+
import { collectMcpTools, mcpToolCallToArgv, mcpToolDescription, sanitizeToolSegment } from "./mcp/tools.ts";
|
|
16
11
|
import { cliSchemaExport } from "./schema.ts";
|
|
17
12
|
import { mcpRequest, nestedMcpFixture, testProgram } from "./test-fixtures.ts";
|
|
18
13
|
import { cliValidateProgram } from "./validate.ts";
|
|
@@ -489,10 +484,9 @@ test("minimal.ts mcp without opt-in fails", async () => {
|
|
|
489
484
|
});
|
|
490
485
|
|
|
491
486
|
test("MCP resources/list includes custom resource", async () => {
|
|
492
|
-
const responses = await mcpRequest(
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
);
|
|
487
|
+
const responses = await mcpRequest([{ jsonrpc: "2.0", id: 10, method: "resources/list", params: {} }], {
|
|
488
|
+
script: "examples/mcp-test.ts",
|
|
489
|
+
});
|
|
496
490
|
const res = responses.get(10) as { result: { resources: { uri: string }[] } };
|
|
497
491
|
const uris = res.result.resources.map((r) => r.uri);
|
|
498
492
|
expect(uris).toContain("mcp_test://schema");
|
package/src/parse.test.ts
CHANGED
|
@@ -12,12 +12,7 @@ import { cliPresentationRoot } from "./builtins/presentation.ts";
|
|
|
12
12
|
import { cliHelpRender } from "./help.ts";
|
|
13
13
|
import { Cli, CliFallbackMode, CliOptionKind, type CliProgram } from "./index.ts";
|
|
14
14
|
import { applyShellEnv } from "./mcp/env.ts";
|
|
15
|
-
import {
|
|
16
|
-
allMcpResources,
|
|
17
|
-
collectMcpTools,
|
|
18
|
-
mcpToolCallToArgv,
|
|
19
|
-
resolveMcpSchemaUri,
|
|
20
|
-
} from "./mcp/tools.ts";
|
|
15
|
+
import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpSchemaUri } from "./mcp/tools.ts";
|
|
21
16
|
import { ParseKind, parse, postParseValidate } from "./parse.ts";
|
|
22
17
|
import { cliSchemaJson } from "./schema.ts";
|
|
23
18
|
import { generatePluginSkillBundle, generateSkillBundle } from "./skill/generate.ts";
|
|
@@ -338,10 +333,7 @@ test("trailing options include parent-scoped flags", () => {
|
|
|
338
333
|
],
|
|
339
334
|
});
|
|
340
335
|
cliValidateProgram(root);
|
|
341
|
-
const pr = postParseValidate(
|
|
342
|
-
root,
|
|
343
|
-
parse(root, ["group", "leaf", "-u", "alice", "./file", "--json"]),
|
|
344
|
-
);
|
|
336
|
+
const pr = postParseValidate(root, parse(root, ["group", "leaf", "-u", "alice", "./file", "--json"]));
|
|
345
337
|
expect(pr.kind).toBe(ParseKind.Ok);
|
|
346
338
|
expect(pr.path).toEqual(["group", "leaf"]);
|
|
347
339
|
expect(pr.args).toEqual(["./file"]);
|
|
@@ -415,10 +407,7 @@ test("stops parsing options at --", () => {
|
|
|
415
407
|
],
|
|
416
408
|
});
|
|
417
409
|
cliValidateProgram(root);
|
|
418
|
-
const pr = postParseValidate(
|
|
419
|
-
root,
|
|
420
|
-
parse(root, ["x", "--name", "pat", "--", "--name", "bob", "-x"]),
|
|
421
|
-
);
|
|
410
|
+
const pr = postParseValidate(root, parse(root, ["x", "--name", "pat", "--", "--name", "bob", "-x"]));
|
|
422
411
|
expect(pr.kind).toBe(ParseKind.Ok);
|
|
423
412
|
expect(pr.opts.name).toBe("pat");
|
|
424
413
|
expect(pr.args).toEqual(["--name", "bob", "-x"]);
|
|
@@ -505,9 +494,7 @@ test("presence option cannot be required", () => {
|
|
|
505
494
|
test("leaf completion help prints correctly", async () => {
|
|
506
495
|
// Test the fix where `completion zsh -h` on a leaf root was incorrectly ignored.
|
|
507
496
|
// We run this as a subprocess so we don't accidentally exit the test runner.
|
|
508
|
-
const { stdout, stderr, exitCode } = await $`bun run examples/minimal.ts completion zsh -h
|
|
509
|
-
.nothrow()
|
|
510
|
-
.quiet();
|
|
497
|
+
const { stdout, stderr, exitCode } = await $`bun run examples/minimal.ts completion zsh -h`.nothrow().quiet();
|
|
511
498
|
const out = stdout.toString();
|
|
512
499
|
expect(exitCode).toBe(0);
|
|
513
500
|
expect(out).toContain("Show help for this command.");
|
|
@@ -517,9 +504,7 @@ test("leaf completion help prints correctly", async () => {
|
|
|
517
504
|
|
|
518
505
|
/** Docs schema exports JSON for nested CLIs. */
|
|
519
506
|
test("docs schema exports JSON for nested CLIs", async () => {
|
|
520
|
-
const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts docs schema
|
|
521
|
-
.nothrow()
|
|
522
|
-
.quiet();
|
|
507
|
+
const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts docs schema`.nothrow().quiet();
|
|
523
508
|
expect(exitCode).toBe(0);
|
|
524
509
|
expect(stderr.toString()).toBe("");
|
|
525
510
|
|
|
@@ -543,11 +528,7 @@ test("docs schema exports JSON for leaf roots", async () => {
|
|
|
543
528
|
expect(schema.key).toBe("minimal.ts");
|
|
544
529
|
expect(schema.positionals[0].name).toBe("name");
|
|
545
530
|
expect(schema.options[0].name).toBe("verbose");
|
|
546
|
-
expect(schema.commands.map((c: { key: string }) => c.key)).toEqual([
|
|
547
|
-
"version",
|
|
548
|
-
"configure",
|
|
549
|
-
"docs",
|
|
550
|
-
]);
|
|
531
|
+
expect(schema.commands.map((c: { key: string }) => c.key)).toEqual(["version", "configure", "docs"]);
|
|
551
532
|
});
|
|
552
533
|
|
|
553
534
|
test("version builtin prints program version", async () => {
|
|
@@ -956,9 +937,7 @@ test("Enum completions list choices in bash script", () => {
|
|
|
956
937
|
{
|
|
957
938
|
key: "run",
|
|
958
939
|
description: "",
|
|
959
|
-
options: [
|
|
960
|
-
{ name: "mode", description: "m", kind: CliOptionKind.Enum, choices: ["dev", "prod"] },
|
|
961
|
-
],
|
|
940
|
+
options: [{ name: "mode", description: "m", kind: CliOptionKind.Enum, choices: ["dev", "prod"] }],
|
|
962
941
|
handler: () => {},
|
|
963
942
|
},
|
|
964
943
|
],
|
|
@@ -1047,9 +1026,7 @@ test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
|
|
|
1047
1026
|
},
|
|
1048
1027
|
],
|
|
1049
1028
|
});
|
|
1050
|
-
expect(() => cliValidateProgram(root)).toThrow(
|
|
1051
|
-
/fallbackCommand 'missing' is not a child of 'docs'/,
|
|
1052
|
-
);
|
|
1029
|
+
expect(() => cliValidateProgram(root)).toThrow(/fallbackCommand 'missing' is not a child of 'docs'/);
|
|
1053
1030
|
});
|
|
1054
1031
|
|
|
1055
1032
|
test("cliValidateProgram accepts nested fallbackCommand when child exists", () => {
|
|
@@ -1279,9 +1256,7 @@ test("cliSkillInstall claude target uses .claude/skills", () => {
|
|
|
1279
1256
|
try {
|
|
1280
1257
|
const files = cliSkillInstall(nestedMcpFixture, "claude", { rimraf: true });
|
|
1281
1258
|
expect(files.some((f) => f.includes(".claude/skills/nested_ts/"))).toBe(true);
|
|
1282
|
-
expect(readFileSync(join(cwd, ".claude", "skills", "nested_ts", "SKILL.md"), "utf8")).toContain(
|
|
1283
|
-
"Claude Code",
|
|
1284
|
-
);
|
|
1259
|
+
expect(readFileSync(join(cwd, ".claude", "skills", "nested_ts", "SKILL.md"), "utf8")).toContain("Claude Code");
|
|
1285
1260
|
} finally {
|
|
1286
1261
|
process.chdir(prev);
|
|
1287
1262
|
rmSync(cwd, { recursive: true, force: true });
|