argsbarg 3.4.2 → 3.5.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 +14 -1
- package/biome.json +29 -6
- package/bun.lock +22 -0
- package/docs/install.md +1 -1
- package/docs/mcp.md +45 -1
- package/index.d.ts +50 -50
- package/justfile +27 -6
- package/package.json +4 -2
- package/scripts/release.ts +26 -9
- package/src/builtins/builtins.test.ts +9 -4
- package/src/builtins/completion-bash.ts +74 -50
- package/src/builtins/completion-fish.ts +3 -8
- package/src/builtins/completion-group.ts +1 -1
- package/src/builtins/completion-zsh.ts +80 -42
- package/src/builtins/dispatch.ts +20 -16
- package/src/builtins/export.ts +19 -10
- package/src/builtins/index.ts +9 -4
- package/src/builtins/install.ts +10 -10
- package/src/builtins/mcp.ts +1 -1
- package/src/builtins/presentation.ts +8 -8
- package/src/builtins/scopes.ts +1 -1
- package/src/builtins/version.ts +1 -1
- package/src/completion.ts +4 -4
- package/src/docs/api-guide.test.ts +2 -2
- package/src/docs/api-guide.ts +2 -2
- package/src/docs/builtin.ts +27 -8
- package/src/docs/docs.test.ts +18 -11
- package/src/docs/mcp-guide.ts +105 -24
- package/src/docs/resolve.ts +10 -3
- package/src/docs/save.ts +11 -3
- package/src/headless.test.ts +8 -16
- package/src/help.ts +73 -43
- package/src/hidden-mcpb.test.ts +7 -6
- package/src/hidden.ts +2 -2
- package/src/index.test.ts +113 -89
- package/src/index.ts +24 -24
- package/src/install/binary.ts +12 -5
- package/src/install/completions.ts +7 -3
- package/src/install/detect-installed.ts +29 -4
- package/src/install/gh-release-update.ts +31 -23
- package/src/install/index.ts +69 -19
- package/src/install/install.test.ts +31 -8
- package/src/install/mcp-codex.test.ts +57 -0
- package/src/install/mcp-codex.ts +125 -0
- package/src/install/mcp-config.ts +12 -5
- package/src/install/mcp-opencode.test.ts +98 -0
- package/src/install/mcp-opencode.ts +149 -0
- package/src/install/paths.ts +29 -3
- package/src/install/plan.ts +73 -6
- package/src/install/shell.ts +1 -4
- package/src/install/status.ts +12 -6
- package/src/install/uninstall.ts +38 -4
- package/src/install/update.test.ts +2 -2
- package/src/install/update.ts +3 -1
- package/src/invoke.ts +12 -9
- package/src/mcp/bundle.ts +36 -8
- package/src/mcp/env.ts +7 -13
- package/src/mcp/server.ts +12 -6
- package/src/mcp/tools.ts +20 -4
- package/src/mcp.ts +3 -3
- package/src/parse.ts +96 -24
- package/src/runtime.ts +22 -12
- package/src/schema.ts +11 -5
- package/src/skill/generate.ts +4 -4
- package/src/skill/install.ts +6 -2
- package/src/validate.ts +21 -16
package/src/builtins/export.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type CliCapabilities, resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
3
|
+
import { visibleOptions } from "../hidden.ts";
|
|
2
4
|
import type { CliFallbackMode, CliNode, CliOption, CliPositional, CliProgram } from "../types.ts";
|
|
3
5
|
import { isCliRouter } from "../types.ts";
|
|
4
|
-
import { visibleOptions } from "../hidden.ts";
|
|
5
6
|
import { cliBuiltinCompletionGroup } from "./completion-group.ts";
|
|
6
7
|
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
7
8
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
8
9
|
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
9
|
-
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
10
10
|
|
|
11
11
|
/** JSON-safe command node (no handlers). */
|
|
12
12
|
export interface CliSchemaExport {
|
|
@@ -55,22 +55,31 @@ function exportBuiltinNode(cmd: CliNode): CliSchemaExport | null {
|
|
|
55
55
|
return out;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
function pushExportedBuiltin(builtins: CliSchemaExport[], cmd: CliNode): void {
|
|
59
|
+
const node = exportBuiltinNode(cmd);
|
|
60
|
+
if (node) {
|
|
61
|
+
builtins.push(node);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
/** Built-in subtrees matching help visibility for `--schema` export. */
|
|
59
|
-
export function exportPresentationBuiltins(
|
|
66
|
+
export function exportPresentationBuiltins(
|
|
67
|
+
program: CliProgram,
|
|
68
|
+
caps?: CliCapabilities,
|
|
69
|
+
): CliSchemaExport[] {
|
|
60
70
|
const resolved = caps ?? resolveCapabilities(program);
|
|
61
|
-
const builtins: CliSchemaExport[] = [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
];
|
|
71
|
+
const builtins: CliSchemaExport[] = [];
|
|
72
|
+
pushExportedBuiltin(builtins, cliBuiltinCompletionGroup(program));
|
|
73
|
+
pushExportedBuiltin(builtins, cliBuiltinVersionCommand());
|
|
65
74
|
if (resolved.install) {
|
|
66
|
-
builtins
|
|
75
|
+
pushExportedBuiltin(builtins, cliBuiltinInstallCommand(program));
|
|
67
76
|
}
|
|
68
77
|
const docsGroup = cliBuiltinDocsGroupIfEnabled(program);
|
|
69
78
|
if (docsGroup) {
|
|
70
|
-
builtins
|
|
79
|
+
pushExportedBuiltin(builtins, docsGroup);
|
|
71
80
|
}
|
|
72
81
|
if (resolved.mcp) {
|
|
73
|
-
builtins
|
|
82
|
+
pushExportedBuiltin(builtins, cliBuiltinMcpCommand(program));
|
|
74
83
|
}
|
|
75
84
|
return builtins;
|
|
76
85
|
}
|
package/src/builtins/index.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
export { completionBashScript } from "./completion-bash.ts";
|
|
2
|
-
export { completionZshScript } from "./completion-zsh.ts";
|
|
3
2
|
export { completionFishScript } from "./completion-fish.ts";
|
|
4
3
|
export { cliBuiltinCompletionGroup } from "./completion-group.ts";
|
|
4
|
+
export { completionZshScript } from "./completion-zsh.ts";
|
|
5
|
+
export { builtinInterceptRoot, dispatchBuiltin } from "./dispatch.ts";
|
|
6
|
+
export { type CliSchemaExport, exportPresentationBuiltins } from "./export.ts";
|
|
5
7
|
export { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
|
|
6
8
|
export { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
export {
|
|
10
|
+
cliParseRoot,
|
|
11
|
+
cliPresentationRoot,
|
|
12
|
+
parseBuiltins,
|
|
13
|
+
presentationBuiltins,
|
|
14
|
+
} from "./presentation.ts";
|
|
10
15
|
export { collectScopes, type ScopeRec } from "./scopes.ts";
|
package/src/builtins/install.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { type CliLeaf, type CliOption, CliOptionKind, type CliProgram } from "../types.ts";
|
|
3
3
|
|
|
4
4
|
/** Install command options (dynamic: `--mcp` only when MCP is enabled). */
|
|
5
5
|
export function installBuiltinOptions(root: CliProgram): CliOption[] {
|
|
@@ -42,12 +42,14 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
|
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
name: "uninstall",
|
|
45
|
-
description:
|
|
45
|
+
description:
|
|
46
|
+
"Remove installed artifacts (use --all or scoped flags; skips targets not on disk).",
|
|
46
47
|
kind: CliOptionKind.Presence,
|
|
47
48
|
},
|
|
48
49
|
{
|
|
49
50
|
name: "prefix",
|
|
50
|
-
description:
|
|
51
|
+
description:
|
|
52
|
+
"Install directory for the binary (default ~/.local/bin; overrides INSTALL_PREFIX).",
|
|
51
53
|
kind: CliOptionKind.String,
|
|
52
54
|
},
|
|
53
55
|
{
|
|
@@ -67,7 +69,8 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
|
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
name: "quiet",
|
|
70
|
-
description:
|
|
72
|
+
description:
|
|
73
|
+
"Suppress informational output (requires --yes, --json, --reinstall, or --update).",
|
|
71
74
|
kind: CliOptionKind.Presence,
|
|
72
75
|
},
|
|
73
76
|
];
|
|
@@ -103,11 +106,7 @@ export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
|
|
|
103
106
|
` ${app} install --reinstall`,
|
|
104
107
|
];
|
|
105
108
|
if (resolveCapabilities(root).update) {
|
|
106
|
-
notesLines.push(
|
|
107
|
-
"",
|
|
108
|
-
"Upgrade to latest release:",
|
|
109
|
-
` ${app} install --update`,
|
|
110
|
-
);
|
|
109
|
+
notesLines.push("", "Upgrade to latest release:", ` ${app} install --update`);
|
|
111
110
|
}
|
|
112
111
|
notesLines.push(
|
|
113
112
|
"",
|
|
@@ -122,7 +121,8 @@ export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
|
|
|
122
121
|
);
|
|
123
122
|
return {
|
|
124
123
|
key: "install",
|
|
125
|
-
description:
|
|
124
|
+
description:
|
|
125
|
+
"Install the binary, shell completions, agent skills, and MCP config to your user environment.",
|
|
126
126
|
notes: notesLines.join("\n"),
|
|
127
127
|
options: installBuiltinOptions(root),
|
|
128
128
|
handler: () => {},
|
package/src/builtins/mcp.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
2
|
import { docsEnabled } from "../docs/resolve.ts";
|
|
3
|
-
import { type CliLeaf, type CliProgram, type CliRouter
|
|
3
|
+
import { CliFallbackMode, type CliLeaf, type CliProgram, type CliRouter } from "../types.ts";
|
|
4
4
|
|
|
5
5
|
/** Built-in `mcp` router: bare `myapp mcp` runs stdio (via hidden `serve` fallback); `mcp bundle` packs `.mcpb`. */
|
|
6
6
|
export function cliBuiltinMcpCommand(program: CliProgram): CliRouter {
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import type { CliCapabilities } from "../capabilities.ts";
|
|
2
2
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
3
|
+
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
3
4
|
import { presentationNode, visibleOptions } from "../hidden.ts";
|
|
4
5
|
import type { CliLeaf, CliNode, CliProgram, CliRouter } from "../types.ts";
|
|
5
|
-
import { isCliLeaf
|
|
6
|
+
import { isCliLeaf } from "../types.ts";
|
|
6
7
|
import { cliBuiltinCompletionGroup } from "./completion-group.ts";
|
|
7
8
|
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
8
9
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
9
10
|
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
10
|
-
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
11
11
|
|
|
12
12
|
/** All built-in command nodes for argv parsing (includes hidden builtins). */
|
|
13
13
|
export function parseBuiltins(program: CliProgram, caps: CliCapabilities): CliNode[] {
|
|
14
|
-
const builtins: CliNode[] = [
|
|
15
|
-
cliBuiltinCompletionGroup(program),
|
|
16
|
-
cliBuiltinVersionCommand(),
|
|
17
|
-
];
|
|
14
|
+
const builtins: CliNode[] = [cliBuiltinCompletionGroup(program), cliBuiltinVersionCommand()];
|
|
18
15
|
if (caps.install) {
|
|
19
16
|
builtins.push(cliBuiltinInstallCommand(program));
|
|
20
17
|
}
|
|
@@ -98,10 +95,13 @@ export function cliPresentationRoot(program: CliProgram): CliRouter {
|
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
/** Root help notes: consumer `program.notes` plus agent discovery when `docs` is enabled. */
|
|
101
|
-
export function presentationRootNotes(
|
|
98
|
+
export function presentationRootNotes(
|
|
99
|
+
program: CliProgram,
|
|
100
|
+
caps: CliCapabilities,
|
|
101
|
+
): string | undefined {
|
|
102
102
|
const parts: string[] = [];
|
|
103
103
|
if ((program.notes ?? "").trim().length > 0) {
|
|
104
|
-
parts.push(program.notes
|
|
104
|
+
parts.push((program.notes ?? "").trim());
|
|
105
105
|
}
|
|
106
106
|
if (caps.docs) {
|
|
107
107
|
parts.push(`For AI agents: \`${program.key} docs skill\`.`);
|
package/src/builtins/scopes.ts
CHANGED
|
@@ -25,7 +25,7 @@ function walkScopes(cmdPath: string, cmd: CliNode, acc: ScopeRec[]): void {
|
|
|
25
25
|
wantsFiles: hasPositionalArguments(cmd),
|
|
26
26
|
});
|
|
27
27
|
for (const ch of kids) {
|
|
28
|
-
const nextPath = cmdPath === "" ? ch.key : cmdPath
|
|
28
|
+
const nextPath = cmdPath === "" ? ch.key : `${cmdPath}/${ch.key}`;
|
|
29
29
|
walkScopes(nextPath, ch, acc);
|
|
30
30
|
}
|
|
31
31
|
}
|
package/src/builtins/version.ts
CHANGED
package/src/completion.ts
CHANGED
|
@@ -3,11 +3,11 @@ Re-export shim — completion emitters and built-in trees live in ./builtins/.
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export {
|
|
6
|
-
completionBashScript,
|
|
7
|
-
completionZshScript,
|
|
8
|
-
completionFishScript,
|
|
9
|
-
cliPresentationRoot,
|
|
10
6
|
cliBuiltinCompletionGroup,
|
|
11
7
|
cliBuiltinInstallCommand,
|
|
12
8
|
cliBuiltinMcpCommand,
|
|
9
|
+
cliPresentationRoot,
|
|
10
|
+
completionBashScript,
|
|
11
|
+
completionFishScript,
|
|
12
|
+
completionZshScript,
|
|
13
13
|
} from "./builtins/index.ts";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { expect, test } from "bun:test";
|
|
2
|
+
import { cliSchemaExport } from "../schema.ts";
|
|
2
3
|
import type { CliProgram } from "../types.ts";
|
|
3
4
|
import { CliOptionKind } from "../types.ts";
|
|
4
5
|
import { generateApiGuide, generateApiGuideBody } from "./api-guide.ts";
|
|
5
|
-
import { cliSchemaExport } from "../schema.ts";
|
|
6
6
|
|
|
7
7
|
const nestedFixture: CliProgram = {
|
|
8
8
|
key: "nested.ts",
|
|
@@ -125,7 +125,7 @@ test("generateApiGuide and cliSchemaExport include leaf outputSchema", () => {
|
|
|
125
125
|
],
|
|
126
126
|
};
|
|
127
127
|
const schema = cliSchemaExport(fixture);
|
|
128
|
-
expect(schema.commands
|
|
128
|
+
expect(schema.commands?.[0]?.outputSchema).toEqual({
|
|
129
129
|
type: "object",
|
|
130
130
|
properties: { id: { type: "string" } },
|
|
131
131
|
required: ["id"],
|
package/src/docs/api-guide.ts
CHANGED
|
@@ -101,7 +101,7 @@ function renderCommandNode(
|
|
|
101
101
|
lines.push("#### Options", "");
|
|
102
102
|
lines.push("| Option | Type | Required | Description |");
|
|
103
103
|
lines.push("| --- | --- | --- | --- |");
|
|
104
|
-
for (const opt of node.options
|
|
104
|
+
for (const opt of node.options ?? []) {
|
|
105
105
|
lines.push(formatOptionRow(opt));
|
|
106
106
|
}
|
|
107
107
|
lines.push("");
|
|
@@ -111,7 +111,7 @@ function renderCommandNode(
|
|
|
111
111
|
lines.push("#### Positionals", "");
|
|
112
112
|
lines.push("| Argument | Type | Required | Description |");
|
|
113
113
|
lines.push("| --- | --- | --- | --- |");
|
|
114
|
-
for (const p of node.positionals
|
|
114
|
+
for (const p of node.positionals ?? []) {
|
|
115
115
|
lines.push(formatPositionalRow(p));
|
|
116
116
|
}
|
|
117
117
|
lines.push("");
|
package/src/docs/builtin.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CliFallbackMode,
|
|
3
|
+
type CliLeaf,
|
|
4
|
+
type CliOption,
|
|
5
|
+
CliOptionKind,
|
|
6
|
+
type CliProgram,
|
|
7
|
+
type CliRouter,
|
|
8
|
+
} from "../types.ts";
|
|
2
9
|
import {
|
|
3
10
|
DOCS_ROUTER_DESCRIPTION,
|
|
4
11
|
docsEffectiveDefaultTopic,
|
|
@@ -16,7 +23,11 @@ const DOCS_SAVE_OPTION: CliOption = {
|
|
|
16
23
|
kind: CliOptionKind.Presence,
|
|
17
24
|
};
|
|
18
25
|
|
|
19
|
-
function runDocsTopic(
|
|
26
|
+
function runDocsTopic(
|
|
27
|
+
program: CliProgram,
|
|
28
|
+
topic: string,
|
|
29
|
+
ctx: { hasFlag(name: string): boolean },
|
|
30
|
+
): void {
|
|
20
31
|
if (ctx.hasFlag("save")) {
|
|
21
32
|
process.stdout.write(`${saveDocsTopic(program, topic)}\n`);
|
|
22
33
|
return;
|
|
@@ -43,24 +54,32 @@ function docsRouterNotes(): string {
|
|
|
43
54
|
|
|
44
55
|
/** Built-in `docs` router with bundled topic subcommands. */
|
|
45
56
|
export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
|
|
46
|
-
const docs = program.docs
|
|
57
|
+
const docs = program.docs;
|
|
58
|
+
if (!docs) {
|
|
59
|
+
throw new Error("docs not enabled");
|
|
60
|
+
}
|
|
47
61
|
const leaves: CliLeaf[] = [];
|
|
48
62
|
|
|
49
63
|
for (const key of docsUserTopicKeys(docs)) {
|
|
50
|
-
const topic = docs.topics[key]
|
|
64
|
+
const topic = docs.topics[key];
|
|
65
|
+
if (!topic) {
|
|
66
|
+
throw new Error(`docs topic missing: ${key}`);
|
|
67
|
+
}
|
|
51
68
|
leaves.push(docsLeaf(program, key, docsTopicDescription(key, topic.description)));
|
|
52
69
|
}
|
|
53
70
|
|
|
54
71
|
if (docsIncludesMcpTopic(program)) {
|
|
55
|
-
leaves.push(
|
|
56
|
-
docsLeaf(program, "mcp", "Print MCP server setup and tool guidance."),
|
|
57
|
-
);
|
|
72
|
+
leaves.push(docsLeaf(program, "mcp", "Print MCP server setup and tool guidance."));
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
leaves.push(
|
|
61
76
|
docsLeaf(program, "schema", "Print the full command tree as JSON."),
|
|
62
77
|
docsLeaf(program, "api", "Print the full command reference as markdown."),
|
|
63
|
-
docsLeaf(
|
|
78
|
+
docsLeaf(
|
|
79
|
+
program,
|
|
80
|
+
"skill",
|
|
81
|
+
"Print a reference agent SKILL, use `install --skill` for optimized.",
|
|
82
|
+
),
|
|
64
83
|
);
|
|
65
84
|
|
|
66
85
|
return {
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, expect, test } from "bun:test";
|
|
2
2
|
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
3
|
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
5
|
import { cliPresentationRoot } from "../builtins/presentation.ts";
|
|
6
6
|
import { completionBashScript } from "../completion.ts";
|
|
7
7
|
import { cliInvoke } from "../index.ts";
|
|
8
8
|
import type { CliProgram } from "../types.ts";
|
|
9
9
|
import { cliValidateProgram } from "../validate.ts";
|
|
10
|
-
import { docsEffectiveDefaultTopic } from "./resolve.ts";
|
|
11
10
|
import { generateMcpGuide } from "./mcp-guide.ts";
|
|
11
|
+
import { docsEffectiveDefaultTopic } from "./resolve.ts";
|
|
12
12
|
import { saveDocsTopic } from "./save.ts";
|
|
13
13
|
|
|
14
14
|
let workDir: string;
|
|
@@ -64,13 +64,15 @@ test("docs reserved when enabled", () => {
|
|
|
64
64
|
|
|
65
65
|
test("docs rejects reserved topic keys", () => {
|
|
66
66
|
const root = docsFixture();
|
|
67
|
-
|
|
67
|
+
const docs = root.docs;
|
|
68
|
+
if (!docs) throw new Error("expected docs fixture");
|
|
69
|
+
docs.topics.schema = { text: "nope" };
|
|
68
70
|
expect(() => cliValidateProgram(root)).toThrow(/reserved/);
|
|
69
|
-
delete
|
|
70
|
-
|
|
71
|
+
delete docs.topics.schema;
|
|
72
|
+
docs.topics.skill = { text: "nope" };
|
|
71
73
|
expect(() => cliValidateProgram(root)).toThrow(/reserved/);
|
|
72
|
-
delete
|
|
73
|
-
|
|
74
|
+
delete docs.topics.skill;
|
|
75
|
+
docs.topics.api = { text: "nope" };
|
|
74
76
|
expect(() => cliValidateProgram(root)).toThrow(/reserved/);
|
|
75
77
|
});
|
|
76
78
|
|
|
@@ -127,9 +129,9 @@ test("presentation includes docs subtree", () => {
|
|
|
127
129
|
const presentation = cliPresentationRoot(docsFixture());
|
|
128
130
|
const docsNode = presentation.commands.find((c) => c.key === "docs");
|
|
129
131
|
expect(docsNode).toBeDefined();
|
|
130
|
-
expect(
|
|
131
|
-
|
|
132
|
-
);
|
|
132
|
+
expect(
|
|
133
|
+
docsNode && "commands" in docsNode && docsNode.commands.some((c) => c.key === "readme"),
|
|
134
|
+
).toBe(true);
|
|
133
135
|
});
|
|
134
136
|
|
|
135
137
|
test("docs schema prints JSON", async () => {
|
|
@@ -199,6 +201,11 @@ test("generateMcpGuide includes schema URI and install targets", () => {
|
|
|
199
201
|
expect(guide).toContain("myapp://schema");
|
|
200
202
|
expect(guide).toContain("~/.cursor/mcp.json");
|
|
201
203
|
expect(guide).toContain("claude_desktop_config.json");
|
|
204
|
+
expect(guide).toContain("## Installation");
|
|
205
|
+
expect(guide).toContain("## Running directly");
|
|
206
|
+
expect(guide).toContain("install --bin");
|
|
207
|
+
expect(guide).toContain("OpenAI Codex");
|
|
208
|
+
expect(guide).toContain("ChatGPT");
|
|
202
209
|
});
|
|
203
210
|
|
|
204
211
|
test("docs --save writes topic file", async () => {
|
package/src/docs/mcp-guide.ts
CHANGED
|
@@ -1,11 +1,75 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { expectedOpenCodeMcpEntry, OPENCODE_CONFIG_SCHEMA } from "../install/mcp-opencode.ts";
|
|
2
3
|
import {
|
|
3
4
|
collectMcpTools,
|
|
5
|
+
type McpToolDef,
|
|
4
6
|
mcpServerId,
|
|
5
7
|
resolveMcpSchemaUri,
|
|
6
|
-
type McpToolDef,
|
|
7
8
|
} from "../mcp/tools.ts";
|
|
8
|
-
import {
|
|
9
|
+
import { collectOptionDefs } from "../parse.ts";
|
|
10
|
+
import { CliOptionKind, type CliProgram } from "../types.ts";
|
|
11
|
+
|
|
12
|
+
/** Extra host notes for generated `docs mcp` (manual fallbacks and ChatGPT Connectors). */
|
|
13
|
+
function appendManualHostSetup(lines: string[], root: CliProgram, serverId: string): void {
|
|
14
|
+
const openCodeEntry = expectedOpenCodeMcpEntry(root);
|
|
15
|
+
|
|
16
|
+
lines.push(
|
|
17
|
+
"| OpenCode | `~/.config/opencode/*` (when `~/.config/opencode` exists) |",
|
|
18
|
+
"| OpenAI Codex | `~/.codex/config.toml` via `codex mcp add` (when `codex` is on PATH) |",
|
|
19
|
+
"| ChatGPT desktop | `chatgpt_mcp_config.json` (when ChatGPT app data exists) |",
|
|
20
|
+
"",
|
|
21
|
+
"Claude Desktop paths by platform:",
|
|
22
|
+
"",
|
|
23
|
+
"- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`",
|
|
24
|
+
"- **Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`",
|
|
25
|
+
"- **Linux:** `~/.config/Claude/claude_desktop_config.json`",
|
|
26
|
+
"",
|
|
27
|
+
"ChatGPT desktop JSON (when auto-installed):",
|
|
28
|
+
"",
|
|
29
|
+
"- **macOS:** `~/Library/Application Support/ChatGPT/chatgpt_mcp_config.json`",
|
|
30
|
+
"- **Windows:** `%APPDATA%\\OpenAI\\ChatGPT\\chatgpt_mcp_config.json`",
|
|
31
|
+
"",
|
|
32
|
+
"Restart Claude Desktop and ChatGPT desktop after changing their config files.",
|
|
33
|
+
"",
|
|
34
|
+
"### Manual fallbacks",
|
|
35
|
+
"",
|
|
36
|
+
"**OpenCode** (no `~/.config/opencode` yet):",
|
|
37
|
+
"",
|
|
38
|
+
"```json",
|
|
39
|
+
JSON.stringify(
|
|
40
|
+
{
|
|
41
|
+
$schema: OPENCODE_CONFIG_SCHEMA,
|
|
42
|
+
mcp: { [serverId]: openCodeEntry },
|
|
43
|
+
},
|
|
44
|
+
null,
|
|
45
|
+
2,
|
|
46
|
+
),
|
|
47
|
+
"```",
|
|
48
|
+
"",
|
|
49
|
+
"**Codex** (`codex` not on PATH):",
|
|
50
|
+
"",
|
|
51
|
+
"```toml",
|
|
52
|
+
`[mcp_servers.${serverId}]`,
|
|
53
|
+
`command = "${root.key}"`,
|
|
54
|
+
'args = ["mcp"]',
|
|
55
|
+
"```",
|
|
56
|
+
"",
|
|
57
|
+
`Or after installing Codex CLI: \`codex mcp add ${serverId} -- ${root.key} mcp\`.`,
|
|
58
|
+
"",
|
|
59
|
+
"### ChatGPT web (Connectors)",
|
|
60
|
+
"",
|
|
61
|
+
`OpenAI's documented path for **ChatGPT web/desktop** is **Settings → Connectors → Developer mode** with a **remote HTTPS MCP URL** — not local stdio. ChatGPT does not spawn \`${root.key} mcp\` directly.`,
|
|
62
|
+
"",
|
|
63
|
+
"For local stdio, bridge and tunnel, then register the HTTPS URL in Connectors:",
|
|
64
|
+
"",
|
|
65
|
+
`1. Expose \`${root.key} mcp\` over HTTP (e.g. \`mcp-remote\`).`,
|
|
66
|
+
"2. Tunnel if needed (ngrok, Cloudflare Tunnel).",
|
|
67
|
+
"3. Add the public URL as a custom connector.",
|
|
68
|
+
"",
|
|
69
|
+
"Desktop `chatgpt_mcp_config.json` is merged when the ChatGPT app is installed; support varies by build. Use Connectors when local JSON is absent or tools do not appear.",
|
|
70
|
+
"",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
9
73
|
|
|
10
74
|
/** Formats one exposed MCP tool for the auto-generated MCP guide. */
|
|
11
75
|
function formatToolLine(root: CliProgram, tool: McpToolDef): string {
|
|
@@ -24,23 +88,36 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
24
88
|
const tools = collectMcpTools(root);
|
|
25
89
|
const schemaUri = resolveMcpSchemaUri(root);
|
|
26
90
|
const serverId = mcpServerId(root);
|
|
27
|
-
const mcp = root.mcpServer
|
|
91
|
+
const mcp = root.mcpServer;
|
|
92
|
+
if (!mcp) {
|
|
93
|
+
throw new Error("MCP server not enabled");
|
|
94
|
+
}
|
|
95
|
+
const caps = resolveCapabilities(root);
|
|
28
96
|
|
|
29
97
|
const lines: string[] = [
|
|
30
98
|
`# MCP server (${root.key})`,
|
|
31
99
|
"",
|
|
32
100
|
`${root.key} exposes an MCP server with features similar to the CLI.`,
|
|
33
101
|
"",
|
|
34
|
-
"##
|
|
35
|
-
"",
|
|
36
|
-
"```bash",
|
|
37
|
-
`${root.key} mcp`,
|
|
38
|
-
"```",
|
|
39
|
-
"",
|
|
40
|
-
"## Client setup",
|
|
102
|
+
"## Installation",
|
|
41
103
|
"",
|
|
42
104
|
"### `install --mcp`",
|
|
43
105
|
"",
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
if (caps.install) {
|
|
109
|
+
lines.push(
|
|
110
|
+
`Install the CLI first so \`${root.key}\` is on your PATH (e.g. \`${root.key} install --bin --yes\` or \`install --all --yes\`). Host configs reference the binary by name.`,
|
|
111
|
+
"",
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
lines.push(
|
|
115
|
+
`The CLI binary \`${root.key}\` must already be on your PATH. Host configs reference it by name.`,
|
|
116
|
+
"",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
lines.push(
|
|
44
121
|
"```bash",
|
|
45
122
|
`${root.key} install --mcp --yes`,
|
|
46
123
|
"```",
|
|
@@ -52,18 +129,14 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
52
129
|
"| Cursor | `~/.cursor/mcp.json` (when `~/.cursor` exists) |",
|
|
53
130
|
"| Claude Code | `~/.claude.json` |",
|
|
54
131
|
"| Claude Desktop | `claude_desktop_config.json` (when Claude Desktop app data exists) |",
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
appendManualHostSetup(lines, root, serverId);
|
|
135
|
+
|
|
136
|
+
lines.push(
|
|
137
|
+
"### Manual `mcpServers` entry",
|
|
55
138
|
"",
|
|
56
|
-
"Claude
|
|
57
|
-
"",
|
|
58
|
-
"- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`",
|
|
59
|
-
"- **Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`",
|
|
60
|
-
"- **Linux:** `~/.config/Claude/claude_desktop_config.json`",
|
|
61
|
-
"",
|
|
62
|
-
"Restart Claude Desktop after changing its config.",
|
|
63
|
-
"",
|
|
64
|
-
"### Manual entry",
|
|
65
|
-
"",
|
|
66
|
-
"Add under `mcpServers` in the host config:",
|
|
139
|
+
"For Cursor, Claude, and ChatGPT desktop JSON configs, add under `mcpServers`:",
|
|
67
140
|
"",
|
|
68
141
|
"```json",
|
|
69
142
|
JSON.stringify(
|
|
@@ -80,7 +153,15 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
80
153
|
),
|
|
81
154
|
"```",
|
|
82
155
|
"",
|
|
83
|
-
|
|
156
|
+
"## Running directly",
|
|
157
|
+
"",
|
|
158
|
+
"Start the stdio MCP server without editing host config:",
|
|
159
|
+
"",
|
|
160
|
+
"```bash",
|
|
161
|
+
`${root.key} mcp`,
|
|
162
|
+
"```",
|
|
163
|
+
"",
|
|
164
|
+
);
|
|
84
165
|
|
|
85
166
|
if (mcp.shellEnv || mcp.envFile) {
|
|
86
167
|
lines.push("## Environment", "");
|
|
@@ -91,7 +172,7 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
91
172
|
}
|
|
92
173
|
if (mcp.envFile) {
|
|
93
174
|
lines.push(
|
|
94
|
-
|
|
175
|
+
`- **\`envFile\`** — loads \`${mcp.envFile}\` after shell env (overrides for its keys).`,
|
|
95
176
|
);
|
|
96
177
|
}
|
|
97
178
|
lines.push("");
|
package/src/docs/resolve.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CliDocsConfig, CliProgram } from "../types.ts";
|
|
2
1
|
import { cliSchemaJson } from "../schema.ts";
|
|
3
2
|
import { generateSkillBundle } from "../skill/generate.ts";
|
|
3
|
+
import type { CliDocsConfig, CliProgram } from "../types.ts";
|
|
4
4
|
import { generateApiGuide } from "./api-guide.ts";
|
|
5
5
|
import { generateMcpGuide } from "./mcp-guide.ts";
|
|
6
6
|
|
|
@@ -31,7 +31,11 @@ export function docsEffectiveDefaultTopic(docs: CliDocsConfig): string {
|
|
|
31
31
|
if (keys.length === 0) {
|
|
32
32
|
throw new Error("docs.topics must be non-empty");
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
const first = keys[0];
|
|
35
|
+
if (first === undefined) {
|
|
36
|
+
throw new Error("docs.topics must be non-empty");
|
|
37
|
+
}
|
|
38
|
+
return first;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
/** Whether MCP auto-guide topic is included. */
|
|
@@ -53,7 +57,10 @@ export function docsTopicDescription(key: string, custom?: string): string {
|
|
|
53
57
|
|
|
54
58
|
/** Markdown body for one docs topic key. */
|
|
55
59
|
export function docsTopicText(program: CliProgram, topic: string): string {
|
|
56
|
-
const docs = program.docs
|
|
60
|
+
const docs = program.docs;
|
|
61
|
+
if (!docs) {
|
|
62
|
+
throw new Error("docs not enabled");
|
|
63
|
+
}
|
|
57
64
|
if (topic === "mcp") {
|
|
58
65
|
if (!docsIncludesMcpTopic(program)) {
|
|
59
66
|
throw new Error("Unknown docs topic 'mcp'.");
|
package/src/docs/save.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { generatedFileHtmlComment, insertGeneratedHint } from "../skill/hint.ts";
|
|
3
4
|
import type { CliProgram } from "../types.ts";
|
|
4
5
|
import { docsTopicContent } from "./resolve.ts";
|
|
5
|
-
import { generatedFileHtmlComment, insertGeneratedHint } from "../skill/hint.ts";
|
|
6
6
|
|
|
7
7
|
/** Relative output directory for `docs --save`. */
|
|
8
8
|
export const DOCS_SAVE_DIR = "docs";
|
|
@@ -21,12 +21,20 @@ export function docsSaveGeneratedHint(program: CliProgram, topic: string): strin
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** Inserts save hint without breaking YAML frontmatter (`docs skill`). */
|
|
24
|
-
export function applySaveGeneratedHint(
|
|
24
|
+
export function applySaveGeneratedHint(
|
|
25
|
+
program: CliProgram,
|
|
26
|
+
topic: string,
|
|
27
|
+
content: string,
|
|
28
|
+
): string {
|
|
25
29
|
if (!docsTopicIsGeneratedByArgsbarg(topic)) {
|
|
26
30
|
return content;
|
|
27
31
|
}
|
|
28
32
|
const hint = docsSaveGeneratedHint(program, topic);
|
|
29
|
-
return insertGeneratedHint(
|
|
33
|
+
return insertGeneratedHint(
|
|
34
|
+
content,
|
|
35
|
+
hint,
|
|
36
|
+
topic === "skill" ? { afterFrontmatter: true } : undefined,
|
|
37
|
+
);
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
/** File body for `--save` (hint on argsbarg-generated markdown only). */
|
package/src/headless.test.ts
CHANGED
|
@@ -22,28 +22,20 @@ test("shouldRunHeadless is true for MCP and json", () => {
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
test("shouldRunHeadlessWithPositionals requires positionals in non-tty", () => {
|
|
25
|
-
expect(
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
expect(
|
|
29
|
-
|
|
30
|
-
)
|
|
25
|
+
expect(shouldRunHeadlessWithPositionals({ invocation: "cli" }, false, [], false, false)).toBe(
|
|
26
|
+
false,
|
|
27
|
+
);
|
|
28
|
+
expect(shouldRunHeadlessWithPositionals({ invocation: "cli" }, false, ["a"], false, false)).toBe(
|
|
29
|
+
true,
|
|
30
|
+
);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
test("shouldRunHeadlessWithYes requires yes in non-tty", () => {
|
|
34
34
|
expect(
|
|
35
|
-
shouldRunHeadlessWithYes(
|
|
36
|
-
{ invocation: "cli" },
|
|
37
|
-
{ yes: true, hasRequiredArgs: true },
|
|
38
|
-
false,
|
|
39
|
-
),
|
|
35
|
+
shouldRunHeadlessWithYes({ invocation: "cli" }, { yes: true, hasRequiredArgs: true }, false),
|
|
40
36
|
).toBe(true);
|
|
41
37
|
expect(
|
|
42
|
-
shouldRunHeadlessWithYes(
|
|
43
|
-
{ invocation: "cli" },
|
|
44
|
-
{ yes: false, hasRequiredArgs: true },
|
|
45
|
-
false,
|
|
46
|
-
),
|
|
38
|
+
shouldRunHeadlessWithYes({ invocation: "cli" }, { yes: false, hasRequiredArgs: true }, false),
|
|
47
39
|
).toBe(false);
|
|
48
40
|
expect(
|
|
49
41
|
shouldRunHeadlessWithYes(
|