argsbarg 6.1.1 → 6.1.3
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 +72 -1
- package/README.md +17 -19
- package/bin/argsbarg +10 -0
- package/docs/README.md +4 -3
- package/docs/ai-skills.md +4 -2
- package/docs/bundled-docs.md +50 -25
- package/docs/cli-program.md +76 -10
- package/docs/config-schema.md +10 -11
- package/docs/configure.md +2 -0
- package/docs/decisions.md +40 -0
- package/docs/developing.md +43 -5
- package/docs/http-server.md +171 -0
- package/docs/json-schema-subset.md +51 -0
- package/docs/mcp.md +4 -2
- package/docs/output-schema.md +55 -62
- package/examples/formats.ts +6 -6
- package/examples/full-example/Formula/full-example.rb +35 -0
- package/examples/full-example/README.md +20 -21
- package/examples/full-example/docs/README.md +1 -1
- package/examples/full-example/docs/cli-schema.json +1790 -98
- package/examples/full-example/docs/cli.md +1990 -0
- package/examples/full-example/docs/http.md +28 -29
- package/examples/full-example/docs/mcp.md +8 -22
- package/examples/full-example/docs/openapi.json +783 -50
- package/examples/full-example/docs/skill.md +10 -10
- package/examples/full-example/justfile +11 -1
- package/examples/full-example/src/commands/render-json/__generated__/RenderJsonInputSchema.json +15 -0
- package/examples/full-example/src/commands/render-json/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/render-json/command.test.ts +46 -0
- package/examples/full-example/src/commands/render-json/command.ts +30 -0
- package/examples/full-example/src/commands/render-json/types.ts +9 -0
- package/examples/full-example/src/commands/status/__generated__/StatusJsonOutputSchema.json +15 -0
- package/examples/full-example/src/commands/status/__generated__/index.ts +2 -2
- package/examples/full-example/src/commands/status/command.ts +5 -13
- package/examples/full-example/src/commands/status/types.ts +1 -14
- package/examples/full-example/src/commands/workspaces/__generated__/WorkspaceNameInputSchema.json +15 -0
- package/examples/full-example/src/commands/workspaces/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/workspaces/command.test.ts +58 -0
- package/examples/full-example/src/commands/workspaces/command.ts +94 -0
- package/examples/full-example/src/commands/workspaces/types.ts +6 -0
- package/examples/full-example/src/db/index.test.ts +86 -0
- package/examples/full-example/src/db/index.ts +101 -0
- package/examples/full-example/src/db/migrate.test.ts +35 -0
- package/examples/full-example/src/db/migrate.ts +69 -0
- package/examples/full-example/src/db/migrations/001_workspaces.sql +6 -0
- package/examples/full-example/src/db/tables/workspaces.ts +66 -0
- package/examples/full-example/src/program.ts +11 -36
- package/examples/full-example/src/types/argsbarg.d.ts +11 -0
- package/examples/full-example/src/types/md.d.ts +4 -0
- package/examples/full-example/tsconfig.json +5 -2
- package/examples/mcp-test.ts +1 -2
- package/examples/minimal.ts +1 -7
- package/examples/nested.ts +1 -2
- package/examples/option-required.ts +1 -1
- package/examples/servers.ts +4 -5
- package/index.d.ts +440 -136
- package/package.json +19 -2
- package/src/builtins/builtins.test.ts +7 -7
- package/src/builtins/completion-bash.ts +1 -1
- package/src/builtins/completion-fish.ts +1 -1
- package/src/builtins/completion-group.ts +4 -4
- package/src/builtins/completion-simulate-shared.ts +9 -0
- package/src/builtins/completion-zsh.ts +1 -1
- package/src/builtins/config.test.ts +3 -3
- package/src/builtins/config.ts +9 -9
- package/src/builtins/configure-copy.ts +2 -2
- package/src/builtins/configure.ts +4 -4
- package/src/builtins/dispatch.ts +19 -18
- package/src/builtins/export.ts +7 -5
- package/src/builtins/http.ts +68 -0
- package/src/builtins/mcp.ts +28 -4
- package/src/builtins/presentation.ts +6 -6
- package/src/builtins/registry.ts +6 -6
- package/src/builtins/scopes.ts +2 -2
- package/src/builtins/version.ts +1 -1
- package/src/cli-tool/full-example-capabilities.test.ts +10 -15
- package/src/cli-tool/main.ts +1 -1
- package/src/cli-tool/program.ts +3 -2
- package/src/cli-tool/prompt.ts +1 -1
- package/src/cli-tool/run-schemagen.ts +1 -3
- package/src/cli-tool/schemagen/cleanup.ts +6 -7
- package/src/cli-tool/schemagen/discover-schema-roots.ts +66 -120
- package/src/cli-tool/schemagen/index.ts +2 -2
- package/src/cli-tool/schemagen/names.ts +8 -13
- package/src/cli-tool/schemagen/run.ts +21 -28
- package/src/cli-tool/schemagen/schemagen.test.ts +136 -46
- package/src/config/bindings.test.ts +1 -1
- package/src/config/bindings.ts +1 -1
- package/src/config/bootstrap.test.ts +1 -1
- package/src/config/bootstrap.ts +36 -4
- package/src/config/context.test.ts +1 -1
- package/src/config/context.ts +1 -1
- package/src/config/entry.ts +1 -1
- package/src/config/file.test.ts +1 -1
- package/src/config/file.ts +3 -3
- package/src/config/manifest.ts +1 -1
- package/src/config/resolve.test.ts +1 -1
- package/src/config/resolve.ts +1 -1
- package/src/config/schema.ts +1 -1
- package/src/config/validate.ts +1 -1
- package/src/{install → configure/artifacts}/binary-placement.test.ts +1 -1
- package/src/{install → configure/artifacts}/binary-placement.ts +1 -1
- package/src/{install → configure/artifacts}/gh-release-update.ts +1 -1
- package/src/{install → configure/artifacts}/install-validate.test.ts +3 -3
- package/src/{install → configure/artifacts}/mcp-config.ts +1 -1
- package/src/{install → configure/artifacts}/mcp-opencode.test.ts +1 -1
- package/src/{install → configure/artifacts}/mcp-opencode.ts +1 -1
- package/src/{install → configure/artifacts}/paths.ts +5 -5
- package/src/configure/artifacts/plan.ts +24 -0
- package/src/{install → configure/artifacts}/status.test.ts +1 -1
- package/src/{install → configure/artifacts}/status.ts +2 -2
- package/src/{install → configure/artifacts}/target-base.ts +1 -1
- package/src/{install → configure/artifacts}/target-detect.ts +1 -1
- package/src/{install → configure/artifacts}/target-effective.ts +3 -9
- package/src/{install → configure/artifacts}/target-mcp-cli.ts +1 -1
- package/src/{install → configure/artifacts}/target-mcp-json.ts +1 -1
- package/src/{install → configure/artifacts}/target-plan-build.ts +2 -2
- package/src/{install → configure/artifacts}/target-registry.ts +2 -2
- package/src/{install → configure/artifacts}/target-scope.ts +3 -3
- package/src/{install → configure/artifacts}/target-skill.ts +1 -1
- package/src/{install → configure/artifacts}/target-types.ts +2 -2
- package/src/{install → configure/artifacts}/targets/app.ts +5 -5
- package/src/{install → configure/artifacts}/targets/chatgpt-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-code-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-desktop-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/codex-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/codex-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/configure.ts +5 -5
- package/src/{install → configure/artifacts}/targets/cursor-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/cursor-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/index.ts +1 -1
- package/src/{install → configure/artifacts}/targets/openclaw-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/openclaw-skill.ts +3 -3
- package/src/{install → configure/artifacts}/targets/opencode-mcp.ts +5 -5
- package/src/{install → configure/artifacts}/targets/opencode-skill.ts +3 -3
- package/src/{install → configure/artifacts}/targets.test.ts +1 -1
- package/src/{install → configure/artifacts}/uninstall.ts +1 -1
- package/src/configure/configure.test.ts +11 -11
- package/src/configure/index.ts +14 -14
- package/src/configure/prompt.ts +2 -2
- package/src/{context.ts → core/context.ts} +26 -20
- package/src/core/json-leaf.test.ts +156 -0
- package/src/{leaf-inputs.test.ts → core/leaf-inputs.test.ts} +7 -7
- package/src/{leaf-inputs.ts → core/leaf-inputs.ts} +76 -16
- package/src/{parse.test.ts → core/parse.test.ts} +97 -109
- package/src/{parse.ts → core/parse.ts} +173 -25
- package/src/{schema.ts → core/schema.ts} +25 -13
- package/src/{types.ts → core/types.ts} +238 -35
- package/src/{validate.ts → core/validate.ts} +51 -29
- package/src/docs/builtin.ts +8 -19
- package/src/docs/{api-guide.test.ts → cli-guide.test.ts} +21 -21
- package/src/docs/{api-guide.ts → cli-guide.ts} +45 -16
- package/src/docs/docs.test.ts +76 -41
- package/src/docs/http-guide.ts +37 -34
- package/src/docs/mcp-guide.ts +12 -14
- package/src/docs/mcp-resources.test.ts +2 -3
- package/src/docs/mcp-resources.ts +6 -11
- package/src/docs/resolve.ts +22 -30
- package/src/docs/save.ts +3 -3
- package/src/exports/cli.ts +47 -0
- package/src/exports/headless.ts +13 -0
- package/src/exports/http.ts +6 -0
- package/src/exports/mcp.ts +6 -0
- package/src/{headless.test.ts → headless/routing.test.ts} +3 -3
- package/src/{headless.ts → headless/routing.ts} +3 -3
- package/src/headless/tool-call.ts +114 -46
- package/src/help.test.ts +152 -0
- package/src/help.ts +54 -18
- package/src/hooks/builtin.ts +20 -0
- package/src/hooks/run.ts +142 -0
- package/src/http/openapi.ts +182 -0
- package/src/http/readiness.ts +78 -0
- package/src/{api → http}/result.ts +16 -5
- package/src/http/routes.ts +329 -0
- package/src/http/server.ts +225 -0
- package/src/index.ts +38 -25
- package/src/log/ecs.test.ts +43 -0
- package/src/log/ecs.ts +59 -0
- package/src/log/emitter.ts +166 -0
- package/src/mcp/bundle.ts +2 -2
- package/src/mcp/claude.test.ts +1 -1
- package/src/mcp/claude.ts +4 -4
- package/src/{hidden-mcpb.test.ts → mcp/hidden-mcpb.test.ts} +10 -9
- package/src/mcp/result.ts +2 -2
- package/src/mcp/server.ts +54 -6
- package/src/mcp/tools.ts +18 -20
- package/src/{capabilities.ts → runtime/capabilities.ts} +11 -11
- package/src/{cli-errors.ts → runtime/cli-errors.ts} +4 -4
- package/src/{cli.ts → runtime/cli.ts} +160 -50
- package/src/runtime/exposure.ts +102 -0
- package/src/{invoke.test.ts → runtime/invoke.test.ts} +31 -7
- package/src/server/context.ts +25 -0
- package/src/server/overrides.ts +112 -0
- package/src/skill/generate.ts +8 -8
- package/src/skill/hint.ts +1 -1
- package/src/skill/install.ts +2 -2
- package/src/skill/naming.ts +1 -1
- package/src/{test-fixtures.ts → test/fixtures.ts} +3 -2
- package/src/{config.integration.test.ts → test/integration/config.test.ts} +8 -8
- package/src/{api.integration.test.ts → test/integration/http.test.ts} +170 -67
- package/src/{mcp.integration.test.ts → test/integration/mcp.test.ts} +11 -57
- package/docs/api-server.md +0 -141
- package/examples/full-example/docs/api.md +0 -511
- package/examples/full-example/src/commands/status/__generated__/outputSchema.json +0 -28
- package/examples/full-example/src/config/__generated__/configSchema.json +0 -40
- package/examples/full-example/src/config/__generated__/index.ts +0 -5
- package/examples/full-example/src/config/types.ts +0 -24
- package/src/api/openapi.ts +0 -117
- package/src/api/server.ts +0 -120
- package/src/builtins/api.ts +0 -38
- package/src/hidden.ts +0 -30
- package/src/install/plan.ts +0 -53
- /package/src/{install → configure/artifacts}/detect-installed.ts +0 -0
- /package/src/{install → configure/artifacts}/gh-release-update.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-codex.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-codex.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-openclaw.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-openclaw.ts +0 -0
- /package/src/{install → configure/artifacts}/normalize-uninstall.ts +0 -0
- /package/src/{install → configure/artifacts}/normalize.ts +0 -0
- /package/src/{install → configure/artifacts}/opts.ts +0 -0
- /package/src/{install → configure/artifacts}/shell.ts +0 -0
- /package/src/{formats.test.ts → core/formats.test.ts} +0 -0
- /package/src/{formats.ts → core/formats.ts} +0 -0
- /package/src/{respond.ts → core/respond.ts} +0 -0
- /package/src/{types.test.ts → core/types.test.ts} +0 -0
- /package/src/{api → http}/schema-deref.test.ts +0 -0
- /package/src/{api → http}/schema-deref.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { Cli, CliContext, CliOptionKind } from "
|
|
3
|
-
import { LeafInputError,
|
|
2
|
+
import { Cli, CliContext, CliOptionKind } from "~/index";
|
|
3
|
+
import { LeafInputError, loadLeafInputs } from "./leaf-inputs.ts";
|
|
4
4
|
import type { CliProgram } from "./types.ts";
|
|
5
5
|
|
|
6
6
|
const invoiceSchema = {
|
|
@@ -46,13 +46,13 @@ function renderProgram(): CliProgram {
|
|
|
46
46
|
required: true,
|
|
47
47
|
},
|
|
48
48
|
],
|
|
49
|
-
handler: (ctx) => ctx.
|
|
49
|
+
handler: (ctx) => ctx.inputs,
|
|
50
50
|
},
|
|
51
51
|
],
|
|
52
52
|
} satisfies CliProgram;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
describe("
|
|
55
|
+
describe("loadLeafInputs / jsonOpt", () => {
|
|
56
56
|
test("reads Json option from MCP toolArgs", async () => {
|
|
57
57
|
const cli = new Cli(renderProgram());
|
|
58
58
|
const result = await cli.invoke(["render", "--format", "pdf"], {
|
|
@@ -212,10 +212,10 @@ describe("readLeafInputs / jsonOpt", () => {
|
|
|
212
212
|
expect(inputs[0]).toEqual(inputs[1]);
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
-
test("
|
|
215
|
+
test("loadLeafInputs throws LeafInputError when required Json is missing", () => {
|
|
216
216
|
const program = renderProgram();
|
|
217
217
|
const ctx = new CliContext("json-pipe-test", ["render"], [], { format: "pdf" }, program, "mcp", undefined, {});
|
|
218
|
-
expect(() =>
|
|
218
|
+
expect(() => loadLeafInputs(ctx)).toThrow(LeafInputError);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
test("omits undefined optional properties before inputSchema validation", async () => {
|
|
@@ -264,7 +264,7 @@ describe("readLeafInputs / jsonOpt", () => {
|
|
|
264
264
|
required: true,
|
|
265
265
|
},
|
|
266
266
|
],
|
|
267
|
-
handler: (ctx) => ctx.
|
|
267
|
+
handler: (ctx) => ctx.inputs,
|
|
268
268
|
},
|
|
269
269
|
],
|
|
270
270
|
} satisfies CliProgram;
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
Leaf input reads: Json options (flag, preloaded stdin, or toolArgs), optional inputSchema validation.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { validateConfigDocument } from "
|
|
5
|
+
import { validateConfigDocument } from "~/config/validate.ts";
|
|
6
|
+
import { isInteractiveTty } from "~/utils.ts";
|
|
6
7
|
import type { CliContext, CliLeafInputs } from "./context.ts";
|
|
7
8
|
import { collectOptionDefs } from "./parse.ts";
|
|
8
9
|
import type { CliInvocation, CliLeaf, CliNode, CliOption, CliProgram } from "./types.ts";
|
|
9
|
-
import { CliOptionKind, CliValueFormat, isCliLeaf, isCliRouter } from "./types.ts";
|
|
10
|
-
import { isInteractiveTty } from "./utils.ts";
|
|
10
|
+
import { CliOptionKind, CliValueFormat, isCliLeaf, isCliRouter, isJsonLeaf } from "./types.ts";
|
|
11
11
|
|
|
12
12
|
/** Thrown when leaf input resolution or validation fails. */
|
|
13
13
|
export class LeafInputError extends Error {
|
|
@@ -17,9 +17,12 @@ export class LeafInputError extends Error {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/** Internal key for piped stdin on `kind: "json"` leaves. */
|
|
21
|
+
export const JSON_LEAF_BODY_KEY = "__jsonLeafBody";
|
|
22
|
+
|
|
23
|
+
function resolveLeaf(program: CliProgram, commandPath: string[]): CliLeaf | undefined {
|
|
24
|
+
let node: CliNode = program;
|
|
25
|
+
for (const seg of commandPath) {
|
|
23
26
|
if (!isCliRouter(node)) return undefined;
|
|
24
27
|
const child = node.commands.find((c) => c.key === seg);
|
|
25
28
|
if (!child) return undefined;
|
|
@@ -28,6 +31,10 @@ function leafNode(ctx: CliContext): CliLeaf | undefined {
|
|
|
28
31
|
return isCliLeaf(node) ? node : undefined;
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
function leafNode(ctx: CliContext): CliLeaf | undefined {
|
|
35
|
+
return resolveLeaf(ctx.program, ctx.commandPath);
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
/** Parses a JSON string from a `--name` flag value. */
|
|
32
39
|
export function parseJsonText(raw: string, label: string): unknown {
|
|
33
40
|
const trimmed = raw.trim();
|
|
@@ -54,6 +61,23 @@ async function readPipedJsonStdin(): Promise<unknown> {
|
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
|
|
64
|
+
function jsonLeafBodyHelp(): string {
|
|
65
|
+
return "Missing JSON input: pass a JSON document as an argument or pipe to stdin";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function readPipedJsonStdinForJsonLeaf(): Promise<unknown> {
|
|
69
|
+
const raw = await new Response(Bun.stdin).text();
|
|
70
|
+
const trimmed = raw.trim();
|
|
71
|
+
if (trimmed.length === 0) {
|
|
72
|
+
throw new LeafInputError(jsonLeafBodyHelp());
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(trimmed);
|
|
76
|
+
} catch {
|
|
77
|
+
throw new LeafInputError("stdin is not valid JSON");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
57
81
|
function pipableJsonHelp(opt: CliOption): string {
|
|
58
82
|
return `Missing required option --${opt.name}: pass JSON via --${opt.name} '<json>' or pipe a JSON document to stdin`;
|
|
59
83
|
}
|
|
@@ -99,10 +123,17 @@ export async function preloadPipableJson(
|
|
|
99
123
|
commandPath: string[],
|
|
100
124
|
opts: Record<string, string>,
|
|
101
125
|
invocation: CliInvocation,
|
|
126
|
+
args: string[] = [],
|
|
102
127
|
): Promise<Record<string, unknown>> {
|
|
103
128
|
if (invocation !== "cli" || isInteractiveTty) {
|
|
104
129
|
return {};
|
|
105
130
|
}
|
|
131
|
+
|
|
132
|
+
const leaf = resolveLeaf(program, commandPath);
|
|
133
|
+
if (leaf && isJsonLeaf(leaf) && args.length === 0) {
|
|
134
|
+
return { [JSON_LEAF_BODY_KEY]: await readPipedJsonStdinForJsonLeaf() };
|
|
135
|
+
}
|
|
136
|
+
|
|
106
137
|
for (const opt of collectOptionDefs(program, commandPath)) {
|
|
107
138
|
if (opt.kind === CliOptionKind.Json && opt.pipable && !(opt.name in opts)) {
|
|
108
139
|
return { [opt.name]: await readPipedJsonStdin() };
|
|
@@ -150,6 +181,31 @@ export function loadLeafInputs(ctx: CliContext): CliLeafInputs {
|
|
|
150
181
|
const leaf = leafNode(ctx);
|
|
151
182
|
if (!leaf) return {};
|
|
152
183
|
|
|
184
|
+
if (isJsonLeaf(leaf)) {
|
|
185
|
+
let body: unknown;
|
|
186
|
+
if (ctx.toolArgs !== undefined) {
|
|
187
|
+
body = ctx.toolArgs;
|
|
188
|
+
} else if (ctx.args.length > 0) {
|
|
189
|
+
const [arg0] = ctx.args;
|
|
190
|
+
if (arg0 === undefined) {
|
|
191
|
+
throw new LeafInputError(jsonLeafBodyHelp());
|
|
192
|
+
}
|
|
193
|
+
body = parseJsonText(arg0, "JSON argument");
|
|
194
|
+
} else if (JSON_LEAF_BODY_KEY in ctx.preloadedJson) {
|
|
195
|
+
body = ctx.preloadedJson[JSON_LEAF_BODY_KEY];
|
|
196
|
+
} else {
|
|
197
|
+
throw new LeafInputError(jsonLeafBodyHelp());
|
|
198
|
+
}
|
|
199
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
|
200
|
+
throw new LeafInputError("JSON input must be a JSON object");
|
|
201
|
+
}
|
|
202
|
+
const out = body as CliLeafInputs;
|
|
203
|
+
if (leaf.inputSchema !== undefined) {
|
|
204
|
+
validateAgainstInputSchema(out, leaf.inputSchema);
|
|
205
|
+
}
|
|
206
|
+
return omitUndefinedInputs(out);
|
|
207
|
+
}
|
|
208
|
+
|
|
153
209
|
const out: CliLeafInputs = {};
|
|
154
210
|
const options = collectOptionDefs(ctx.program, ctx.commandPath);
|
|
155
211
|
|
|
@@ -168,6 +224,20 @@ export function loadLeafInputs(ctx: CliContext): CliLeafInputs {
|
|
|
168
224
|
}
|
|
169
225
|
}
|
|
170
226
|
|
|
227
|
+
for (const [name, value] of Object.entries(ctx.pathParams)) {
|
|
228
|
+
if (out[name] === undefined) {
|
|
229
|
+
out[name] = value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (ctx.toolArgs !== undefined) {
|
|
234
|
+
for (const [key, value] of Object.entries(ctx.toolArgs)) {
|
|
235
|
+
if (out[key] === undefined) {
|
|
236
|
+
out[key] = value;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
171
241
|
for (const opt of options) {
|
|
172
242
|
if (opt.required && out[opt.name] === undefined) {
|
|
173
243
|
if (opt.kind === CliOptionKind.Json && opt.pipable && ctx.invocation === "cli" && isInteractiveTty) {
|
|
@@ -183,13 +253,3 @@ export function loadLeafInputs(ctx: CliContext): CliLeafInputs {
|
|
|
183
253
|
|
|
184
254
|
return omitUndefinedInputs(out);
|
|
185
255
|
}
|
|
186
|
-
|
|
187
|
-
/** @deprecated Use {@link CliContext.inputs} or {@link loadLeafInputs} via `ctx.inputs`. */
|
|
188
|
-
export function readLeafInputs(ctx: CliContext): CliLeafInputs {
|
|
189
|
-
return ctx.inputs;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** @deprecated Use sync {@link readLeafInputs} — stdin is preloaded before the handler runs. */
|
|
193
|
-
export function readLeafInputsAsync(ctx: CliContext): Promise<CliLeafInputs> {
|
|
194
|
-
return Promise.resolve(readLeafInputs(ctx));
|
|
195
|
-
}
|
|
@@ -7,23 +7,23 @@ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "no
|
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { $ } from "bun";
|
|
10
|
-
import { completionBashScript, completionZshScript } from "
|
|
11
|
-
import { cliPresentationRoot } from "
|
|
12
|
-
import { cliHelpRender } from "
|
|
13
|
-
import { Cli, CliFallbackMode, CliOptionKind, type CliProgram } from "
|
|
14
|
-
import { applyShellEnv } from "
|
|
15
|
-
import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpSchemaUri } from "
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { generatePluginSkillBundle, generateSkillBundle } from "./skill/generate.ts";
|
|
19
|
-
import { cliSkillInstall } from "./skill/install.ts";
|
|
10
|
+
import { completionBashScript, completionZshScript } from "~/builtins";
|
|
11
|
+
import { cliPresentationRoot } from "~/builtins/presentation.ts";
|
|
12
|
+
import { cliHelpRender } from "~/help.ts";
|
|
13
|
+
import { Cli, CliFallbackMode, CliOptionKind, type CliProgram } from "~/index";
|
|
14
|
+
import { applyShellEnv } from "~/mcp/env.ts";
|
|
15
|
+
import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpSchemaUri } from "~/mcp/tools.ts";
|
|
16
|
+
import { generatePluginSkillBundle, generateSkillBundle } from "~/skill/generate.ts";
|
|
17
|
+
import { cliSkillInstall } from "~/skill/install.ts";
|
|
20
18
|
import {
|
|
21
19
|
enumMcpFixture,
|
|
22
20
|
nestedDocsFallbackFixture,
|
|
23
21
|
nestedMcpFixture,
|
|
24
22
|
testProgram,
|
|
25
23
|
varargsReadFixture,
|
|
26
|
-
} from "
|
|
24
|
+
} from "~/test/fixtures.ts";
|
|
25
|
+
import { ParseKind, parse, postParseValidate } from "./parse.ts";
|
|
26
|
+
import { cliSchemaJson } from "./schema.ts";
|
|
27
27
|
import { cliValidateProgram } from "./validate.ts";
|
|
28
28
|
|
|
29
29
|
/** Tests that bundled short presence flags. */
|
|
@@ -115,6 +115,85 @@ test("fallback missing or unknown root flags", () => {
|
|
|
115
115
|
expect(pr.opts.name).toBe("bob");
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
test("param router descent captures pathParams", () => {
|
|
119
|
+
const root = testProgram({
|
|
120
|
+
key: "app",
|
|
121
|
+
description: "",
|
|
122
|
+
commands: [
|
|
123
|
+
{
|
|
124
|
+
key: "workspaces",
|
|
125
|
+
description: "Workspaces.",
|
|
126
|
+
commands: [
|
|
127
|
+
{
|
|
128
|
+
key: ":id",
|
|
129
|
+
description: "One workspace.",
|
|
130
|
+
commands: [
|
|
131
|
+
{
|
|
132
|
+
key: "get",
|
|
133
|
+
description: "Get workspace.",
|
|
134
|
+
handler: () => {},
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
cliValidateProgram(root);
|
|
143
|
+
const pr = postParseValidate(root, parse(root, ["workspaces", "qa2", "get"]));
|
|
144
|
+
expect(pr.kind).toBe(ParseKind.Ok);
|
|
145
|
+
expect(pr.path).toEqual(["workspaces", ":id", "get"]);
|
|
146
|
+
expect(pr.pathParams).toEqual({ id: "qa2" });
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("cli.enabled cascade blocks disabled router", () => {
|
|
150
|
+
const root = testProgram({
|
|
151
|
+
key: "app",
|
|
152
|
+
description: "",
|
|
153
|
+
commands: [
|
|
154
|
+
{
|
|
155
|
+
key: "workspaces",
|
|
156
|
+
description: "Disabled.",
|
|
157
|
+
cli: { enabled: false },
|
|
158
|
+
commands: [
|
|
159
|
+
{
|
|
160
|
+
key: "list",
|
|
161
|
+
description: "List.",
|
|
162
|
+
handler: () => {},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
});
|
|
168
|
+
cliValidateProgram(root);
|
|
169
|
+
const pr = parse(root, ["workspaces", "list"]);
|
|
170
|
+
expect(pr.kind).toBe(ParseKind.Error);
|
|
171
|
+
expect(pr.errorMsg).toContain("Unknown command");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("completion match child emits param router fallback", () => {
|
|
175
|
+
const root = testProgram({
|
|
176
|
+
key: "app",
|
|
177
|
+
description: "Test",
|
|
178
|
+
commands: [
|
|
179
|
+
{
|
|
180
|
+
key: "workspaces",
|
|
181
|
+
description: "Workspaces.",
|
|
182
|
+
commands: [
|
|
183
|
+
{
|
|
184
|
+
key: ":id",
|
|
185
|
+
description: "One workspace.",
|
|
186
|
+
commands: [{ key: "get", description: "Get.", handler: () => {} }],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
});
|
|
192
|
+
cliValidateProgram(root);
|
|
193
|
+
const bash = completionBashScript(cliPresentationRoot(root));
|
|
194
|
+
expect(bash).toContain("*) echo");
|
|
195
|
+
});
|
|
196
|
+
|
|
118
197
|
test("unknown command", () => {
|
|
119
198
|
const root = testProgram({
|
|
120
199
|
key: "app",
|
|
@@ -551,7 +630,6 @@ test("root --schema is no longer a flag", () => {
|
|
|
551
630
|
version: "1.0.0",
|
|
552
631
|
description: "demo",
|
|
553
632
|
docs: {
|
|
554
|
-
enabled: true,
|
|
555
633
|
topics: { readme: { text: "# readme\n" } },
|
|
556
634
|
},
|
|
557
635
|
commands: [
|
|
@@ -639,98 +717,6 @@ test("cliSchemaExport resolves {argsbarg:program} in consumer notes", () => {
|
|
|
639
717
|
expect(schema.commands[0].notes).toBe("Run `myapp run` to start.");
|
|
640
718
|
});
|
|
641
719
|
|
|
642
|
-
/** Docs help lists schema, api, and skill subcommands. */
|
|
643
|
-
test("docs help lists schema, api, and skill subcommands", () => {
|
|
644
|
-
const root = testProgram({
|
|
645
|
-
key: "app",
|
|
646
|
-
version: "1.0.0",
|
|
647
|
-
description: "demo",
|
|
648
|
-
docs: {
|
|
649
|
-
enabled: true,
|
|
650
|
-
topics: { readme: { text: "# readme\n" } },
|
|
651
|
-
},
|
|
652
|
-
commands: [
|
|
653
|
-
{
|
|
654
|
-
key: "x",
|
|
655
|
-
description: "cmd",
|
|
656
|
-
handler: () => {},
|
|
657
|
-
},
|
|
658
|
-
],
|
|
659
|
-
});
|
|
660
|
-
const help = cliHelpRender(cliPresentationRoot(root), ["docs"], false);
|
|
661
|
-
expect(help).toContain("cli-schema");
|
|
662
|
-
expect(help).toContain("Print the full CLI command tree as JSON.");
|
|
663
|
-
expect(help).toContain("api");
|
|
664
|
-
expect(help).toContain("markdown");
|
|
665
|
-
expect(help).toContain("skill");
|
|
666
|
-
expect(help).toContain("reference agent SKILL");
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
/** Root help omits legacy --schema flag. */
|
|
670
|
-
test("root help omits legacy --schema flag", () => {
|
|
671
|
-
const root = testProgram({
|
|
672
|
-
key: "app",
|
|
673
|
-
version: "1.0.0",
|
|
674
|
-
description: "demo",
|
|
675
|
-
commands: [
|
|
676
|
-
{
|
|
677
|
-
key: "x",
|
|
678
|
-
description: "cmd",
|
|
679
|
-
handler: () => {},
|
|
680
|
-
},
|
|
681
|
-
],
|
|
682
|
-
});
|
|
683
|
-
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
684
|
-
expect(help).not.toContain("--schema");
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
/** Root help shows agent docs hint when docs enabled. */
|
|
688
|
-
test("root help shows agent docs hint when docs enabled", () => {
|
|
689
|
-
const root = testProgram({
|
|
690
|
-
key: "myapp",
|
|
691
|
-
version: "1.0.0",
|
|
692
|
-
description: "demo",
|
|
693
|
-
docs: {
|
|
694
|
-
enabled: true,
|
|
695
|
-
topics: { readme: { text: "# readme\n" } },
|
|
696
|
-
},
|
|
697
|
-
commands: [{ key: "run", description: "Run.", handler: () => {} }],
|
|
698
|
-
});
|
|
699
|
-
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
700
|
-
expect(help).toContain("For AI agents: `myapp docs skill`.");
|
|
701
|
-
expect(help).not.toContain("install --skill");
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
test("root help omits agent hint when docs disabled", () => {
|
|
705
|
-
const root = testProgram({
|
|
706
|
-
key: "myapp",
|
|
707
|
-
version: "1.0.0",
|
|
708
|
-
description: "demo",
|
|
709
|
-
commands: [{ key: "run", description: "Run.", handler: () => {} }],
|
|
710
|
-
});
|
|
711
|
-
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
712
|
-
expect(help).not.toContain("Agents:");
|
|
713
|
-
expect(help).not.toContain("docs skill");
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
/** Root help includes program notes and agent hint. */
|
|
717
|
-
test("root help includes program notes and agent hint", () => {
|
|
718
|
-
const root = testProgram({
|
|
719
|
-
key: "myapp",
|
|
720
|
-
version: "1.0.0",
|
|
721
|
-
description: "demo",
|
|
722
|
-
notes: "See `{argsbarg:program} docs readme` for the user guide.",
|
|
723
|
-
docs: {
|
|
724
|
-
enabled: true,
|
|
725
|
-
topics: { readme: { text: "# readme\n" } },
|
|
726
|
-
},
|
|
727
|
-
commands: [{ key: "run", description: "Run.", handler: () => {} }],
|
|
728
|
-
});
|
|
729
|
-
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
730
|
-
expect(help).toContain("See `myapp docs readme` for the user guide.");
|
|
731
|
-
expect(help).toContain("myapp docs skill");
|
|
732
|
-
});
|
|
733
|
-
|
|
734
720
|
test("Enum option inputSchema includes enum array", () => {
|
|
735
721
|
const tools = collectMcpTools(enumMcpFixture);
|
|
736
722
|
const run = tools.find((t) => t.name === "run")!;
|
|
@@ -814,14 +800,14 @@ test("cliValidateProgram rejects empty mcpServer", () => {
|
|
|
814
800
|
expect(() => cliValidateProgram(root)).toThrow(/mcpServer requires enabled: true/);
|
|
815
801
|
});
|
|
816
802
|
|
|
817
|
-
test("cliValidateProgram rejects empty
|
|
803
|
+
test("cliValidateProgram rejects empty httpServer", () => {
|
|
818
804
|
const root = testProgram({
|
|
819
805
|
key: "app",
|
|
820
806
|
description: "",
|
|
821
|
-
|
|
807
|
+
httpServer: {} as { enabled: boolean },
|
|
822
808
|
handler: () => {},
|
|
823
809
|
});
|
|
824
|
-
expect(() => cliValidateProgram(root)).toThrow(/
|
|
810
|
+
expect(() => cliValidateProgram(root)).toThrow(/httpServer requires enabled: true/);
|
|
825
811
|
});
|
|
826
812
|
|
|
827
813
|
test("resolveMcpSchemaUri uses sanitized root key", () => {
|
|
@@ -877,7 +863,7 @@ test("cliValidateProgram rejects resource URI matching auto docs topic", () => {
|
|
|
877
863
|
const root = testProgram({
|
|
878
864
|
key: "app",
|
|
879
865
|
description: "",
|
|
880
|
-
docs: {
|
|
866
|
+
docs: { topics: { readme: { text: "# r\n" } } },
|
|
881
867
|
mcpServer: {
|
|
882
868
|
enabled: true,
|
|
883
869
|
resources: [{ uri: "app://docs/readme", name: "dup", load: () => "" }],
|
|
@@ -908,7 +894,7 @@ test("allMcpResources includes docs topic resources", () => {
|
|
|
908
894
|
const root = testProgram({
|
|
909
895
|
key: "app",
|
|
910
896
|
description: "",
|
|
911
|
-
docs: {
|
|
897
|
+
docs: { topics: { readme: { text: "# hi\n" } } },
|
|
912
898
|
mcpServer: { enabled: true },
|
|
913
899
|
commands: [{ key: "leaf", description: "", handler: () => {} }],
|
|
914
900
|
});
|
|
@@ -971,6 +957,7 @@ test("nested fallback MissingOrUnknown routes unknown token to default", () => {
|
|
|
971
957
|
const root = testProgram({
|
|
972
958
|
key: "app",
|
|
973
959
|
description: "",
|
|
960
|
+
docs: { enabled: false },
|
|
974
961
|
commands: [
|
|
975
962
|
{
|
|
976
963
|
key: "docs",
|
|
@@ -1021,6 +1008,7 @@ test("cliValidateProgram rejects invalid nested fallbackCommand", () => {
|
|
|
1021
1008
|
const root = testProgram({
|
|
1022
1009
|
key: "app",
|
|
1023
1010
|
description: "",
|
|
1011
|
+
docs: { enabled: false },
|
|
1024
1012
|
commands: [
|
|
1025
1013
|
{
|
|
1026
1014
|
key: "docs",
|