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.
Files changed (229) hide show
  1. package/CHANGELOG.md +72 -1
  2. package/README.md +17 -19
  3. package/bin/argsbarg +10 -0
  4. package/docs/README.md +4 -3
  5. package/docs/ai-skills.md +4 -2
  6. package/docs/bundled-docs.md +50 -25
  7. package/docs/cli-program.md +76 -10
  8. package/docs/config-schema.md +10 -11
  9. package/docs/configure.md +2 -0
  10. package/docs/decisions.md +40 -0
  11. package/docs/developing.md +43 -5
  12. package/docs/http-server.md +171 -0
  13. package/docs/json-schema-subset.md +51 -0
  14. package/docs/mcp.md +4 -2
  15. package/docs/output-schema.md +55 -62
  16. package/examples/formats.ts +6 -6
  17. package/examples/full-example/Formula/full-example.rb +35 -0
  18. package/examples/full-example/README.md +20 -21
  19. package/examples/full-example/docs/README.md +1 -1
  20. package/examples/full-example/docs/cli-schema.json +1790 -98
  21. package/examples/full-example/docs/cli.md +1990 -0
  22. package/examples/full-example/docs/http.md +28 -29
  23. package/examples/full-example/docs/mcp.md +8 -22
  24. package/examples/full-example/docs/openapi.json +783 -50
  25. package/examples/full-example/docs/skill.md +10 -10
  26. package/examples/full-example/justfile +11 -1
  27. package/examples/full-example/src/commands/render-json/__generated__/RenderJsonInputSchema.json +15 -0
  28. package/examples/full-example/src/commands/render-json/__generated__/index.ts +5 -0
  29. package/examples/full-example/src/commands/render-json/command.test.ts +46 -0
  30. package/examples/full-example/src/commands/render-json/command.ts +30 -0
  31. package/examples/full-example/src/commands/render-json/types.ts +9 -0
  32. package/examples/full-example/src/commands/status/__generated__/StatusJsonOutputSchema.json +15 -0
  33. package/examples/full-example/src/commands/status/__generated__/index.ts +2 -2
  34. package/examples/full-example/src/commands/status/command.ts +5 -13
  35. package/examples/full-example/src/commands/status/types.ts +1 -14
  36. package/examples/full-example/src/commands/workspaces/__generated__/WorkspaceNameInputSchema.json +15 -0
  37. package/examples/full-example/src/commands/workspaces/__generated__/index.ts +5 -0
  38. package/examples/full-example/src/commands/workspaces/command.test.ts +58 -0
  39. package/examples/full-example/src/commands/workspaces/command.ts +94 -0
  40. package/examples/full-example/src/commands/workspaces/types.ts +6 -0
  41. package/examples/full-example/src/db/index.test.ts +86 -0
  42. package/examples/full-example/src/db/index.ts +101 -0
  43. package/examples/full-example/src/db/migrate.test.ts +35 -0
  44. package/examples/full-example/src/db/migrate.ts +69 -0
  45. package/examples/full-example/src/db/migrations/001_workspaces.sql +6 -0
  46. package/examples/full-example/src/db/tables/workspaces.ts +66 -0
  47. package/examples/full-example/src/program.ts +11 -36
  48. package/examples/full-example/src/types/argsbarg.d.ts +11 -0
  49. package/examples/full-example/src/types/md.d.ts +4 -0
  50. package/examples/full-example/tsconfig.json +5 -2
  51. package/examples/mcp-test.ts +1 -2
  52. package/examples/minimal.ts +1 -7
  53. package/examples/nested.ts +1 -2
  54. package/examples/option-required.ts +1 -1
  55. package/examples/servers.ts +4 -5
  56. package/index.d.ts +440 -136
  57. package/package.json +19 -2
  58. package/src/builtins/builtins.test.ts +7 -7
  59. package/src/builtins/completion-bash.ts +1 -1
  60. package/src/builtins/completion-fish.ts +1 -1
  61. package/src/builtins/completion-group.ts +4 -4
  62. package/src/builtins/completion-simulate-shared.ts +9 -0
  63. package/src/builtins/completion-zsh.ts +1 -1
  64. package/src/builtins/config.test.ts +3 -3
  65. package/src/builtins/config.ts +9 -9
  66. package/src/builtins/configure-copy.ts +2 -2
  67. package/src/builtins/configure.ts +4 -4
  68. package/src/builtins/dispatch.ts +19 -18
  69. package/src/builtins/export.ts +7 -5
  70. package/src/builtins/http.ts +68 -0
  71. package/src/builtins/mcp.ts +28 -4
  72. package/src/builtins/presentation.ts +6 -6
  73. package/src/builtins/registry.ts +6 -6
  74. package/src/builtins/scopes.ts +2 -2
  75. package/src/builtins/version.ts +1 -1
  76. package/src/cli-tool/full-example-capabilities.test.ts +10 -15
  77. package/src/cli-tool/main.ts +1 -1
  78. package/src/cli-tool/program.ts +3 -2
  79. package/src/cli-tool/prompt.ts +1 -1
  80. package/src/cli-tool/run-schemagen.ts +1 -3
  81. package/src/cli-tool/schemagen/cleanup.ts +6 -7
  82. package/src/cli-tool/schemagen/discover-schema-roots.ts +66 -120
  83. package/src/cli-tool/schemagen/index.ts +2 -2
  84. package/src/cli-tool/schemagen/names.ts +8 -13
  85. package/src/cli-tool/schemagen/run.ts +21 -28
  86. package/src/cli-tool/schemagen/schemagen.test.ts +136 -46
  87. package/src/config/bindings.test.ts +1 -1
  88. package/src/config/bindings.ts +1 -1
  89. package/src/config/bootstrap.test.ts +1 -1
  90. package/src/config/bootstrap.ts +36 -4
  91. package/src/config/context.test.ts +1 -1
  92. package/src/config/context.ts +1 -1
  93. package/src/config/entry.ts +1 -1
  94. package/src/config/file.test.ts +1 -1
  95. package/src/config/file.ts +3 -3
  96. package/src/config/manifest.ts +1 -1
  97. package/src/config/resolve.test.ts +1 -1
  98. package/src/config/resolve.ts +1 -1
  99. package/src/config/schema.ts +1 -1
  100. package/src/config/validate.ts +1 -1
  101. package/src/{install → configure/artifacts}/binary-placement.test.ts +1 -1
  102. package/src/{install → configure/artifacts}/binary-placement.ts +1 -1
  103. package/src/{install → configure/artifacts}/gh-release-update.ts +1 -1
  104. package/src/{install → configure/artifacts}/install-validate.test.ts +3 -3
  105. package/src/{install → configure/artifacts}/mcp-config.ts +1 -1
  106. package/src/{install → configure/artifacts}/mcp-opencode.test.ts +1 -1
  107. package/src/{install → configure/artifacts}/mcp-opencode.ts +1 -1
  108. package/src/{install → configure/artifacts}/paths.ts +5 -5
  109. package/src/configure/artifacts/plan.ts +24 -0
  110. package/src/{install → configure/artifacts}/status.test.ts +1 -1
  111. package/src/{install → configure/artifacts}/status.ts +2 -2
  112. package/src/{install → configure/artifacts}/target-base.ts +1 -1
  113. package/src/{install → configure/artifacts}/target-detect.ts +1 -1
  114. package/src/{install → configure/artifacts}/target-effective.ts +3 -9
  115. package/src/{install → configure/artifacts}/target-mcp-cli.ts +1 -1
  116. package/src/{install → configure/artifacts}/target-mcp-json.ts +1 -1
  117. package/src/{install → configure/artifacts}/target-plan-build.ts +2 -2
  118. package/src/{install → configure/artifacts}/target-registry.ts +2 -2
  119. package/src/{install → configure/artifacts}/target-scope.ts +3 -3
  120. package/src/{install → configure/artifacts}/target-skill.ts +1 -1
  121. package/src/{install → configure/artifacts}/target-types.ts +2 -2
  122. package/src/{install → configure/artifacts}/targets/app.ts +5 -5
  123. package/src/{install → configure/artifacts}/targets/chatgpt-mcp.ts +2 -2
  124. package/src/{install → configure/artifacts}/targets/claude-code-mcp.ts +2 -2
  125. package/src/{install → configure/artifacts}/targets/claude-desktop-mcp.ts +2 -2
  126. package/src/{install → configure/artifacts}/targets/claude-skill.ts +2 -2
  127. package/src/{install → configure/artifacts}/targets/codex-mcp.ts +2 -2
  128. package/src/{install → configure/artifacts}/targets/codex-skill.ts +2 -2
  129. package/src/{install → configure/artifacts}/targets/configure.ts +5 -5
  130. package/src/{install → configure/artifacts}/targets/cursor-mcp.ts +2 -2
  131. package/src/{install → configure/artifacts}/targets/cursor-skill.ts +2 -2
  132. package/src/{install → configure/artifacts}/targets/index.ts +1 -1
  133. package/src/{install → configure/artifacts}/targets/openclaw-mcp.ts +2 -2
  134. package/src/{install → configure/artifacts}/targets/openclaw-skill.ts +3 -3
  135. package/src/{install → configure/artifacts}/targets/opencode-mcp.ts +5 -5
  136. package/src/{install → configure/artifacts}/targets/opencode-skill.ts +3 -3
  137. package/src/{install → configure/artifacts}/targets.test.ts +1 -1
  138. package/src/{install → configure/artifacts}/uninstall.ts +1 -1
  139. package/src/configure/configure.test.ts +11 -11
  140. package/src/configure/index.ts +14 -14
  141. package/src/configure/prompt.ts +2 -2
  142. package/src/{context.ts → core/context.ts} +26 -20
  143. package/src/core/json-leaf.test.ts +156 -0
  144. package/src/{leaf-inputs.test.ts → core/leaf-inputs.test.ts} +7 -7
  145. package/src/{leaf-inputs.ts → core/leaf-inputs.ts} +76 -16
  146. package/src/{parse.test.ts → core/parse.test.ts} +97 -109
  147. package/src/{parse.ts → core/parse.ts} +173 -25
  148. package/src/{schema.ts → core/schema.ts} +25 -13
  149. package/src/{types.ts → core/types.ts} +238 -35
  150. package/src/{validate.ts → core/validate.ts} +51 -29
  151. package/src/docs/builtin.ts +8 -19
  152. package/src/docs/{api-guide.test.ts → cli-guide.test.ts} +21 -21
  153. package/src/docs/{api-guide.ts → cli-guide.ts} +45 -16
  154. package/src/docs/docs.test.ts +76 -41
  155. package/src/docs/http-guide.ts +37 -34
  156. package/src/docs/mcp-guide.ts +12 -14
  157. package/src/docs/mcp-resources.test.ts +2 -3
  158. package/src/docs/mcp-resources.ts +6 -11
  159. package/src/docs/resolve.ts +22 -30
  160. package/src/docs/save.ts +3 -3
  161. package/src/exports/cli.ts +47 -0
  162. package/src/exports/headless.ts +13 -0
  163. package/src/exports/http.ts +6 -0
  164. package/src/exports/mcp.ts +6 -0
  165. package/src/{headless.test.ts → headless/routing.test.ts} +3 -3
  166. package/src/{headless.ts → headless/routing.ts} +3 -3
  167. package/src/headless/tool-call.ts +114 -46
  168. package/src/help.test.ts +152 -0
  169. package/src/help.ts +54 -18
  170. package/src/hooks/builtin.ts +20 -0
  171. package/src/hooks/run.ts +142 -0
  172. package/src/http/openapi.ts +182 -0
  173. package/src/http/readiness.ts +78 -0
  174. package/src/{api → http}/result.ts +16 -5
  175. package/src/http/routes.ts +329 -0
  176. package/src/http/server.ts +225 -0
  177. package/src/index.ts +38 -25
  178. package/src/log/ecs.test.ts +43 -0
  179. package/src/log/ecs.ts +59 -0
  180. package/src/log/emitter.ts +166 -0
  181. package/src/mcp/bundle.ts +2 -2
  182. package/src/mcp/claude.test.ts +1 -1
  183. package/src/mcp/claude.ts +4 -4
  184. package/src/{hidden-mcpb.test.ts → mcp/hidden-mcpb.test.ts} +10 -9
  185. package/src/mcp/result.ts +2 -2
  186. package/src/mcp/server.ts +54 -6
  187. package/src/mcp/tools.ts +18 -20
  188. package/src/{capabilities.ts → runtime/capabilities.ts} +11 -11
  189. package/src/{cli-errors.ts → runtime/cli-errors.ts} +4 -4
  190. package/src/{cli.ts → runtime/cli.ts} +160 -50
  191. package/src/runtime/exposure.ts +102 -0
  192. package/src/{invoke.test.ts → runtime/invoke.test.ts} +31 -7
  193. package/src/server/context.ts +25 -0
  194. package/src/server/overrides.ts +112 -0
  195. package/src/skill/generate.ts +8 -8
  196. package/src/skill/hint.ts +1 -1
  197. package/src/skill/install.ts +2 -2
  198. package/src/skill/naming.ts +1 -1
  199. package/src/{test-fixtures.ts → test/fixtures.ts} +3 -2
  200. package/src/{config.integration.test.ts → test/integration/config.test.ts} +8 -8
  201. package/src/{api.integration.test.ts → test/integration/http.test.ts} +170 -67
  202. package/src/{mcp.integration.test.ts → test/integration/mcp.test.ts} +11 -57
  203. package/docs/api-server.md +0 -141
  204. package/examples/full-example/docs/api.md +0 -511
  205. package/examples/full-example/src/commands/status/__generated__/outputSchema.json +0 -28
  206. package/examples/full-example/src/config/__generated__/configSchema.json +0 -40
  207. package/examples/full-example/src/config/__generated__/index.ts +0 -5
  208. package/examples/full-example/src/config/types.ts +0 -24
  209. package/src/api/openapi.ts +0 -117
  210. package/src/api/server.ts +0 -120
  211. package/src/builtins/api.ts +0 -38
  212. package/src/hidden.ts +0 -30
  213. package/src/install/plan.ts +0 -53
  214. /package/src/{install → configure/artifacts}/detect-installed.ts +0 -0
  215. /package/src/{install → configure/artifacts}/gh-release-update.test.ts +0 -0
  216. /package/src/{install → configure/artifacts}/mcp-codex.test.ts +0 -0
  217. /package/src/{install → configure/artifacts}/mcp-codex.ts +0 -0
  218. /package/src/{install → configure/artifacts}/mcp-openclaw.test.ts +0 -0
  219. /package/src/{install → configure/artifacts}/mcp-openclaw.ts +0 -0
  220. /package/src/{install → configure/artifacts}/normalize-uninstall.ts +0 -0
  221. /package/src/{install → configure/artifacts}/normalize.ts +0 -0
  222. /package/src/{install → configure/artifacts}/opts.ts +0 -0
  223. /package/src/{install → configure/artifacts}/shell.ts +0 -0
  224. /package/src/{formats.test.ts → core/formats.test.ts} +0 -0
  225. /package/src/{formats.ts → core/formats.ts} +0 -0
  226. /package/src/{respond.ts → core/respond.ts} +0 -0
  227. /package/src/{types.test.ts → core/types.test.ts} +0 -0
  228. /package/src/{api → http}/schema-deref.test.ts +0 -0
  229. /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 "./index.ts";
