argsbarg 3.1.0 → 3.2.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 +17 -1
- package/README.md +4 -6
- package/docs/ai-skills.md +1 -1
- package/docs/bundled-docs.md +17 -3
- package/docs/install.md +16 -4
- package/docs/mcp.md +4 -6
- package/examples/minimal.ts +6 -0
- package/index.d.ts +18 -1
- package/package.json +1 -1
- package/src/builtins/completion-bash.ts +1 -8
- package/src/builtins/completion-fish.ts +0 -5
- package/src/builtins/completion-zsh.ts +1 -9
- package/src/builtins/dispatch.ts +27 -0
- package/src/builtins/export.ts +4 -0
- package/src/builtins/install.ts +9 -3
- package/src/builtins/presentation.ts +4 -0
- package/src/builtins/shell-helpers.ts +0 -2
- package/src/builtins/update.ts +14 -0
- package/src/capabilities.ts +7 -1
- package/src/docs/api-guide.test.ts +55 -0
- package/src/docs/api-guide.ts +129 -0
- package/src/docs/builtin.ts +3 -0
- package/src/docs/docs.test.ts +47 -1
- package/src/docs/mcp-guide.ts +3 -3
- package/src/docs/resolve.ts +32 -1
- package/src/help.ts +3 -7
- package/src/index.test.ts +36 -65
- package/src/index.ts +2 -0
- package/src/install/binary.ts +8 -3
- package/src/install/index.ts +55 -30
- package/src/install/plan.ts +5 -3
- package/src/install/update.test.ts +106 -0
- package/src/install/update.ts +55 -0
- package/src/invoke.ts +1 -11
- package/src/mcp/tools.ts +1 -1
- package/src/parse.ts +0 -27
- package/src/runtime.ts +7 -6
- package/src/schema.ts +7 -2
- package/src/skill/generate.ts +2 -2
- package/src/types.ts +17 -0
- package/src/validate.ts +11 -6
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { CliSchemaExport } from "../builtins/export.ts";
|
|
2
|
+
import { cliPositionalLabel } from "../help.ts";
|
|
3
|
+
import { cliSchemaExport } from "../schema.ts";
|
|
4
|
+
import type { CliOption, CliPositional, CliProgram } from "../types.ts";
|
|
5
|
+
import { CliFallbackMode, CliOptionKind } from "../types.ts";
|
|
6
|
+
|
|
7
|
+
/** CLI invocation path as a single string (`myapp stat owner lookup`). */
|
|
8
|
+
function commandPath(rootKey: string, path: string[]): string {
|
|
9
|
+
if (path.length === 0) {
|
|
10
|
+
return rootKey;
|
|
11
|
+
}
|
|
12
|
+
return [rootKey, ...path].join(" ");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Human-readable option type for API tables. */
|
|
16
|
+
function optionType(opt: CliOption): string {
|
|
17
|
+
if (opt.kind === CliOptionKind.Presence) {
|
|
18
|
+
return "flag";
|
|
19
|
+
}
|
|
20
|
+
if (opt.kind === CliOptionKind.Enum) {
|
|
21
|
+
return `enum (\`${(opt.choices ?? []).join("`, `")}\`)`;
|
|
22
|
+
}
|
|
23
|
+
return opt.kind;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Markdown table cell for one option flag. */
|
|
27
|
+
function optionLabel(opt: CliOption): string {
|
|
28
|
+
const long = `\`--${opt.name}\``;
|
|
29
|
+
const short = opt.shortName ? ` (\`-${opt.shortName}\`)` : "";
|
|
30
|
+
return `${long}${short}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One options table row. */
|
|
34
|
+
function formatOptionRow(opt: CliOption): string {
|
|
35
|
+
const req = opt.required ? "required" : "optional";
|
|
36
|
+
return `| ${optionLabel(opt)} | ${optionType(opt)} | ${req} | ${opt.description} |`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** One positionals table row. */
|
|
40
|
+
function formatPositionalRow(p: CliPositional): string {
|
|
41
|
+
const label = cliPositionalLabel(p, false);
|
|
42
|
+
const req = (p.argMin ?? 1) > 0 ? "required" : "optional";
|
|
43
|
+
return `| \`${label}\` | ${p.kind} | ${req} | ${p.description} |`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Fallback routing note when present on a router node. */
|
|
47
|
+
function fallbackLine(node: CliSchemaExport): string | null {
|
|
48
|
+
if (node.fallbackCommand === undefined) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const mode = node.fallbackMode ?? CliFallbackMode.MissingOnly;
|
|
52
|
+
return `**Default subcommand:** \`${node.fallbackCommand}\` (\`${mode}\`)`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Renders one command node and recurses into subcommands. */
|
|
56
|
+
function renderCommandNode(
|
|
57
|
+
rootKey: string,
|
|
58
|
+
path: string[],
|
|
59
|
+
node: CliSchemaExport,
|
|
60
|
+
lines: string[],
|
|
61
|
+
): void {
|
|
62
|
+
const level = Math.min(path.length + 2, 6);
|
|
63
|
+
const heading = "#".repeat(level);
|
|
64
|
+
const cmd = commandPath(rootKey, path);
|
|
65
|
+
|
|
66
|
+
lines.push(`${heading} \`${cmd}\``, "", node.description, "");
|
|
67
|
+
|
|
68
|
+
if (node.notes) {
|
|
69
|
+
lines.push(`> ${node.notes}`, "");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const fb = fallbackLine(node);
|
|
73
|
+
if (fb) {
|
|
74
|
+
lines.push(fb, "");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ((node.options ?? []).length > 0) {
|
|
78
|
+
lines.push("#### Options", "");
|
|
79
|
+
lines.push("| Option | Type | Required | Description |");
|
|
80
|
+
lines.push("| --- | --- | --- | --- |");
|
|
81
|
+
for (const opt of node.options!) {
|
|
82
|
+
lines.push(formatOptionRow(opt));
|
|
83
|
+
}
|
|
84
|
+
lines.push("");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if ((node.positionals ?? []).length > 0) {
|
|
88
|
+
lines.push("#### Positionals", "");
|
|
89
|
+
lines.push("| Argument | Type | Required | Description |");
|
|
90
|
+
lines.push("| --- | --- | --- | --- |");
|
|
91
|
+
for (const p of node.positionals!) {
|
|
92
|
+
lines.push(formatPositionalRow(p));
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const children = node.commands ?? [];
|
|
98
|
+
if (children.length > 0) {
|
|
99
|
+
lines.push("#### Subcommands", "");
|
|
100
|
+
for (const child of children) {
|
|
101
|
+
lines.push(`- \`${child.key}\` — ${child.description}`);
|
|
102
|
+
}
|
|
103
|
+
lines.push("");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const child of children) {
|
|
107
|
+
renderCommandNode(rootKey, [...path, child.key], child, lines);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Generates markdown API reference from the same export as `docs schema`. */
|
|
112
|
+
export function generateApiGuide(program: CliProgram): string {
|
|
113
|
+
const schema = cliSchemaExport(program);
|
|
114
|
+
const lines: string[] = [
|
|
115
|
+
`# ${program.key} — CLI API reference`,
|
|
116
|
+
"",
|
|
117
|
+
schema.description,
|
|
118
|
+
"",
|
|
119
|
+
`Machine-readable export: \`${program.key} docs schema\``,
|
|
120
|
+
"",
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
if (schema.notes) {
|
|
124
|
+
lines.push(`> ${schema.notes}`, "");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
renderCommandNode(program.key, [], schema, lines);
|
|
128
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
129
|
+
}
|
package/src/docs/builtin.ts
CHANGED
|
@@ -37,6 +37,9 @@ export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
leaves.push(
|
|
40
|
+
docsLeaf(program, "schema", "Print the full command tree as JSON."),
|
|
41
|
+
docsLeaf(program, "api", "Print the command tree as markdown."),
|
|
42
|
+
docsLeaf(program, "skill", "Print generated Cursor SKILL.md content."),
|
|
40
43
|
docsLeaf(program, "all", "Print all bundled documentation combined."),
|
|
41
44
|
);
|
|
42
45
|
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -46,7 +46,13 @@ test("docs reserved when enabled", () => {
|
|
|
46
46
|
|
|
47
47
|
test("docs rejects reserved topic keys", () => {
|
|
48
48
|
const root = docsFixture();
|
|
49
|
-
root.docs!.topics.
|
|
49
|
+
root.docs!.topics.schema = { text: "nope" };
|
|
50
|
+
expect(() => cliValidateProgram(root)).toThrow(/reserved/);
|
|
51
|
+
delete root.docs!.topics.schema;
|
|
52
|
+
root.docs!.topics.skill = { text: "nope" };
|
|
53
|
+
expect(() => cliValidateProgram(root)).toThrow(/reserved/);
|
|
54
|
+
delete root.docs!.topics.skill;
|
|
55
|
+
root.docs!.topics.api = { text: "nope" };
|
|
50
56
|
expect(() => cliValidateProgram(root)).toThrow(/reserved/);
|
|
51
57
|
});
|
|
52
58
|
|
|
@@ -109,10 +115,50 @@ test("presentation includes docs subtree", () => {
|
|
|
109
115
|
);
|
|
110
116
|
});
|
|
111
117
|
|
|
118
|
+
test("docs schema prints JSON", async () => {
|
|
119
|
+
const result = await cliInvoke(docsFixture(), ["docs", "schema"]);
|
|
120
|
+
expect(result.exitCode).toBe(0);
|
|
121
|
+
const schema = JSON.parse(result.stdout);
|
|
122
|
+
expect(schema.key).toBe("myapp");
|
|
123
|
+
expect(schema.commands.some((c: { key: string }) => c.key === "run")).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("docs api prints markdown reference", async () => {
|
|
127
|
+
const result = await cliInvoke(docsFixture(), ["docs", "api"]);
|
|
128
|
+
expect(result.exitCode).toBe(0);
|
|
129
|
+
expect(result.stdout).toContain("# myapp — CLI API reference");
|
|
130
|
+
expect(result.stdout).toContain("## `myapp run`");
|
|
131
|
+
expect(result.stdout).toContain("Run something.");
|
|
132
|
+
expect(result.stdout).toContain("myapp docs schema");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("docs skill prints Cursor SKILL.md", async () => {
|
|
136
|
+
const result = await cliInvoke(docsFixture(), ["docs", "skill"]);
|
|
137
|
+
expect(result.exitCode).toBe(0);
|
|
138
|
+
expect(result.stdout).toContain("---");
|
|
139
|
+
expect(result.stdout).toContain("name: myapp");
|
|
140
|
+
expect(result.stdout).toContain("## Commands");
|
|
141
|
+
expect(result.stdout).not.toContain("mcp.json");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("presentation includes docs schema and skill", () => {
|
|
145
|
+
const presentation = cliPresentationRoot(docsFixture());
|
|
146
|
+
const docsNode = presentation.commands.find((c) => c.key === "docs");
|
|
147
|
+
expect(docsNode && "commands" in docsNode).toBe(true);
|
|
148
|
+
if (docsNode && "commands" in docsNode) {
|
|
149
|
+
expect(docsNode.commands.some((c) => c.key === "schema")).toBe(true);
|
|
150
|
+
expect(docsNode.commands.some((c) => c.key === "api")).toBe(true);
|
|
151
|
+
expect(docsNode.commands.some((c) => c.key === "skill")).toBe(true);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
112
155
|
test("completions offer docs subcommands", () => {
|
|
113
156
|
const bash = completionBashScript(cliPresentationRoot(docsFixture()));
|
|
114
157
|
expect(bash).toContain("docs) echo");
|
|
115
158
|
expect(bash).toContain("readme) echo");
|
|
159
|
+
expect(bash).toContain("schema) echo");
|
|
160
|
+
expect(bash).toContain("api) echo");
|
|
161
|
+
expect(bash).toContain("skill) echo");
|
|
116
162
|
});
|
|
117
163
|
|
|
118
164
|
test("generateMcpGuide includes schema URI", () => {
|
package/src/docs/mcp-guide.ts
CHANGED
|
@@ -84,7 +84,7 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
84
84
|
"|-----------|---------|",
|
|
85
85
|
"| `tools/list` | Callable tools for exposed leaf commands |",
|
|
86
86
|
"| `tools/call` | Runs handlers headlessly; JSON stdout becomes `structuredContent` when valid |",
|
|
87
|
-
`| Schema resource | \`${schemaUri}\` — same JSON as \`${root.key}
|
|
87
|
+
`| Schema resource | \`${schemaUri}\` — same JSON as \`${root.key} docs schema\` |`,
|
|
88
88
|
"",
|
|
89
89
|
"## Exposed tools",
|
|
90
90
|
"",
|
|
@@ -103,13 +103,13 @@ export function generateMcpGuide(root: CliProgram): string {
|
|
|
103
103
|
"## Tool arguments",
|
|
104
104
|
"",
|
|
105
105
|
"Arguments are a flat JSON object keyed by long option and positional names (hyphenated option names are valid keys).",
|
|
106
|
-
`See \`${root.key}
|
|
106
|
+
`See \`${root.key} docs schema\` or the schema resource for per-tool shapes.`,
|
|
107
107
|
"",
|
|
108
108
|
"Varargs positionals accept a JSON array or a comma-separated string.",
|
|
109
109
|
"",
|
|
110
110
|
"## Protocol",
|
|
111
111
|
"",
|
|
112
|
-
"Stdio NDJSON JSON-RPC. Help and
|
|
112
|
+
"Stdio NDJSON JSON-RPC. Help and `docs schema` are not available through tool calls.",
|
|
113
113
|
`Run \`${root.key} docs\` for bundled user documentation.`,
|
|
114
114
|
"",
|
|
115
115
|
);
|
package/src/docs/resolve.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { CliDocsConfig, CliProgram } from "../types.ts";
|
|
2
|
+
import { cliSchemaJson } from "../schema.ts";
|
|
3
|
+
import { generateSkillBundle } from "../skill/generate.ts";
|
|
4
|
+
import { generateApiGuide } from "./api-guide.ts";
|
|
2
5
|
import { generateMcpGuide } from "./mcp-guide.ts";
|
|
3
6
|
|
|
4
7
|
/** Built-in docs subcommand keys not allowed in `docs.topics`. */
|
|
5
|
-
export const DOCS_BUILTIN_TOPIC_KEYS = ["mcp", "all"] as const;
|
|
8
|
+
export const DOCS_BUILTIN_TOPIC_KEYS = ["mcp", "all", "schema", "api", "skill"] as const;
|
|
6
9
|
|
|
7
10
|
export type DocsBuiltinTopicKey = (typeof DOCS_BUILTIN_TOPIC_KEYS)[number];
|
|
8
11
|
|
|
@@ -81,8 +84,36 @@ export function combineAllDocs(program: CliProgram): string {
|
|
|
81
84
|
.join("\n\n---\n\n");
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
/** Writes CLI schema JSON to stdout (`docs schema`). */
|
|
88
|
+
export function printDocsSchema(program: CliProgram): void {
|
|
89
|
+
process.stdout.write(cliSchemaJson(program));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Writes markdown API reference to stdout (`docs api`). */
|
|
93
|
+
export function printDocsApi(program: CliProgram): void {
|
|
94
|
+
process.stdout.write(generateApiGuide(program));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Writes generated Cursor SKILL.md to stdout (`docs skill`). */
|
|
98
|
+
export function printDocsSkill(program: CliProgram): void {
|
|
99
|
+
const bundle = generateSkillBundle(program, "cursor");
|
|
100
|
+
process.stdout.write(`${bundle.skillMd}\n`);
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
/** Writes one docs topic (or `all`) to stdout. */
|
|
85
104
|
export function printDocsTopic(program: CliProgram, topic: string): void {
|
|
105
|
+
if (topic === "schema") {
|
|
106
|
+
printDocsSchema(program);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (topic === "api") {
|
|
110
|
+
printDocsApi(program);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (topic === "skill") {
|
|
114
|
+
printDocsSkill(program);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
86
117
|
const content = topic === "all" ? combineAllDocs(program) : docsTopicText(program, topic);
|
|
87
118
|
process.stdout.write(`${content}\n`);
|
|
88
119
|
}
|
package/src/help.ts
CHANGED
|
@@ -346,16 +346,12 @@ function usageLines(
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
/** Table rows for named options, including synthetic built-in rows. */
|
|
349
|
-
function rowsForOptions(defs: CliOption[], color: boolean
|
|
349
|
+
function rowsForOptions(defs: CliOption[], color: boolean): HelpRow[] {
|
|
350
350
|
const rows: HelpRow[] = [];
|
|
351
351
|
const helpLabel = color
|
|
352
352
|
? style.aquaBold("--help, ") + style.greenBright("-h")
|
|
353
353
|
: "--help, -h";
|
|
354
354
|
rows.push({ label: helpLabel, description: "Show help for this command." });
|
|
355
|
-
if (isRoot) {
|
|
356
|
-
const schemaLabel = color ? style.aquaBold("--schema") : "--schema";
|
|
357
|
-
rows.push({ label: schemaLabel, description: "Print the full command tree as JSON." });
|
|
358
|
-
}
|
|
359
355
|
for (const o of defs) {
|
|
360
356
|
const desc = o.required ? "(required) " + o.description : o.description;
|
|
361
357
|
rows.push({ label: cliOptionLabel(o, color), description: desc });
|
|
@@ -401,7 +397,7 @@ export function cliHelpRender(schema: CliRouter, helpPath: string[], useStderr:
|
|
|
401
397
|
).join("\n"),
|
|
402
398
|
);
|
|
403
399
|
|
|
404
|
-
const optBox = renderTableBox("Options", rowsForOptions(schema.options ?? [], color
|
|
400
|
+
const optBox = renderTableBox("Options", rowsForOptions(schema.options ?? [], color), hw, color);
|
|
405
401
|
if (optBox.length > 0) {
|
|
406
402
|
lines.push("");
|
|
407
403
|
lines.push(optBox.join("\n"));
|
|
@@ -450,7 +446,7 @@ export function cliHelpRender(schema: CliRouter, helpPath: string[], useStderr:
|
|
|
450
446
|
).join("\n"),
|
|
451
447
|
);
|
|
452
448
|
|
|
453
|
-
const optBox = renderTableBox("Options", rowsForOptions(node.options ?? [], color
|
|
449
|
+
const optBox = renderTableBox("Options", rowsForOptions(node.options ?? [], color), hw, color);
|
|
454
450
|
if (optBox.length > 0) {
|
|
455
451
|
lines.push("");
|
|
456
452
|
lines.push(optBox.join("\n"));
|
package/src/index.test.ts
CHANGED
|
@@ -500,8 +500,8 @@ test("leaf completion help prints correctly", async () => {
|
|
|
500
500
|
expect(stderr.toString()).toBe("");
|
|
501
501
|
});
|
|
502
502
|
|
|
503
|
-
test("
|
|
504
|
-
const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts
|
|
503
|
+
test("docs schema exports JSON for nested CLIs", async () => {
|
|
504
|
+
const { stdout, stderr, exitCode } = await $`bun run examples/nested.ts docs schema`.nothrow().quiet();
|
|
505
505
|
expect(exitCode).toBe(0);
|
|
506
506
|
expect(stderr.toString()).toBe("");
|
|
507
507
|
|
|
@@ -516,15 +516,20 @@ test("--schema exports JSON for nested CLIs", async () => {
|
|
|
516
516
|
expect(lookup.positionals[0].name).toBe("path");
|
|
517
517
|
});
|
|
518
518
|
|
|
519
|
-
test("
|
|
520
|
-
const { stdout, exitCode } = await $`bun run examples/minimal.ts
|
|
519
|
+
test("docs schema exports JSON for leaf roots", async () => {
|
|
520
|
+
const { stdout, exitCode } = await $`bun run examples/minimal.ts docs schema`.nothrow().quiet();
|
|
521
521
|
expect(exitCode).toBe(0);
|
|
522
522
|
|
|
523
523
|
const schema = JSON.parse(stdout.toString());
|
|
524
524
|
expect(schema.key).toBe("minimal.ts");
|
|
525
525
|
expect(schema.positionals[0].name).toBe("name");
|
|
526
526
|
expect(schema.options[0].name).toBe("verbose");
|
|
527
|
-
expect(schema.commands.map((c: { key: string }) => c.key)).toEqual([
|
|
527
|
+
expect(schema.commands.map((c: { key: string }) => c.key)).toEqual([
|
|
528
|
+
"completion",
|
|
529
|
+
"version",
|
|
530
|
+
"install",
|
|
531
|
+
"docs",
|
|
532
|
+
]);
|
|
528
533
|
});
|
|
529
534
|
|
|
530
535
|
test("version builtin prints program version", async () => {
|
|
@@ -540,10 +545,15 @@ test("leaf root help lists completion built-in", async () => {
|
|
|
540
545
|
expect(stdout.toString()).toContain("Generate the autocompletion script for shells.");
|
|
541
546
|
});
|
|
542
547
|
|
|
543
|
-
test("
|
|
544
|
-
const root= testProgram({
|
|
548
|
+
test("root --schema is no longer a flag", () => {
|
|
549
|
+
const root = testProgram({
|
|
545
550
|
key: "app",
|
|
551
|
+
version: "1.0.0",
|
|
546
552
|
description: "demo",
|
|
553
|
+
docs: {
|
|
554
|
+
enabled: true,
|
|
555
|
+
topics: { readme: { text: "# readme\n" } },
|
|
556
|
+
},
|
|
547
557
|
commands: [
|
|
548
558
|
{
|
|
549
559
|
key: "x",
|
|
@@ -553,8 +563,8 @@ test("parse recognizes --schema at the program root", () => {
|
|
|
553
563
|
],
|
|
554
564
|
});
|
|
555
565
|
cliValidateProgram(root);
|
|
556
|
-
const pr = parse(root, ["--schema"]);
|
|
557
|
-
expect(pr.kind).toBe(ParseKind.
|
|
566
|
+
const pr = parse(cliPresentationRoot(root), ["--schema"]);
|
|
567
|
+
expect(pr.kind).not.toBe(ParseKind.Ok);
|
|
558
568
|
});
|
|
559
569
|
|
|
560
570
|
test("cliSchemaJson omits handlers and completion built-ins", () => {
|
|
@@ -587,32 +597,15 @@ test("cliSchemaJson omits handlers and completion built-ins", () => {
|
|
|
587
597
|
expect(schema).not.toHaveProperty("handler");
|
|
588
598
|
});
|
|
589
599
|
|
|
590
|
-
test("
|
|
591
|
-
const root= testProgram({
|
|
592
|
-
key: "app",
|
|
593
|
-
description: "",
|
|
594
|
-
commands: [
|
|
595
|
-
{
|
|
596
|
-
key: "x",
|
|
597
|
-
description: "cmd",
|
|
598
|
-
options: [
|
|
599
|
-
{
|
|
600
|
-
name: "schema",
|
|
601
|
-
description: "",
|
|
602
|
-
kind: CliOptionKind.String,
|
|
603
|
-
},
|
|
604
|
-
],
|
|
605
|
-
handler: () => {},
|
|
606
|
-
},
|
|
607
|
-
],
|
|
608
|
-
});
|
|
609
|
-
expect(() => cliValidateProgram(root)).toThrow(/reserved for --schema/);
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
test("root help lists --schema built-in", () => {
|
|
613
|
-
const root= testProgram({
|
|
600
|
+
test("docs help lists schema, api, and skill subcommands", () => {
|
|
601
|
+
const root = testProgram({
|
|
614
602
|
key: "app",
|
|
603
|
+
version: "1.0.0",
|
|
615
604
|
description: "demo",
|
|
605
|
+
docs: {
|
|
606
|
+
enabled: true,
|
|
607
|
+
topics: { readme: { text: "# readme\n" } },
|
|
608
|
+
},
|
|
616
609
|
commands: [
|
|
617
610
|
{
|
|
618
611
|
key: "x",
|
|
@@ -621,14 +614,19 @@ test("root help lists --schema built-in", () => {
|
|
|
621
614
|
},
|
|
622
615
|
],
|
|
623
616
|
});
|
|
624
|
-
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
625
|
-
expect(help).toContain("
|
|
617
|
+
const help = cliHelpRender(cliPresentationRoot(root), ["docs"], false);
|
|
618
|
+
expect(help).toContain("schema");
|
|
626
619
|
expect(help).toContain("Print the full command tree as JSON.");
|
|
620
|
+
expect(help).toContain("api");
|
|
621
|
+
expect(help).toContain("markdown");
|
|
622
|
+
expect(help).toContain("skill");
|
|
623
|
+
expect(help).toContain("SKILL.md");
|
|
627
624
|
});
|
|
628
625
|
|
|
629
|
-
test("
|
|
630
|
-
const root= testProgram({
|
|
626
|
+
test("root help omits legacy --schema flag", () => {
|
|
627
|
+
const root = testProgram({
|
|
631
628
|
key: "app",
|
|
629
|
+
version: "1.0.0",
|
|
632
630
|
description: "demo",
|
|
633
631
|
commands: [
|
|
634
632
|
{
|
|
@@ -638,37 +636,10 @@ test("nested help omits --schema built-in", () => {
|
|
|
638
636
|
},
|
|
639
637
|
],
|
|
640
638
|
});
|
|
641
|
-
const help = cliHelpRender(cliPresentationRoot(root), [
|
|
639
|
+
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
642
640
|
expect(help).not.toContain("--schema");
|
|
643
641
|
});
|
|
644
642
|
|
|
645
|
-
test("completion scripts offer --schema at the program root only", () => {
|
|
646
|
-
const root= testProgram({
|
|
647
|
-
key: "myapp",
|
|
648
|
-
description: "",
|
|
649
|
-
commands: [
|
|
650
|
-
{
|
|
651
|
-
key: "stat",
|
|
652
|
-
description: "stats",
|
|
653
|
-
commands: [
|
|
654
|
-
{
|
|
655
|
-
key: "show",
|
|
656
|
-
description: "show",
|
|
657
|
-
handler: () => {},
|
|
658
|
-
},
|
|
659
|
-
],
|
|
660
|
-
},
|
|
661
|
-
],
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
const bash = completionBashScript(cliPresentationRoot(root));
|
|
665
|
-
expect(bash).toContain("A_myapp_0_opts+=('--schema')");
|
|
666
|
-
expect(bash).not.toContain("A_myapp_1_opts+=('--schema')");
|
|
667
|
-
|
|
668
|
-
const zsh = completionZshScript(cliPresentationRoot(root));
|
|
669
|
-
expect(zsh).toContain("'--schema:Print the full command tree as JSON.'");
|
|
670
|
-
});
|
|
671
|
-
|
|
672
643
|
const nestedMcpFixture = testProgram({
|
|
673
644
|
key: "nested.ts",
|
|
674
645
|
description: "Nested groups demo.",
|
package/src/index.ts
CHANGED
package/src/install/binary.ts
CHANGED
|
@@ -16,8 +16,13 @@ export interface BinaryInstallResult {
|
|
|
16
16
|
patchedZshRc: boolean;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/** Copies
|
|
20
|
-
export function installBinary(
|
|
19
|
+
/** Copies a binary to the install path and patches rc files when shells are detected. */
|
|
20
|
+
export function installBinary(
|
|
21
|
+
root: CliProgram,
|
|
22
|
+
paths: InstallPaths,
|
|
23
|
+
dry: boolean,
|
|
24
|
+
sourcePath: string = process.execPath,
|
|
25
|
+
): BinaryInstallResult {
|
|
21
26
|
const changed: string[] = [];
|
|
22
27
|
const shells = detectShells();
|
|
23
28
|
let patchedBashRc = false;
|
|
@@ -25,7 +30,7 @@ export function installBinary(root: CliProgram, paths: InstallPaths, dry: boolea
|
|
|
25
30
|
|
|
26
31
|
if (!dry) {
|
|
27
32
|
mkdirSync(paths.bindir, { recursive: true });
|
|
28
|
-
copyFileSync(
|
|
33
|
+
copyFileSync(sourcePath, paths.binaryPath);
|
|
29
34
|
}
|
|
30
35
|
changed.push(paths.binaryPath);
|
|
31
36
|
|