argsbarg 5.1.7 → 5.1.9
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 +22 -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 +3 -2
- package/examples/full-example/justfile +2 -5
- package/examples/full-example/scripts/with-dev-formula.ts +6 -1
- package/index.d.ts +10 -1
- package/package.json +1 -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
|
@@ -46,11 +46,7 @@ export abstract class InstallTarget {
|
|
|
46
46
|
|
|
47
47
|
protected abstract isDetectedFromSnapshot(detected: DetectedSnapshot): boolean;
|
|
48
48
|
|
|
49
|
-
statusLine(
|
|
50
|
-
paths: InstallPaths,
|
|
51
|
-
root: CliProgram,
|
|
52
|
-
detected: InstalledArtifacts,
|
|
53
|
-
): string | undefined {
|
|
49
|
+
statusLine(paths: InstallPaths, root: CliProgram, detected: InstalledArtifacts): string | undefined {
|
|
54
50
|
if (!this.isDetectedFromInstalled(detected)) return undefined;
|
|
55
51
|
return this.formatStatusLine(paths, root);
|
|
56
52
|
}
|
|
@@ -78,12 +74,7 @@ export abstract class InstallTarget {
|
|
|
78
74
|
|
|
79
75
|
protected abstract buildUninstallActions(ctx: TargetPlanContext): UninstallAction[];
|
|
80
76
|
|
|
81
|
-
contributeStatus(
|
|
82
|
-
paths: InstallPaths,
|
|
83
|
-
root: CliProgram,
|
|
84
|
-
detected: InstalledArtifacts,
|
|
85
|
-
status: InstallStatus,
|
|
86
|
-
): void {
|
|
77
|
+
contributeStatus(paths: InstallPaths, root: CliProgram, detected: InstalledArtifacts, status: InstallStatus): void {
|
|
87
78
|
const line = this.statusLine(paths, root, detected);
|
|
88
79
|
if (!line) return;
|
|
89
80
|
this.assignStatusLine(status, line);
|
|
@@ -7,11 +7,7 @@ export { buildDetectedSnapshot, detectInstalledArtifacts } from "./target-scope.
|
|
|
7
7
|
export type { InstalledArtifacts, InstallStatus } from "./target-types.ts";
|
|
8
8
|
|
|
9
9
|
/** Builds a status inventory from detected artifacts. */
|
|
10
|
-
export function buildInstallStatus(
|
|
11
|
-
paths: InstallPaths,
|
|
12
|
-
detected: InstalledArtifacts,
|
|
13
|
-
root: CliProgram,
|
|
14
|
-
): InstallStatus {
|
|
10
|
+
export function buildInstallStatus(paths: InstallPaths, detected: InstalledArtifacts, root: CliProgram): InstallStatus {
|
|
15
11
|
const status: InstallStatus = {};
|
|
16
12
|
for (const target of INSTALL_TARGETS) {
|
|
17
13
|
target.contributeStatus(paths, root, detected, status);
|
|
@@ -19,10 +19,7 @@ export type { InstallTargetSpec, ResolvedInstallTarget } from "../types.ts";
|
|
|
19
19
|
|
|
20
20
|
export type { InstallPlanMode, InstallScope } from "./target-types.ts";
|
|
21
21
|
|
|
22
|
-
export function resolveAgentIntegration(
|
|
23
|
-
configure?: CliConfigureConfig,
|
|
24
|
-
mcpEnabled = false,
|
|
25
|
-
): InstallAgentIntegration {
|
|
22
|
+
export function resolveAgentIntegration(configure?: CliConfigureConfig, mcpEnabled = false): InstallAgentIntegration {
|
|
26
23
|
return configure?.agentIntegration ?? (mcpEnabled ? "mcp" : "skill");
|
|
27
24
|
}
|
|
28
25
|
|
|
@@ -95,10 +92,7 @@ export function resolveEffectiveInstallTargets(
|
|
|
95
92
|
const user = configure?.targets;
|
|
96
93
|
const out = {} as Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>;
|
|
97
94
|
for (const key of INSTALL_ARTIFACT_KEYS) {
|
|
98
|
-
out[key] = resolveInstallTargetSpec(
|
|
99
|
-
user?.[key],
|
|
100
|
-
artifactDefaults(key, integration, mcpEnabled),
|
|
101
|
-
);
|
|
95
|
+
out[key] = resolveInstallTargetSpec(user?.[key], artifactDefaults(key, integration, mcpEnabled));
|
|
102
96
|
}
|
|
103
97
|
applyAgentPairDedupe(out, user, integration);
|
|
104
98
|
return out;
|
|
@@ -132,10 +126,6 @@ export function resolveInstallPlanMode(opts: {
|
|
|
132
126
|
}
|
|
133
127
|
|
|
134
128
|
/** @deprecated Use target registry. */
|
|
135
|
-
export function isInstallTargetAvailable(
|
|
136
|
-
key: CliInstallArtifactKey,
|
|
137
|
-
root: CliProgram,
|
|
138
|
-
paths: InstallPaths,
|
|
139
|
-
): boolean {
|
|
129
|
+
export function isInstallTargetAvailable(key: CliInstallArtifactKey, root: CliProgram, paths: InstallPaths): boolean {
|
|
140
130
|
return installTargetForKey(key)?.isAvailable(root, paths) ?? false;
|
|
141
131
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import type { CliProgram } from "../types.ts";
|
|
2
|
-
import {
|
|
3
|
-
checkCodexMcpConflict,
|
|
4
|
-
codexMcpHasServer,
|
|
5
|
-
mergeCodexMcpConfig,
|
|
6
|
-
removeCodexMcpConfig,
|
|
7
|
-
} from "./mcp-codex.ts";
|
|
2
|
+
import { checkCodexMcpConflict, codexMcpHasServer, mergeCodexMcpConfig, removeCodexMcpConfig } from "./mcp-codex.ts";
|
|
8
3
|
import { expectedMcpEntry } from "./mcp-config.ts";
|
|
9
4
|
import {
|
|
10
5
|
checkOpenclawMcpConflict,
|
|
@@ -37,20 +32,9 @@ export interface McpCliHostSpec {
|
|
|
37
32
|
hasServer: (home: string, name: string) => boolean;
|
|
38
33
|
installMessage: string;
|
|
39
34
|
uninstallMessage: string;
|
|
40
|
-
merge: (
|
|
41
|
-
home: string,
|
|
42
|
-
name: string,
|
|
43
|
-
entry: ReturnType<typeof expectedMcpEntry>,
|
|
44
|
-
dry: boolean,
|
|
45
|
-
yes: boolean,
|
|
46
|
-
) => string;
|
|
35
|
+
merge: (home: string, name: string, entry: ReturnType<typeof expectedMcpEntry>, dry: boolean, yes: boolean) => string;
|
|
47
36
|
remove: (home: string, name: string, dry: boolean) => void;
|
|
48
|
-
preflight: (
|
|
49
|
-
home: string,
|
|
50
|
-
name: string,
|
|
51
|
-
entry: ReturnType<typeof expectedMcpEntry>,
|
|
52
|
-
yes: boolean,
|
|
53
|
-
) => string | null;
|
|
37
|
+
preflight: (home: string, name: string, entry: ReturnType<typeof expectedMcpEntry>, yes: boolean) => string | null;
|
|
54
38
|
}
|
|
55
39
|
|
|
56
40
|
/** MCP host registered via an external CLI (codex, openclaw). */
|
|
@@ -108,13 +92,7 @@ export class McpCliInstallTarget extends InstallTarget {
|
|
|
108
92
|
summary: `${this.spec.label}: ${displayInstallPath(configPath)}`,
|
|
109
93
|
message: this.spec.installMessage.replace("{name}", ctx.paths.mcpName),
|
|
110
94
|
run: () => {
|
|
111
|
-
const written = this.spec.merge(
|
|
112
|
-
userHome(),
|
|
113
|
-
ctx.paths.mcpName,
|
|
114
|
-
entry,
|
|
115
|
-
ctx.dry,
|
|
116
|
-
!!ctx.opts.yes,
|
|
117
|
-
);
|
|
95
|
+
const written = this.spec.merge(userHome(), ctx.paths.mcpName, entry, ctx.dry, !!ctx.opts.yes);
|
|
118
96
|
return [written];
|
|
119
97
|
},
|
|
120
98
|
},
|
|
@@ -25,14 +25,8 @@ export interface McpJsonHostSpec {
|
|
|
25
25
|
label: string;
|
|
26
26
|
pairedSkillKey?: CliInstallArtifactKey;
|
|
27
27
|
configPath: (paths: InstallPaths) => string;
|
|
28
|
-
detectedKey: keyof Pick<
|
|
29
|
-
|
|
30
|
-
"cursorMcp" | "claudeMcp" | "claudeDesktopMcp" | "chatGptMcp"
|
|
31
|
-
>;
|
|
32
|
-
statusField: keyof Pick<
|
|
33
|
-
InstallStatus,
|
|
34
|
-
"cursorMcp" | "claudeMcp" | "claudeDesktopMcp" | "chatGptMcp"
|
|
35
|
-
>;
|
|
28
|
+
detectedKey: keyof Pick<InstalledArtifacts, "cursorMcp" | "claudeMcp" | "claudeDesktopMcp" | "chatGptMcp">;
|
|
29
|
+
statusField: keyof Pick<InstallStatus, "cursorMcp" | "claudeMcp" | "claudeDesktopMcp" | "chatGptMcp">;
|
|
36
30
|
isAvailable: (root: CliProgram, paths: InstallPaths) => boolean;
|
|
37
31
|
/** Append server name to status line (default false). */
|
|
38
32
|
statusIncludesServer?: boolean;
|
|
@@ -89,12 +83,7 @@ export class McpJsonInstallTarget extends InstallTarget {
|
|
|
89
83
|
|
|
90
84
|
preflight(ctx: TargetPlanContext): string | null {
|
|
91
85
|
const entry = expectedMcpEntry(ctx.root);
|
|
92
|
-
return checkMcpConflict(
|
|
93
|
-
this.spec.configPath(ctx.paths),
|
|
94
|
-
ctx.paths.mcpName,
|
|
95
|
-
entry,
|
|
96
|
-
!!ctx.opts.yes,
|
|
97
|
-
);
|
|
86
|
+
return checkMcpConflict(this.spec.configPath(ctx.paths), ctx.paths.mcpName, entry, !!ctx.opts.yes);
|
|
98
87
|
}
|
|
99
88
|
|
|
100
89
|
protected buildInstallActions(ctx: TargetPlanContext): InstallAction[] {
|
|
@@ -7,11 +7,7 @@ import { buildDetectedSnapshot, buildTargetPlanContext } from "./target-scope.ts
|
|
|
7
7
|
import type { InstallAction, InstallOpts, UninstallAction } from "./target-types.ts";
|
|
8
8
|
|
|
9
9
|
/** Builds install actions for normal mode (--all / scoped targets). */
|
|
10
|
-
export function buildInstallPlanFromTargets(
|
|
11
|
-
root: CliProgram,
|
|
12
|
-
paths: InstallPaths,
|
|
13
|
-
opts: InstallOpts,
|
|
14
|
-
): InstallAction[] {
|
|
10
|
+
export function buildInstallPlanFromTargets(root: CliProgram, paths: InstallPaths, opts: InstallOpts): InstallAction[] {
|
|
15
11
|
const detected = buildDetectedSnapshot(root, paths);
|
|
16
12
|
const ctx = buildTargetPlanContext(root, paths, opts, detected);
|
|
17
13
|
|
|
@@ -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");
|