argsbarg 4.0.3 → 4.1.0
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 +42 -1
- package/README.md +6 -6
- package/docs/ai-skills.md +9 -4
- package/docs/bundled-docs.md +1 -0
- package/docs/cli-program.md +4 -4
- package/docs/config-schema.md +1 -4
- package/docs/developing.md +1 -1
- package/docs/install.md +160 -39
- package/docs/mcp.md +38 -11
- package/docs/templates/cursor/rules/cli-program.mdc +1 -1
- package/examples/config-app/program.ts +0 -3
- package/examples/consumer-app/README.md +1 -1
- package/examples/consumer-app/src/program.ts +5 -13
- package/examples/mcp-test.ts +14 -24
- package/index.d.ts +60 -5
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +20 -4
- package/src/builtins/config.test.ts +31 -25
- package/src/builtins/config.ts +4 -3
- package/src/builtins/install.ts +50 -57
- package/src/builtins/mcp.ts +1 -1
- package/src/capabilities.ts +4 -4
- package/src/cli.ts +2 -0
- package/src/config/bootstrap.ts +167 -66
- package/src/config/context.test.ts +22 -36
- package/src/config/context.ts +5 -4
- package/src/config/file.test.ts +66 -56
- package/src/config/file.ts +33 -25
- package/src/config/resolve.test.ts +25 -1
- package/src/config/resolve.ts +43 -8
- package/src/config.integration.test.ts +17 -10
- package/src/docs/api-guide.test.ts +1 -1
- package/src/docs/docs.test.ts +1 -1
- package/src/docs/mcp-guide.ts +15 -4
- package/src/docs/mcp-resources.test.ts +63 -0
- package/src/docs/mcp-resources.ts +68 -0
- package/src/hidden-mcpb.test.ts +41 -1
- package/src/index.ts +4 -0
- package/src/install/{binary.ts → app.ts} +13 -13
- package/src/install/bootstrap.ts +22 -0
- package/src/install/detect-installed.ts +2 -97
- package/src/install/index.ts +187 -110
- package/src/install/install-validate.test.ts +61 -0
- package/src/install/install.test.ts +138 -42
- package/src/install/mcp-openclaw.test.ts +40 -0
- package/src/install/mcp-openclaw.ts +106 -0
- package/src/install/normalize.ts +35 -0
- package/src/install/paths.ts +27 -13
- package/src/install/plan.ts +30 -259
- package/src/install/shell.ts +2 -2
- package/src/install/status.test.ts +85 -0
- package/src/install/status.ts +22 -9
- package/src/install/target-base.ts +93 -0
- package/src/install/target-detect.ts +20 -0
- package/src/install/target-effective.ts +131 -0
- package/src/install/target-mcp-cli.ts +149 -0
- package/src/install/target-mcp-json.ts +130 -0
- package/src/install/target-plan-build.ts +67 -0
- package/src/install/target-registry.ts +57 -0
- package/src/install/target-scope.ts +266 -0
- package/src/install/target-skill.ts +104 -0
- package/src/install/target-types.ts +145 -0
- package/src/install/targets/app.ts +69 -0
- package/src/install/targets/chatgpt-mcp.ts +12 -0
- package/src/install/targets/claude-code-mcp.ts +15 -0
- package/src/install/targets/claude-desktop-mcp.ts +12 -0
- package/src/install/targets/claude-skill.ts +16 -0
- package/src/install/targets/codex-mcp.ts +25 -0
- package/src/install/targets/codex-skill.ts +14 -0
- package/src/install/targets/completions.ts +133 -0
- package/src/install/targets/configure.ts +59 -0
- package/src/install/targets/cursor-mcp.ts +15 -0
- package/src/install/targets/cursor-skill.ts +16 -0
- package/src/install/targets/index.ts +53 -0
- package/src/install/targets/openclaw-mcp.ts +25 -0
- package/src/install/targets/openclaw-skill.ts +17 -0
- package/src/install/targets/opencode-mcp.ts +101 -0
- package/src/install/targets/opencode-skill.ts +15 -0
- package/src/install/targets.test.ts +136 -0
- package/src/install/uninstall.ts +16 -152
- package/src/install/update.test.ts +17 -2
- package/src/install/update.ts +2 -5
- package/src/invoke.test.ts +7 -1
- package/src/mcp/bundle.ts +16 -4
- package/src/mcp/claude.test.ts +23 -4
- package/src/mcp/claude.ts +18 -13
- package/src/mcp/tools.ts +4 -1
- package/src/mcp/zip.test.ts +17 -0
- package/src/mcp/zip.ts +62 -9
- package/src/mcp.integration.test.ts +18 -1
- package/src/parse.test.ts +57 -4
- package/src/paths/host.ts +11 -11
- package/src/paths/remove-empty-dir.ts +13 -0
- package/src/skill/generate.ts +89 -5
- package/src/skill/hint.ts +5 -0
- package/src/skill/install.ts +33 -6
- package/src/skill/naming.ts +28 -0
- package/src/types.ts +65 -4
- package/src/validate.ts +79 -4
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Auto MCP resources for user docs.topics when docs and MCP are both enabled.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CliProgram } from "../types.ts";
|
|
6
|
+
import {
|
|
7
|
+
docsEnabled,
|
|
8
|
+
docsTopicContent,
|
|
9
|
+
docsTopicDescription,
|
|
10
|
+
docsUserTopicKeys,
|
|
11
|
+
} from "./resolve.ts";
|
|
12
|
+
|
|
13
|
+
/** Default URI pattern for a docs topic MCP resource (`<mcpId>://docs/<topicKey>`). */
|
|
14
|
+
export function defaultDocsTopicResourceUri(mcpId: string, topicKey: string): string {
|
|
15
|
+
return `${mcpId}://docs/${topicKey}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Sanitized MCP server id from program key (matches mcpServerId in mcp/tools.ts). */
|
|
19
|
+
function mcpIdFromProgram(program: CliProgram): string {
|
|
20
|
+
return program.key.replace(/[^a-zA-Z0-9]/g, "_");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Resolved URI for one user docs topic resource. */
|
|
24
|
+
export function resolveDocsTopicResourceUri(program: CliProgram, topicKey: string): string {
|
|
25
|
+
return defaultDocsTopicResourceUri(mcpIdFromProgram(program), topicKey);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** All auto-generated docs topic resources (empty when docs or MCP disabled). */
|
|
29
|
+
export function docsMcpResources(program: CliProgram): {
|
|
30
|
+
uri: string;
|
|
31
|
+
name: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
mimeType: string;
|
|
34
|
+
load: () => string;
|
|
35
|
+
}[] {
|
|
36
|
+
if (!docsEnabled(program) || program.mcpServer?.enabled !== true) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const docs = program.docs;
|
|
40
|
+
if (!docs) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return docsUserTopicKeys(docs).map((key) => {
|
|
44
|
+
const topic = docs.topics[key];
|
|
45
|
+
if (!topic) {
|
|
46
|
+
throw new Error(`docs topic missing: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
uri: resolveDocsTopicResourceUri(program, key),
|
|
50
|
+
name: key,
|
|
51
|
+
description: docsTopicDescription(key, topic.description),
|
|
52
|
+
mimeType: "text/markdown",
|
|
53
|
+
load: () => docsTopicContent(program, key),
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Reserved MCP resource URIs from auto docs topics (for validation). */
|
|
59
|
+
export function reservedDocsTopicResourceUris(program: CliProgram): string[] {
|
|
60
|
+
if (!docsEnabled(program) || program.mcpServer?.enabled !== true) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const docs = program.docs;
|
|
64
|
+
if (!docs) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return docsUserTopicKeys(docs).map((key) => resolveDocsTopicResourceUri(program, key));
|
|
68
|
+
}
|
package/src/hidden-mcpb.test.ts
CHANGED
|
@@ -19,7 +19,7 @@ const hiddenFixture: CliProgram = {
|
|
|
19
19
|
key: "myapp",
|
|
20
20
|
version: "1.0.0",
|
|
21
21
|
description: "Hidden demo.",
|
|
22
|
-
mcpServer: { enabled: true },
|
|
22
|
+
mcpServer: { enabled: true, mcpd: true, claudePlugin: true },
|
|
23
23
|
commands: [
|
|
24
24
|
{
|
|
25
25
|
key: "public",
|
|
@@ -187,4 +187,44 @@ describe("mcp bundle", () => {
|
|
|
187
187
|
rmSync(work, { recursive: true, force: true });
|
|
188
188
|
}
|
|
189
189
|
});
|
|
190
|
+
|
|
191
|
+
test("runMcpBundle with claudePlugin only prints plugin path", () => {
|
|
192
|
+
const work = mkdtempSync(join(tmpdir(), "mcpb-run-"));
|
|
193
|
+
const stdout: string[] = [];
|
|
194
|
+
const orig = process.stdout.write.bind(process.stdout);
|
|
195
|
+
process.stdout.write = ((chunk: string | Uint8Array) => {
|
|
196
|
+
stdout.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"));
|
|
197
|
+
return true;
|
|
198
|
+
}) as typeof process.stdout.write;
|
|
199
|
+
const fixture: CliProgram = {
|
|
200
|
+
...hiddenFixture,
|
|
201
|
+
mcpServer: { enabled: true, claudePlugin: true },
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
const dist = join(work, "dist");
|
|
205
|
+
mkdirSync(dist, { recursive: true });
|
|
206
|
+
writeFileSync(join(dist, "myapp"), "#!/bin/sh\n", { mode: 0o755 });
|
|
207
|
+
const prevCwd = process.cwd();
|
|
208
|
+
process.chdir(work);
|
|
209
|
+
try {
|
|
210
|
+
runMcpBundle(fixture);
|
|
211
|
+
} finally {
|
|
212
|
+
process.chdir(prevCwd);
|
|
213
|
+
}
|
|
214
|
+
const lines = stdout.join("").trim().split("\n");
|
|
215
|
+
expect(lines).toHaveLength(1);
|
|
216
|
+
expect(lines[0]).toContain("claude-plugin");
|
|
217
|
+
} finally {
|
|
218
|
+
process.stdout.write = orig;
|
|
219
|
+
rmSync(work, { recursive: true, force: true });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("runMcpBundle errors when no bundle flags enabled", () => {
|
|
224
|
+
const fixture: CliProgram = {
|
|
225
|
+
...hiddenFixture,
|
|
226
|
+
mcpServer: { enabled: true },
|
|
227
|
+
};
|
|
228
|
+
expect(() => runMcpBundle(fixture)).toThrow(/mcpd/);
|
|
229
|
+
});
|
|
190
230
|
});
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ export type {
|
|
|
43
43
|
CliDocsTopic,
|
|
44
44
|
CliHandler,
|
|
45
45
|
CliInstallConfig,
|
|
46
|
+
CliInstallTargets,
|
|
46
47
|
CliInvocation,
|
|
47
48
|
CliMcpBundleConfig,
|
|
48
49
|
CliMcpResource,
|
|
@@ -53,6 +54,9 @@ export type {
|
|
|
53
54
|
CliProgram,
|
|
54
55
|
CliUpdateArtifact,
|
|
55
56
|
CliUpdateGetLatest,
|
|
57
|
+
InstallAgentIntegration,
|
|
58
|
+
InstallTargetSpec,
|
|
59
|
+
ResolvedInstallTarget,
|
|
56
60
|
} from "./types.ts";
|
|
57
61
|
export {
|
|
58
62
|
CliFallbackMode,
|
|
@@ -17,32 +17,32 @@ import {
|
|
|
17
17
|
removeRcBlock,
|
|
18
18
|
} from "./shell.ts";
|
|
19
19
|
|
|
20
|
-
export interface
|
|
20
|
+
export interface AppInstallResult {
|
|
21
21
|
changedFiles: string[];
|
|
22
22
|
patchedBashRc: boolean;
|
|
23
23
|
patchedZshRc: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/** Copies
|
|
27
|
-
export function
|
|
26
|
+
/** Copies the app executable to the install path and patches rc files when shells are detected. */
|
|
27
|
+
export function installApp(
|
|
28
28
|
root: CliProgram,
|
|
29
29
|
paths: InstallPaths,
|
|
30
30
|
dry: boolean,
|
|
31
31
|
sourcePath: string = process.execPath,
|
|
32
|
-
):
|
|
32
|
+
): AppInstallResult {
|
|
33
33
|
const changed: string[] = [];
|
|
34
34
|
const shells = detectShells();
|
|
35
35
|
let patchedBashRc = false;
|
|
36
36
|
let patchedZshRc = false;
|
|
37
37
|
|
|
38
38
|
if (!dry) {
|
|
39
|
-
mkdirSync(paths.
|
|
40
|
-
copyFileSync(sourcePath, paths.
|
|
39
|
+
mkdirSync(paths.appDir, { recursive: true });
|
|
40
|
+
copyFileSync(sourcePath, paths.appPath);
|
|
41
41
|
}
|
|
42
|
-
changed.push(paths.
|
|
42
|
+
changed.push(paths.appPath);
|
|
43
43
|
|
|
44
44
|
if (shells.bash && existsSync(dirname(paths.bashRc))) {
|
|
45
|
-
const block = buildPathRcBlock(root.key, paths.
|
|
45
|
+
const block = buildPathRcBlock(root.key, paths.appDir);
|
|
46
46
|
let content = existsSync(paths.bashRc) ? readFileSync(paths.bashRc, "utf8") : "";
|
|
47
47
|
if (!hasRcBlock(content, root.key, "path")) {
|
|
48
48
|
if (!content.endsWith("\n") && content.length > 0) content += "\n";
|
|
@@ -69,12 +69,12 @@ export function installBinary(
|
|
|
69
69
|
return { changedFiles: changed, patchedBashRc, patchedZshRc };
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
/** Removes
|
|
73
|
-
export function
|
|
72
|
+
/** Removes the installed app and rc marker blocks. */
|
|
73
|
+
export function uninstallApp(root: CliProgram, paths: InstallPaths, dry: boolean): string[] {
|
|
74
74
|
const changed: string[] = [];
|
|
75
|
-
if (existsSync(paths.
|
|
76
|
-
if (!dry) unlinkSync(paths.
|
|
77
|
-
changed.push(paths.
|
|
75
|
+
if (existsSync(paths.appPath)) {
|
|
76
|
+
if (!dry) unlinkSync(paths.appPath);
|
|
77
|
+
changed.push(paths.appPath);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
for (const [rcPath, tag] of [
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import type { CliProgram } from "../types.ts";
|
|
3
|
+
import { resolveAppDir } from "./paths.ts";
|
|
4
|
+
|
|
5
|
+
/** Rewrites empty argv to `install` when the app is not yet installed (TTY only). */
|
|
6
|
+
export function maybeBootstrapInstallArgv(argv: string[], program: CliProgram): string[] {
|
|
7
|
+
if (argv.length !== 0) {
|
|
8
|
+
return argv;
|
|
9
|
+
}
|
|
10
|
+
if (program.install?.enabled === false) {
|
|
11
|
+
return argv;
|
|
12
|
+
}
|
|
13
|
+
if (!process.stdin.isTTY) {
|
|
14
|
+
return argv;
|
|
15
|
+
}
|
|
16
|
+
const appDir = resolveAppDir();
|
|
17
|
+
const appPath = `${appDir}/${program.key}`;
|
|
18
|
+
if (existsSync(appPath)) {
|
|
19
|
+
return argv;
|
|
20
|
+
}
|
|
21
|
+
return ["install"];
|
|
22
|
+
}
|
|
@@ -1,97 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { detectOpenCodeMcpConfigPath } from "./mcp-opencode.ts";
|
|
4
|
-
import { type InstallPaths, userHome } from "./paths.ts";
|
|
5
|
-
|
|
6
|
-
export interface InstalledArtifacts {
|
|
7
|
-
binary: boolean;
|
|
8
|
-
bashCompletion: boolean;
|
|
9
|
-
zshCompletion: boolean;
|
|
10
|
-
fishCompletion: boolean;
|
|
11
|
-
cursorSkill: boolean;
|
|
12
|
-
claudeSkill: boolean;
|
|
13
|
-
cursorMcp: boolean;
|
|
14
|
-
claudeMcp: boolean;
|
|
15
|
-
claudeDesktopMcp: boolean;
|
|
16
|
-
opencodeMcp: boolean;
|
|
17
|
-
codexMcp: boolean;
|
|
18
|
-
chatGptMcp: boolean;
|
|
19
|
-
bashRcPath: boolean;
|
|
20
|
-
zshRcFpath: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function mcpConfigHasServer(path: string, name: string): boolean {
|
|
24
|
-
if (!existsSync(path)) return false;
|
|
25
|
-
try {
|
|
26
|
-
const data = JSON.parse(readFileSync(path, "utf8")) as { mcpServers?: Record<string, unknown> };
|
|
27
|
-
return data.mcpServers?.[name] !== undefined;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Detects which install artifacts are currently present. */
|
|
34
|
-
export function detectInstalledArtifacts(paths: InstallPaths): InstalledArtifacts {
|
|
35
|
-
return {
|
|
36
|
-
binary: existsSync(paths.binaryPath),
|
|
37
|
-
bashCompletion: existsSync(paths.bashCompletion),
|
|
38
|
-
zshCompletion: existsSync(paths.zshCompletion),
|
|
39
|
-
fishCompletion: existsSync(paths.fishCompletion),
|
|
40
|
-
cursorSkill: existsSync(paths.cursorSkillDir),
|
|
41
|
-
claudeSkill: existsSync(paths.claudeSkillDir),
|
|
42
|
-
cursorMcp: mcpConfigHasServer(paths.cursorMcpPath, paths.mcpName),
|
|
43
|
-
claudeMcp: mcpConfigHasServer(paths.claudeMcpPath, paths.mcpName),
|
|
44
|
-
claudeDesktopMcp: mcpConfigHasServer(paths.claudeDesktopMcpPath, paths.mcpName),
|
|
45
|
-
opencodeMcp: detectOpenCodeMcpConfigPath(userHome(), paths.mcpName) !== undefined,
|
|
46
|
-
codexMcp: codexMcpHasServer(userHome(), paths.mcpName),
|
|
47
|
-
chatGptMcp: mcpConfigHasServer(paths.chatGptMcpPath, paths.mcpName),
|
|
48
|
-
bashRcPath: false,
|
|
49
|
-
zshRcFpath: false,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface InstallStatus {
|
|
54
|
-
binary?: string;
|
|
55
|
-
bashCompletion?: string;
|
|
56
|
-
zshCompletion?: string;
|
|
57
|
-
fishCompletion?: string;
|
|
58
|
-
cursorSkill?: string;
|
|
59
|
-
claudeSkill?: string;
|
|
60
|
-
cursorMcp?: string;
|
|
61
|
-
claudeMcp?: string;
|
|
62
|
-
claudeDesktopMcp?: string;
|
|
63
|
-
opencodeMcp?: string;
|
|
64
|
-
codexMcp?: string;
|
|
65
|
-
chatGptMcp?: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Builds a status inventory from detected artifacts. */
|
|
69
|
-
export function buildInstallStatus(
|
|
70
|
-
paths: InstallPaths,
|
|
71
|
-
detected: InstalledArtifacts,
|
|
72
|
-
): InstallStatus {
|
|
73
|
-
const status: InstallStatus = {};
|
|
74
|
-
if (detected.binary) status.binary = paths.binaryPath;
|
|
75
|
-
if (detected.bashCompletion) status.bashCompletion = paths.bashCompletion;
|
|
76
|
-
if (detected.zshCompletion) status.zshCompletion = paths.zshCompletion;
|
|
77
|
-
if (detected.fishCompletion) status.fishCompletion = paths.fishCompletion;
|
|
78
|
-
if (detected.cursorSkill) status.cursorSkill = `${paths.cursorSkillDir}/`;
|
|
79
|
-
if (detected.claudeSkill) status.claudeSkill = `${paths.claudeSkillDir}/`;
|
|
80
|
-
if (detected.cursorMcp) status.cursorMcp = `${paths.cursorMcpPath} (server "${paths.mcpName}")`;
|
|
81
|
-
if (detected.claudeMcp) status.claudeMcp = `${paths.claudeMcpPath} (server "${paths.mcpName}")`;
|
|
82
|
-
if (detected.claudeDesktopMcp) {
|
|
83
|
-
status.claudeDesktopMcp = `${paths.claudeDesktopMcpPath} (server "${paths.mcpName}")`;
|
|
84
|
-
}
|
|
85
|
-
if (detected.opencodeMcp) {
|
|
86
|
-
const openCodePath =
|
|
87
|
-
detectOpenCodeMcpConfigPath(userHome(), paths.mcpName) ?? paths.opencodeMcpPath;
|
|
88
|
-
status.opencodeMcp = `${openCodePath} (server "${paths.mcpName}")`;
|
|
89
|
-
}
|
|
90
|
-
if (detected.codexMcp) {
|
|
91
|
-
status.codexMcp = `${paths.codexConfigPath} (server "${paths.mcpName}")`;
|
|
92
|
-
}
|
|
93
|
-
if (detected.chatGptMcp) {
|
|
94
|
-
status.chatGptMcp = `${paths.chatGptMcpPath} (server "${paths.mcpName}")`;
|
|
95
|
-
}
|
|
96
|
-
return status;
|
|
97
|
-
}
|
|
1
|
+
export { buildInstallStatus, detectInstalledArtifacts } from "./target-detect.ts";
|
|
2
|
+
export type { InstalledArtifacts, InstallStatus } from "./target-types.ts";
|