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
|
@@ -7,6 +7,8 @@ It keeps handler dispatch and help on one parser so the CLI behavior stays consi
|
|
|
7
7
|
across every entry path.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { isCliCallable } from "~/runtime/exposure.ts";
|
|
11
|
+
import { fullStringIsDouble } from "~/utils.ts";
|
|
10
12
|
import { formatValidationError, validateFormatValue } from "./formats.ts";
|
|
11
13
|
import {
|
|
12
14
|
CliFallbackMode,
|
|
@@ -14,10 +16,11 @@ import {
|
|
|
14
16
|
type CliNode,
|
|
15
17
|
type CliOption,
|
|
16
18
|
CliOptionKind,
|
|
19
|
+
type CliRouter,
|
|
17
20
|
isCliLeaf,
|
|
18
21
|
isCliRouter,
|
|
22
|
+
isJsonLeaf,
|
|
19
23
|
} from "./types.ts";
|
|
20
|
-
import { fullStringIsDouble } from "./utils.ts";
|
|
21
24
|
|
|
22
25
|
// ── Parse Result ──────────────────────────────────────────────────────────────
|
|
23
26
|
|
|
@@ -47,6 +50,8 @@ export interface ParseResult {
|
|
|
47
50
|
helpExplicit: boolean;
|
|
48
51
|
/** Path segments for scoped help (empty for root help). */
|
|
49
52
|
helpPath: string[];
|
|
53
|
+
/** Path parameter values from `:param` router descent (e.g. `{ id: "qa2" }`). */
|
|
54
|
+
pathParams: Record<string, string>;
|
|
50
55
|
/** User-facing error message when `kind === Error`. */
|
|
51
56
|
errorMsg: string;
|
|
52
57
|
/** Help path to render next to an error (for contextual help). */
|
|
@@ -68,6 +73,24 @@ function findChild(cmds: CliNode[], name: string): CliNode | undefined {
|
|
|
68
73
|
return cmds.find((c) => c.key === name);
|
|
69
74
|
}
|
|
70
75
|
|
|
76
|
+
function isParamRouterKey(key: string): boolean {
|
|
77
|
+
return key.startsWith(":");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Static (non-`:param`) child by key. */
|
|
81
|
+
function findStaticChild(cmds: CliNode[], name: string): CliNode | undefined {
|
|
82
|
+
const ch = cmds.find((c) => c.key === name);
|
|
83
|
+
if (!ch || isParamRouterKey(ch.key)) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return ch;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The single `:param` router child at this level, if any. */
|
|
90
|
+
function findParamChild(cmds: CliNode[]): CliNode | undefined {
|
|
91
|
+
return cmds.find((c) => isParamRouterKey(c.key));
|
|
92
|
+
}
|
|
93
|
+
|
|
71
94
|
/** Resolves a long-option definition by name (without leading `--`). */
|
|
72
95
|
function findOptionByName(defs: CliOption[], name: string): CliOption | undefined {
|
|
73
96
|
return defs.find((o) => o.name === name);
|
|
@@ -237,6 +260,50 @@ export function collectOptionDefs(root: CliNode, path: string[]): CliOption[] {
|
|
|
237
260
|
return defs;
|
|
238
261
|
}
|
|
239
262
|
|
|
263
|
+
/** Fills `args` for a json leaf from `startIdx` (0 or 1 JSON string positional). */
|
|
264
|
+
function finishJsonLeaf(
|
|
265
|
+
_node: CliLeaf,
|
|
266
|
+
startIdx: number,
|
|
267
|
+
argv: string[],
|
|
268
|
+
path: string[],
|
|
269
|
+
opts: Record<string, string>,
|
|
270
|
+
pathParams: Record<string, string>,
|
|
271
|
+
): ParseResult {
|
|
272
|
+
let idx = startIdx;
|
|
273
|
+
const args: string[] = [];
|
|
274
|
+
|
|
275
|
+
if (idx < argv.length) {
|
|
276
|
+
const tok = argv[idx];
|
|
277
|
+
if (isHelpTok(tok)) {
|
|
278
|
+
return helpResult(path, true, pathParams);
|
|
279
|
+
}
|
|
280
|
+
if (tok === "--") {
|
|
281
|
+
return errorResult("Unexpected extra arguments", path, [], pathParams);
|
|
282
|
+
}
|
|
283
|
+
if (tok.startsWith("-")) {
|
|
284
|
+
return errorResult(`JSON commands do not accept options: ${tok}`, path, [], pathParams);
|
|
285
|
+
}
|
|
286
|
+
args.push(tok);
|
|
287
|
+
idx += 1;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (idx < argv.length) {
|
|
291
|
+
return errorResult("Unexpected extra arguments", path, [], pathParams);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
kind: ParseKind.Ok,
|
|
296
|
+
path,
|
|
297
|
+
opts,
|
|
298
|
+
args,
|
|
299
|
+
pathParams,
|
|
300
|
+
helpExplicit: false,
|
|
301
|
+
helpPath: [],
|
|
302
|
+
errorMsg: "",
|
|
303
|
+
errorHelpPath: [],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
240
307
|
/** Fills `args` for a leaf from `startIdx` according to `node.positionals`. */
|
|
241
308
|
function finishLeaf(
|
|
242
309
|
node: CliLeaf,
|
|
@@ -246,6 +313,7 @@ function finishLeaf(
|
|
|
246
313
|
opts: Record<string, string>,
|
|
247
314
|
optionDefs: CliOption[],
|
|
248
315
|
forcePositionalsIn: boolean,
|
|
316
|
+
pathParams: Record<string, string>,
|
|
249
317
|
): ParseResult {
|
|
250
318
|
let idx = startIdx;
|
|
251
319
|
const args: string[] = [];
|
|
@@ -256,7 +324,7 @@ function finishLeaf(
|
|
|
256
324
|
if (argMax === 1) {
|
|
257
325
|
if (argMin >= 1) {
|
|
258
326
|
if (idx >= argv.length) {
|
|
259
|
-
return errorResult(`Missing positional argument: ${p.name}`, path, []);
|
|
327
|
+
return errorResult(`Missing positional argument: ${p.name}`, path, [], pathParams);
|
|
260
328
|
}
|
|
261
329
|
args.push(argv[idx]);
|
|
262
330
|
idx += 1;
|
|
@@ -284,14 +352,14 @@ function finishLeaf(
|
|
|
284
352
|
}
|
|
285
353
|
|
|
286
354
|
if (!forcePositionals && isHelpTok(tok)) {
|
|
287
|
-
return helpResult(path, true);
|
|
355
|
+
return helpResult(path, true, pathParams);
|
|
288
356
|
}
|
|
289
357
|
|
|
290
358
|
if (!forcePositionals && tok.startsWith("-")) {
|
|
291
359
|
// MUST be false — lenient mode swallows unknown flags as positionals silently
|
|
292
360
|
const tailRep = consumeOptions(optionDefs, false, argv, idx, opts);
|
|
293
361
|
if (tailRep.report.err) {
|
|
294
|
-
return errorResult(tailRep.report.err, path, []);
|
|
362
|
+
return errorResult(tailRep.report.err, path, [], pathParams);
|
|
295
363
|
}
|
|
296
364
|
if (tailRep.report.sawDoubleDash) {
|
|
297
365
|
forcePositionals = true;
|
|
@@ -300,7 +368,7 @@ function finishLeaf(
|
|
|
300
368
|
idx = tailRep.nextIndex;
|
|
301
369
|
continue;
|
|
302
370
|
}
|
|
303
|
-
return errorResult(`Unexpected option token: ${tok}`, path, []);
|
|
371
|
+
return errorResult(`Unexpected option token: ${tok}`, path, [], pathParams);
|
|
304
372
|
}
|
|
305
373
|
|
|
306
374
|
args.push(tok);
|
|
@@ -315,27 +383,27 @@ function finishLeaf(
|
|
|
315
383
|
}
|
|
316
384
|
}
|
|
317
385
|
if (count < argMin) {
|
|
318
|
-
return errorResult(`Expected at least ${argMin} argument(s) for ${p.name}, got ${count}`, path, []);
|
|
386
|
+
return errorResult(`Expected at least ${argMin} argument(s) for ${p.name}, got ${count}`, path, [], pathParams);
|
|
319
387
|
}
|
|
320
388
|
}
|
|
321
389
|
|
|
322
390
|
if (idx < argv.length) {
|
|
323
391
|
if (forcePositionals) {
|
|
324
|
-
return errorResult("Unexpected extra arguments", path, []);
|
|
392
|
+
return errorResult("Unexpected extra arguments", path, [], pathParams);
|
|
325
393
|
}
|
|
326
394
|
|
|
327
395
|
if (isHelpTok(argv[idx])) {
|
|
328
|
-
return helpResult(path, true);
|
|
396
|
+
return helpResult(path, true, pathParams);
|
|
329
397
|
}
|
|
330
398
|
|
|
331
399
|
const tailRep = consumeOptions(optionDefs, false, argv, idx, opts);
|
|
332
400
|
if (tailRep.report.err) {
|
|
333
|
-
return errorResult(tailRep.report.err, path, []);
|
|
401
|
+
return errorResult(tailRep.report.err, path, [], pathParams);
|
|
334
402
|
}
|
|
335
403
|
idx = tailRep.nextIndex;
|
|
336
404
|
|
|
337
405
|
if (idx < argv.length) {
|
|
338
|
-
return errorResult("Unexpected extra arguments", path, []);
|
|
406
|
+
return errorResult("Unexpected extra arguments", path, [], pathParams);
|
|
339
407
|
}
|
|
340
408
|
}
|
|
341
409
|
|
|
@@ -344,6 +412,7 @@ function finishLeaf(
|
|
|
344
412
|
path,
|
|
345
413
|
opts,
|
|
346
414
|
args,
|
|
415
|
+
pathParams,
|
|
347
416
|
helpExplicit: false,
|
|
348
417
|
helpPath: [],
|
|
349
418
|
errorMsg: "",
|
|
@@ -354,12 +423,18 @@ function finishLeaf(
|
|
|
354
423
|
// ── Main Parser ───────────────────────────────────────────────────────────────
|
|
355
424
|
|
|
356
425
|
/** Builds a user-error parse result; `path` defaults to `errorHelpPath`. */
|
|
357
|
-
function errorResult(
|
|
426
|
+
function errorResult(
|
|
427
|
+
errorMsg: string,
|
|
428
|
+
errorHelpPath: string[] = [],
|
|
429
|
+
path: string[] = errorHelpPath,
|
|
430
|
+
pathParams: Record<string, string> = {},
|
|
431
|
+
): ParseResult {
|
|
358
432
|
return {
|
|
359
433
|
kind: ParseKind.Error,
|
|
360
434
|
path,
|
|
361
435
|
opts: {},
|
|
362
436
|
args: [],
|
|
437
|
+
pathParams,
|
|
363
438
|
helpExplicit: false,
|
|
364
439
|
helpPath: [],
|
|
365
440
|
errorMsg,
|
|
@@ -368,12 +443,13 @@ function errorResult(errorMsg: string, errorHelpPath: string[] = [], path: strin
|
|
|
368
443
|
}
|
|
369
444
|
|
|
370
445
|
/** Builds a help-request result for the current routing path. */
|
|
371
|
-
function helpResult(p: string[], explicit: boolean): ParseResult {
|
|
446
|
+
function helpResult(p: string[], explicit: boolean, pathParams: Record<string, string> = {}): ParseResult {
|
|
372
447
|
return {
|
|
373
448
|
kind: ParseKind.Help,
|
|
374
449
|
path: [],
|
|
375
450
|
opts: {},
|
|
376
451
|
args: [],
|
|
452
|
+
pathParams,
|
|
377
453
|
helpExplicit: explicit,
|
|
378
454
|
helpPath: p,
|
|
379
455
|
errorMsg: "",
|
|
@@ -381,13 +457,45 @@ function helpResult(p: string[], explicit: boolean): ParseResult {
|
|
|
381
457
|
};
|
|
382
458
|
}
|
|
383
459
|
|
|
460
|
+
type DescendResult = { ok: true; node: CliNode; cliEnabled: boolean } | { ok: false; error: ParseResult };
|
|
461
|
+
|
|
462
|
+
/** Descends into a static or `:param` child, updating path and pathParams. */
|
|
463
|
+
function descendChild(
|
|
464
|
+
parent: CliRouter,
|
|
465
|
+
tok: string,
|
|
466
|
+
path: string[],
|
|
467
|
+
pathParams: Record<string, string>,
|
|
468
|
+
cliEnabled: boolean,
|
|
469
|
+
): DescendResult {
|
|
470
|
+
const staticChild = findStaticChild(parent.commands, tok);
|
|
471
|
+
if (staticChild) {
|
|
472
|
+
if (!isCliCallable(staticChild, cliEnabled)) {
|
|
473
|
+
return { ok: false, error: errorResult(`Unknown subcommand: ${tok}`, path, [], pathParams) };
|
|
474
|
+
}
|
|
475
|
+
path.push(tok);
|
|
476
|
+
return { ok: true, node: staticChild, cliEnabled: isCliCallable(staticChild, cliEnabled) };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const paramChild = findParamChild(parent.commands);
|
|
480
|
+
if (paramChild && isCliCallable(paramChild, cliEnabled)) {
|
|
481
|
+
const paramName = paramChild.key.slice(1);
|
|
482
|
+
path.push(paramChild.key);
|
|
483
|
+
pathParams[paramName] = tok;
|
|
484
|
+
return { ok: true, node: paramChild, cliEnabled: isCliCallable(paramChild, cliEnabled) };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return { ok: false, error: errorResult(`Unknown subcommand: ${tok}`, path, [], pathParams) };
|
|
488
|
+
}
|
|
489
|
+
|
|
384
490
|
/**
|
|
385
491
|
* Parses `argv` against the program root, routing into subcommands and filling `opts` / `args`.
|
|
386
492
|
*/
|
|
387
493
|
export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
388
494
|
let i = 0;
|
|
389
495
|
const path: string[] = [];
|
|
496
|
+
const pathParams: Record<string, string> = {};
|
|
390
497
|
const opts: Record<string, string> = {};
|
|
498
|
+
let cliEnabled = true;
|
|
391
499
|
|
|
392
500
|
const rootLenient =
|
|
393
501
|
isCliRouter(root) &&
|
|
@@ -412,7 +520,10 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
412
520
|
let node: CliNode | undefined;
|
|
413
521
|
|
|
414
522
|
if (isCliLeaf(root)) {
|
|
415
|
-
|
|
523
|
+
if (isJsonLeaf(root)) {
|
|
524
|
+
return finishJsonLeaf(root, i, argv, path, opts, pathParams);
|
|
525
|
+
}
|
|
526
|
+
return finishLeaf(root, i, argv, path, opts, root.options ?? [], forcePositionals, pathParams);
|
|
416
527
|
}
|
|
417
528
|
|
|
418
529
|
if (i >= argv.length) {
|
|
@@ -431,12 +542,41 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
431
542
|
}
|
|
432
543
|
} else {
|
|
433
544
|
const peek = argv[i];
|
|
434
|
-
const childPick = !forcePositionals ?
|
|
545
|
+
const childPick = !forcePositionals ? findStaticChild(root.commands, peek) : undefined;
|
|
435
546
|
|
|
436
547
|
if (childPick !== undefined) {
|
|
548
|
+
if (!isCliCallable(childPick, cliEnabled)) {
|
|
549
|
+
return errorResult(`Unknown command: ${peek}`, path, [], pathParams);
|
|
550
|
+
}
|
|
437
551
|
cmdName = peek;
|
|
438
552
|
i += 1;
|
|
439
553
|
node = childPick;
|
|
554
|
+
cliEnabled = isCliCallable(childPick, cliEnabled);
|
|
555
|
+
} else if (!forcePositionals && isCliRouter(root)) {
|
|
556
|
+
const paramChild = findParamChild(root.commands);
|
|
557
|
+
if (paramChild && isCliCallable(paramChild, cliEnabled)) {
|
|
558
|
+
cmdName = paramChild.key;
|
|
559
|
+
pathParams[paramChild.key.slice(1)] = peek;
|
|
560
|
+
i += 1;
|
|
561
|
+
node = paramChild;
|
|
562
|
+
cliEnabled = isCliCallable(paramChild, cliEnabled);
|
|
563
|
+
} else {
|
|
564
|
+
const fallbackCommand = root.fallbackCommand;
|
|
565
|
+
const canRouteUnknown =
|
|
566
|
+
fallbackCommand !== undefined &&
|
|
567
|
+
((root.fallbackMode ?? CliFallbackMode.MissingOnly) === CliFallbackMode.MissingOrUnknown ||
|
|
568
|
+
(root.fallbackMode ?? CliFallbackMode.MissingOnly) === CliFallbackMode.UnknownOnly);
|
|
569
|
+
|
|
570
|
+
if (canRouteUnknown) {
|
|
571
|
+
cmdName = fallbackCommand;
|
|
572
|
+
node = findChild(root.commands, cmdName);
|
|
573
|
+
if (!node) {
|
|
574
|
+
return errorResult(`Unknown command: ${cmdName}`, path, [], pathParams);
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
return errorResult(`Unknown command: ${peek}`, path, [], pathParams);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
440
580
|
} else {
|
|
441
581
|
const fallbackCommand = root.fallbackCommand;
|
|
442
582
|
const canRouteUnknown =
|
|
@@ -473,6 +613,10 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
473
613
|
|
|
474
614
|
// Walk the command tree
|
|
475
615
|
while (true) {
|
|
616
|
+
if (isCliLeaf(current) && isJsonLeaf(current)) {
|
|
617
|
+
return finishJsonLeaf(current, i, argv, path, opts, pathParams);
|
|
618
|
+
}
|
|
619
|
+
|
|
476
620
|
if (!forcePositionals) {
|
|
477
621
|
const orep = consumeOptions(collectOptionDefs(root, path), false, argv, i, opts);
|
|
478
622
|
if (orep.report.err) {
|
|
@@ -485,7 +629,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
485
629
|
}
|
|
486
630
|
|
|
487
631
|
if (i < argv.length && !forcePositionals && isHelpTok(argv[i])) {
|
|
488
|
-
return helpResult(path, true);
|
|
632
|
+
return helpResult(path, true, pathParams);
|
|
489
633
|
}
|
|
490
634
|
|
|
491
635
|
if (i >= argv.length) {
|
|
@@ -497,28 +641,29 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
497
641
|
if (fbNode) {
|
|
498
642
|
path.push(fb);
|
|
499
643
|
current = fbNode;
|
|
644
|
+
cliEnabled = isCliCallable(fbNode, cliEnabled);
|
|
500
645
|
continue;
|
|
501
646
|
}
|
|
502
647
|
}
|
|
503
|
-
return helpResult(path, false);
|
|
648
|
+
return helpResult(path, false, pathParams);
|
|
504
649
|
}
|
|
505
650
|
if (!isCliLeaf(current)) {
|
|
506
|
-
return helpResult(path, false);
|
|
651
|
+
return helpResult(path, false, pathParams);
|
|
507
652
|
}
|
|
508
|
-
return finishLeaf(current, i, argv, path, opts, collectOptionDefs(root, path), forcePositionals);
|
|
653
|
+
return finishLeaf(current, i, argv, path, opts, collectOptionDefs(root, path), forcePositionals, pathParams);
|
|
509
654
|
}
|
|
510
655
|
|
|
511
656
|
const tok = argv[i];
|
|
512
657
|
if (!forcePositionals && tok.startsWith("-")) {
|
|
513
|
-
return errorResult(`Unexpected option token: ${tok}`, path);
|
|
658
|
+
return errorResult(`Unexpected option token: ${tok}`, path, [], pathParams);
|
|
514
659
|
}
|
|
515
660
|
|
|
516
661
|
if (!forcePositionals && isCliRouter(current)) {
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
662
|
+
const descended = descendChild(current, tok, path, pathParams, cliEnabled);
|
|
663
|
+
if (descended.ok) {
|
|
519
664
|
i += 1;
|
|
520
|
-
|
|
521
|
-
|
|
665
|
+
current = descended.node;
|
|
666
|
+
cliEnabled = descended.cliEnabled;
|
|
522
667
|
continue;
|
|
523
668
|
}
|
|
524
669
|
}
|
|
@@ -534,6 +679,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
534
679
|
if (fbNode) {
|
|
535
680
|
path.push(fb);
|
|
536
681
|
current = fbNode;
|
|
682
|
+
cliEnabled = isCliCallable(fbNode, cliEnabled);
|
|
537
683
|
continue;
|
|
538
684
|
}
|
|
539
685
|
}
|
|
@@ -541,13 +687,15 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
541
687
|
return errorResult(
|
|
542
688
|
forcePositionals ? `Expected subcommand but got positional: ${tok}` : `Unknown subcommand: ${tok}`,
|
|
543
689
|
path,
|
|
690
|
+
[],
|
|
691
|
+
pathParams,
|
|
544
692
|
);
|
|
545
693
|
}
|
|
546
694
|
|
|
547
695
|
if (!isCliLeaf(current)) {
|
|
548
|
-
return helpResult(path, false);
|
|
696
|
+
return helpResult(path, false, pathParams);
|
|
549
697
|
}
|
|
550
|
-
return finishLeaf(current, i, argv, path, opts, collectOptionDefs(root, path), forcePositionals);
|
|
698
|
+
return finishLeaf(current, i, argv, path, opts, collectOptionDefs(root, path), forcePositionals, pathParams);
|
|
551
699
|
}
|
|
552
700
|
}
|
|
553
701
|
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
This module serializes the CLI schema tree to JSON for machine-readable introspection.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { type CliSchemaExport, exportPresentationBuiltins } from "
|
|
6
|
-
import { cliResolveNotes } from "
|
|
7
|
-
import { visibleOptions } from "
|
|
5
|
+
import { type CliSchemaExport, exportPresentationBuiltins } from "~/builtins/export.ts";
|
|
6
|
+
import { cliResolveNotes } from "~/help.ts";
|
|
7
|
+
import { isCliSchemaHidden, visibleOptions } from "~/runtime/exposure.ts";
|
|
8
8
|
import { type CliNode, type CliProgram, isCliLeaf, isCliRouter, leafOutputSchema } from "./types.ts";
|
|
9
9
|
|
|
10
|
-
const RESERVED = new Set(["
|
|
10
|
+
const RESERVED = new Set(["http", "completion", "configure", "docs", "mcp", "version"]);
|
|
11
11
|
|
|
12
12
|
function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport | null {
|
|
13
|
-
if (cmd
|
|
13
|
+
if (isCliSchemaHidden(cmd)) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -35,6 +35,8 @@ function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport | null {
|
|
|
35
35
|
const outputSchema = leafOutputSchema(cmd);
|
|
36
36
|
if (outputSchema !== undefined) {
|
|
37
37
|
out.outputSchema = outputSchema;
|
|
38
|
+
} else if (cmd.http?.successContentType !== undefined) {
|
|
39
|
+
out.outputContentType = cmd.http.successContentType;
|
|
38
40
|
}
|
|
39
41
|
out.commands = exportPresentationBuiltins(root);
|
|
40
42
|
return out;
|
|
@@ -67,17 +69,27 @@ function resolveSchemaNotes(node: CliSchemaExport, appKey: string): CliSchemaExp
|
|
|
67
69
|
return out;
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/** JSON-safe command tree export (handlers omitted). */
|
|
73
|
+
export interface CliSchemaRootExport extends CliSchemaExport {
|
|
74
|
+
/** Program-level error JSON Schema when configured on `httpServer.errors` or `mcpServer.errors`. */
|
|
75
|
+
errorSchema?: Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
/** Returns the JSON-safe command tree (handlers omitted). */
|
|
71
|
-
export function cliSchemaExport(root: CliProgram):
|
|
79
|
+
export function cliSchemaExport(root: CliProgram): CliSchemaRootExport {
|
|
72
80
|
const exported = exportCommand(root, root);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
const errorSchema = root.httpServer?.errors?.errorSchema ?? root.mcpServer?.errors?.errorSchema;
|
|
82
|
+
const base: CliSchemaRootExport = !exported
|
|
83
|
+
? {
|
|
84
|
+
key: root.key,
|
|
85
|
+
description: root.description,
|
|
86
|
+
commands: exportPresentationBuiltins(root),
|
|
87
|
+
}
|
|
88
|
+
: resolveSchemaNotes(exported, root.key);
|
|
89
|
+
if (errorSchema !== undefined) {
|
|
90
|
+
base.errorSchema = errorSchema;
|
|
79
91
|
}
|
|
80
|
-
return
|
|
92
|
+
return base;
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
export function cliSchemaJson(root: CliProgram): string {
|