3
- import { LeafInputError, readLeafInputs } from "./leaf-inputs.ts";
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.readLeafInputs(),
49
+ handler: (ctx) => ctx.inputs,
50
50
  },
51
51
  ],
52
52
  } satisfies CliProgram;
53
53
  }
54
54
 
55
- describe("readLeafInputs / jsonOpt", () => {
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("readLeafInputs throws LeafInputError when required Json is missing", () => {
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(() => readLeafInputs(ctx)).toThrow(LeafInputError);
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.readLeafInputs(),
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 "./config/validate.ts";
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
- function leafNode(ctx: CliContext): CliLeaf | undefined {
21
- let node: CliNode = ctx.program;
22
- for (const seg of ctx.commandPath) {
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 "./builtins/index.ts";
11
- import { cliPresentationRoot } from "./builtins/presentation.ts";
12
- import { cliHelpRender } from "./help.ts";
13
- import { Cli, CliFallbackMode, CliOptionKind, type CliProgram } from "./index.ts";
14
- import { applyShellEnv } from "./mcp/env.ts";
15
- import { allMcpResources, collectMcpTools, mcpToolCallToArgv, resolveMcpSchemaUri } from "./mcp/tools.ts";
16
- import { ParseKind, parse, postParseValidate } from "./parse.ts";
17
- import { cliSchemaJson } from "./schema.ts";
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 "./test-fixtures.ts";
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 apiServer", () => {
803
+ test("cliValidateProgram rejects empty httpServer", () => {
818
804
  const root = testProgram({
819
805
  key: "app",
820
806
  description: "",
821
- apiServer: {} as { enabled: boolean },
807
+ httpServer: {} as { enabled: boolean },
822
808
  handler: () => {},
823
809
  });
824
- expect(() => cliValidateProgram(root)).toThrow(/apiServer requires enabled: true/);
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: { enabled: true, topics: { readme: { text: "# r\n" } } },
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: { enabled: true, topics: { readme: { text: "# hi\n" } } },
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",