argsbarg 6.1.2 → 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 +65 -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 +52 -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 +431 -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/{json-leaf.test.ts → core/json-leaf.test.ts} +4 -4
  144. package/src/{leaf-inputs.test.ts → core/leaf-inputs.test.ts} +7 -7
  145. package/src/{leaf-inputs.ts → core/leaf-inputs.ts} +16 -12
  146. package/src/{parse.test.ts → core/parse.test.ts} +97 -109
  147. package/src/{parse.ts → core/parse.ts} +129 -31
  148. package/src/{schema.ts → core/schema.ts} +25 -13
  149. package/src/{types.ts → core/types.ts} +225 -35
  150. package/src/{validate.ts → core/validate.ts} +39 -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 +3 -3
  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 +36 -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 +9 -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} +159 -49
  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
@@ -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 "./builtins/export.ts";
6
- import { cliResolveNotes } from "./help.ts";
7
- import { visibleOptions } from "./hidden.ts";
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(["api", "completion", "configure", "docs", "mcp", "version"]);
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.hidden) {
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): CliSchemaExport {
79
+ export function cliSchemaExport(root: CliProgram): CliSchemaRootExport {
72
80
  const exported = exportCommand(root, root);
73
- if (!exported) {
74
- return {
75
- key: root.key,
76
- description: root.description,
77
- commands: exportPresentationBuiltins(root),
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 resolveSchemaNotes(exported, root.key);
92
+ return base;
81
93
  }
82
94
 
83
95
  export function cliSchemaJson(root: CliProgram): string {
@@ -4,12 +4,13 @@ It is the shared declarative model that parsing, validation, help, and completio
4
4
  read from, so the package has one source of truth.
5
5
  */
6
6
 
7
+ import type { AnyAppConfigSnapshot } from "~/config/context.ts";
7
8
  import type { CliContext } from "./context.ts";
8
9
 
9
10
  /**
10
11
  * How a leaf handler was dispatched.
11
12
  */
12
- export type CliInvocation = "cli" | "mcp" | "api";
13
+ export type CliInvocation = "cli" | "mcp" | "http";
13
14
 
14
15
  /**
15
16
  * Option kinds: presence (boolean flag), string (free-form text), number (strict double), enum (fixed choices), or json (parsed JSON object/array).
@@ -61,14 +62,49 @@ export enum CliFallbackMode {
61
62
  UnknownOnly = "unknownOnly",
62
63
  }
63
64
 
65
+ /**
66
+ * Per-surface CLI exposure (help, completions, cli-schema).
67
+ */
68
+ export interface CliCliExposureConfig {
69
+ /** When `false`, not callable via CLI (cascades to descendants). Default: true. */
70
+ enabled?: boolean;
71
+ /** Callable; omit from help, completions, and schema export. */
72
+ hidden?: boolean;
73
+ completions?: { enabled?: boolean; hidden?: boolean };
74
+ schema?: { enabled?: boolean; hidden?: boolean };
75
+ }
76
+
77
+ /** HTTP method for REST leaves. */
78
+ export type CliHttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
79
+
80
+ /**
81
+ * Per-node HTTP exposure and response defaults (routers: segment/enabled/hidden; leaves: full set).
82
+ */
83
+ export interface CliHttpExposureConfig {
84
+ /** When `false`, omit from HTTP route table. Default: exposed. */
85
+ enabled?: boolean;
86
+ /** Callable; omit from OpenAPI / route discovery. */
87
+ hidden?: boolean;
88
+ /** Override inferred HTTP verb. */
89
+ method?: CliHttpMethod;
90
+ /** URL path segment override (≠ `key`). */
91
+ segment?: string;
92
+ /** Default success HTTP status when handler omits `ctx.respond({ status })`. */
93
+ successStatus?: number;
94
+ /** Default success Content-Type (OpenAPI + response headers). */
95
+ successContentType?: string;
96
+ /** Default Content-Disposition for binary/downloads. */
97
+ contentDisposition?: string;
98
+ }
99
+
64
100
  /**
65
101
  * A named flag or value option (`--long`, `-short`), listed on command `options`.
66
102
  */
67
103
  export interface CliOption {
68
104
  /** Option name (e.g., "name", "verbose"). */
69
105
  name: string;
70
- /** When `true`, omit from help, schema, completions, and MCP tool inputSchema (still parseable). */
71
- hidden?: boolean;
106
+ /** Per-surface CLI exposure for this option. */
107
+ cli?: Pick<CliCliExposureConfig, "hidden">;
72
108
  /** Description shown in help. */
73
109
  description: string;
74
110
  /** Option kind: presence flag, string value, or number value. */
@@ -120,7 +156,7 @@ export interface CliPositional {
120
156
  argMax?: number;
121
157
  }
122
158
 
123
- /** Optional metadata for `mcp bundle` MCP Bundle output (program root `mcpServer.bundle` only). */
159
+ /** @experimental MCP bundle output options (program root `mcpServer.bundle` only). */
124
160
  export interface CliMcpBundleConfig {
125
161
  author?: {
126
162
  name: string;
@@ -136,10 +172,15 @@ export interface CliMcpBundleConfig {
136
172
  /**
137
173
  * Enables `myapp mcp` and MCP stdio server metadata (program root only).
138
174
  * Must include `enabled: true`; omit `mcpServer` entirely to disable MCP.
175
+ * @experimental
139
176
  */
140
177
  export interface CliMcpServerConfig {
141
178
  /** When `true`, enables the `mcp` built-in and MCP stdio server. */
142
179
  enabled: boolean;
180
+ /** MCP error response defaults. */
181
+ errors?: CliMcpServerErrorsConfig;
182
+ /** Observe-only hooks for JSON-RPC messages. */
183
+ hooks?: CliMcpWireHooks;
143
184
  /** When `true`, `mcp bundle` writes `dist/<key>.mcpb` for Claude Desktop. Default false. */
144
185
  mcpd?: boolean;
145
186
  /** When `true`, `mcp bundle` also writes `dist/claude-plugin/<name>.zip`. Default false. */
@@ -161,23 +202,69 @@ export interface CliMcpServerConfig {
161
202
  bundle?: CliMcpBundleConfig;
162
203
  }
163
204
 
205
+ /** JSON Schema for structured error responses (OpenAPI + HTTP/MCP error bodies). */
206
+ export type CliJsonSchema = Record<string, unknown>;
207
+
208
+ /** Wire-level HTTP hooks (observe-only; all requests including health and 404s). */
209
+ export interface CliHttpWireHooks {
210
+ onRequest?: (ctx: CliHttpWireContext) => void | Promise<void>;
211
+ onResponse?: (ctx: CliHttpWireContext & { status: number; durationMs: number }) => void | Promise<void>;
212
+ onError?: (ctx: CliHttpWireContext & { failureKind: InvokeFailureKind; error: unknown }) => void | Promise<void>;
213
+ }
214
+
215
+ /** Per-request HTTP wire context for {@link CliHttpWireHooks}. */
216
+ export interface CliHttpWireContext {
217
+ request: Request;
218
+ requestId: string;
219
+ clientIp: string;
220
+ path: string;
221
+ method: string;
222
+ }
223
+
224
+ /** Wire-level MCP hooks on JSON-RPC messages (observe-only). */
225
+ export interface CliMcpWireHooks {
226
+ onRequest?: (ctx: CliMcpWireContext) => void | Promise<void>;
227
+ onResponse?: (ctx: CliMcpWireContext & { durationMs: number }) => void | Promise<void>;
228
+ onError?: (ctx: CliMcpWireContext & { failureKind: InvokeFailureKind; error: unknown }) => void | Promise<void>;
229
+ }
230
+
231
+ /** Per-message MCP wire context for {@link CliMcpWireHooks}. */
232
+ export interface CliMcpWireContext {
233
+ rpcMethod: string;
234
+ requestId: string;
235
+ toolName?: string;
236
+ }
237
+
164
238
  /**
165
- * Enables `myapp api` and the HTTP tool server (program root only).
166
- * Must include `enabled: true`; omit `apiServer` entirely to disable HTTP.
239
+ * Enables `myapp http` and the HTTP tool server (program root only).
240
+ * Must include `enabled: true`; omit `httpServer` entirely to disable HTTP.
167
241
  */
168
- export interface CliApiServerConfig {
169
- /** When `true`, enables the `api` built-in and HTTP tool server. */
242
+ export interface CliHttpServerConfig {
243
+ /** When `true`, enables the `http` built-in and HTTP tool server. */
170
244
  enabled: boolean;
171
245
  /** Listen host (default: `127.0.0.1`). */
172
246
  host?: string;
173
247
  /** Listen port (default: `3000`). */
174
248
  port?: number;
249
+ /** Honor `X-Forwarded-For` for client IP in hooks and logs. */
250
+ trustProxy?: boolean;
251
+ /** HTTP error response defaults. */
252
+ errors?: { errorSchema?: CliJsonSchema; obscureUnexpected?: boolean };
253
+ /** Observe-only hooks for all HTTP requests. */
254
+ hooks?: CliHttpWireHooks;
255
+ }
256
+
257
+ /** MCP server error defaults. */
258
+ export interface CliMcpServerErrorsConfig {
259
+ errorSchema?: CliJsonSchema;
260
+ obscureUnexpected?: boolean;
175
261
  }
176
262
 
177
263
  /**
178
- * Declarative HTTP response hints for a leaf (used by OpenAPI and default headers).
264
+ * Declarative HTTP response hints passed to {@link apiSuccessResponse}.
265
+ * @internal Prefer `CliHttpExposureConfig` on the leaf.
179
266
  */
180
- export interface CliApiResponseConfig {
267
+ export interface CliHttpResponseConfig {
181
268
  /** Default success Content-Type (default: `application/json`). */
182
269
  contentType?: string;
183
270
  /** Optional Content-Disposition (e.g. `attachment; filename="invoice.pdf"`). */
@@ -219,15 +306,13 @@ export interface CliMcpResource {
219
306
  export interface CliMcpToolConfig {
220
307
  /** When `false`, omit from `tools/list` (default: exposed). */
221
308
  enabled?: boolean;
309
+ /** Callable; omit from `tools/list` and MCP tool schemas. */
310
+ hidden?: boolean;
222
311
  /**
223
312
  * Override the generated MCP tool description.
224
313
  * Default: auto-generated from command path and description.
225
314
  */
226
315
  description?: string;
227
- /**
228
- * @deprecated Set `outputSchema` on the leaf command instead.
229
- */
230
- outputSchema?: Record<string, unknown>;
231
316
  }
232
317
 
233
318
  /**
@@ -310,6 +395,7 @@ export interface CliCompletionConfig {
310
395
  enabled?: boolean;
311
396
  }
312
397
 
398
+ /** @experimental */
313
399
  export interface CliConfigureConfig {
314
400
  /** When `false`, hide/disable `configure` (default: enabled). */
315
401
  enabled?: boolean;
@@ -385,21 +471,16 @@ export interface CliDocsTopic {
385
471
  }
386
472
 
387
473
  /**
388
- * Enables `myapp docs` and bundled markdown topics (program root only).
389
- * Must include `enabled: true`; omit `docs` entirely to disable.
474
+ * Opt-out and optional topics for the `docs` built-in (program root only).
475
+ * Docs is enabled by default; set `enabled: false` to disable.
390
476
  */
391
477
  export interface CliDocsConfig {
392
- /** When `true`, enables the `docs` built-in command group. */
393
- enabled: boolean;
478
+ /** When `false`, hide/disable `docs` (default: enabled). */
479
+ enabled?: boolean;
394
480
  /** Router description for `myapp docs` (default: "Print bundled CLI documentation."). */
395
481
  description?: string;
396
- /**
397
- * Subcommand for bare `myapp docs` (maps to router `fallbackCommand`).
398
- * When omitted, uses the first key in `topics` (insertion order).
399
- */
400
- defaultTopic?: string;
401
- /** Topic key → bundled markdown. Reserved keys: `mcp`, `all` (supplied by the built-in). */
402
- topics: Record<string, CliDocsTopic>;
482
+ /** Optional consumer markdown topics. Reserved keys: `mcp`, `all` (supplied by the built-in). */
483
+ topics?: Record<string, CliDocsTopic>;
403
484
  }
404
485
 
405
486
  /**
@@ -408,8 +489,10 @@ export interface CliDocsConfig {
408
489
  export interface CliNodeBase {
409
490
  /** Program or command key (e.g., "myapp", "stat", "owner"). */
410
491
  key: string;
411
- /** When `true`, omit from help listings, schema, completions, and MCP tools (still invocable). */
412
- hidden?: boolean;
492
+ /** Per-surface CLI exposure. */
493
+ cli?: CliCliExposureConfig;
494
+ /** Per-surface HTTP exposure and response defaults. */
495
+ http?: CliHttpExposureConfig;
413
496
  /** Short description shown in help. */
414
497
  description: string;
415
498
  /** Additional notes shown in help (`{argsbarg:program}` → program key). */
@@ -436,13 +519,11 @@ export type CliLeaf = CliNodeBase & {
436
519
  positionals?: CliPositional[];
437
520
  /**
438
521
  * JSON Schema for structured stdout (e.g. with `--json` or MCP when the handler emits JSON).
439
- * Exported in `docs cli-schema`, `docs api`, and MCP `tools/list`; not validated at runtime yet.
522
+ * Exported in `docs cli-schema`, `docs cli`, and MCP `tools/list`; not validated at runtime yet.
440
523
  */
441
524
  outputSchema?: Record<string, unknown>;
442
525
  /** JSON Schema for MCP/HTTP tool arguments (flat object). */
443
526
  inputSchema?: Record<string, unknown>;
444
- /** Declarative HTTP response metadata (Content-Type, Content-Disposition). */
445
- apiResponse?: CliApiResponseConfig;
446
527
  /** Per-tool MCP exposure and metadata. */
447
528
  mcpTool?: CliMcpToolConfig;
448
529
  };
@@ -464,6 +545,109 @@ export type CliRouter = CliNodeBase & {
464
545
  */
465
546
  export type CliNode = CliLeaf | CliRouter;
466
547
 
548
+ /** Classified failure kind for invoke error pipeline and HTTP/MCP status mapping. */
549
+ export type InvokeFailureKind = "validation" | "help" | "unexpected" | "not_ready" | "missing_config" | "unknown_route";
550
+
551
+ /**
552
+ * Per-invocation context attached in hooks (e.g. DB handles, auth principals).
553
+ * Augment in app code: `declare module "argsbarg" { interface CliLocals { db: AppDb } }`.
554
+ */
555
+ export interface CliLocals {
556
+ /** Correlation id seeded before hooks run (HTTP/MCP wire id or generated UUID). */
557
+ requestId?: string;
558
+ }
559
+
560
+ /**
561
+ * Cross-request server state (HTTP/MCP runtime bag).
562
+ * Augment in app code: `declare module "argsbarg" { interface ServerState { db: AppDb } }`.
563
+ */
564
+ export interface ServerState {
565
+ /** Set when app config soft-validation fails at server start. */
566
+ configFileError?: string;
567
+ /** Short-TTL cache for readiness probe results. */
568
+ readinessCache?: {
569
+ at: number;
570
+ result: { ok: boolean; checks: Record<string, { ok: boolean; error?: string; missing?: string[] }> };
571
+ };
572
+ /** Last readiness probe result. */
573
+ readiness?: { ok: boolean; checks: Record<string, { ok: boolean; error?: string; missing?: string[] }> };
574
+ }
575
+
576
+ /** Cross-request mutable state created at HTTP/MCP server start. */
577
+ export interface ServerRuntime {
578
+ /** Mutable global bag (DB pool, degraded flags, readiness cache, etc.). */
579
+ state: ServerState;
580
+ program: CliProgram;
581
+ surface: "http" | "mcp";
582
+ }
583
+
584
+ /** Context for program-level invoke hooks (CLI, HTTP, MCP user commands). */
585
+ export interface InvokeHookContext {
586
+ invocation: CliInvocation;
587
+ path: string[];
588
+ pathParams: Record<string, string>;
589
+ opts: Record<string, string>;
590
+ /** Per-invocation bag; `beforeInvoke` may write. Framework seeds `requestId` before hooks run. */
591
+ locals: CliLocals;
592
+ runtime?: ServerRuntime;
593
+ appConfig: AnyAppConfigSnapshot;
594
+ http?: { request: Request; clientIp: string; requestId: string };
595
+ mcp?: { rpcMethod: string; toolName?: string; requestId: string };
596
+ }
597
+
598
+ /** Error hook context after failure classification. */
599
+ export interface ErrorHookContext extends InvokeHookContext {
600
+ failureKind: InvokeFailureKind;
601
+ error: unknown;
602
+ /** Default client-facing error before `formatError` override. */
603
+ clientError: ClientErrorOverride;
604
+ }
605
+
606
+ /** Client-facing error payload; `formatError` may return a partial override. */
607
+ export interface ClientErrorOverride {
608
+ message: string;
609
+ exitCode?: number;
610
+ }
611
+
612
+ /** Minimal invoke result passed to `afterInvoke` (see {@link Cli.invoke}). */
613
+ export interface CliInvokeHookResult {
614
+ kind: "ok" | "help" | "error";
615
+ exitCode: number;
616
+ failureKind?: InvokeFailureKind;
617
+ errorMsg?: string;
618
+ }
619
+
620
+ /** Program-level invoke and error hooks (skipped for builtins). */
621
+ export interface CliProgramHooks {
622
+ /** May mutate `locals`, `opts`, `args`; may throw. Skipped for builtins. */
623
+ beforeInvoke?: (ctx: InvokeHookContext) => void | Promise<void>;
624
+ afterInvoke?: (ctx: InvokeHookContext & { result: CliInvokeHookResult }) => void | Promise<void>;
625
+ /** Mutate client-facing error payload only. Runs before `onError`. */
626
+ formatError?: (ctx: ErrorHookContext) => ClientErrorOverride | undefined | Promise<ClientErrorOverride | undefined>;
627
+ /** Observe only — runs after `formatError`; may enrich `locals`. Never mutates client response. */
628
+ onError?: (ctx: ErrorHookContext) => void | Promise<void>;
629
+ }
630
+
631
+ /** Context for optional `program.readiness` (HTTP/MCP health only). */
632
+ export interface ReadinessContext {
633
+ program: CliProgram;
634
+ surface: "http" | "mcp";
635
+ appConfig: AnyAppConfigSnapshot;
636
+ runtime: ServerRuntime;
637
+ }
638
+
639
+ /** Framework logging defaults (ECS json or human text on stderr). */
640
+ export interface CliLogConfig {
641
+ /** `json` = ECS lines; `text` = human stderr lines. Default: `json`. */
642
+ format?: "json" | "text";
643
+ /** Tee stderr + append; relative paths resolve under the app config dir. */
644
+ file?: string;
645
+ /** Emit HTTP/MCP access logs. Default: true. */
646
+ access?: boolean;
647
+ /** Emit error events after the hook pipeline. Default: true. */
648
+ errors?: boolean;
649
+ }
650
+
467
651
  /**
468
652
  * Program root passed to {@link Cli}.
469
653
  * May be a leaf or router, plus optional program-level MCP and install config.
@@ -475,14 +659,20 @@ export type CliProgram = CliNode & {
475
659
  appConfig?: CliAppConfig;
476
660
  /** When set with `enabled: true`, enables the `mcp` built-in subcommand. */
477
661
  mcpServer?: CliMcpServerConfig;
478
- /** When set with `enabled: true`, enables the `api` built-in HTTP server. */
479
- apiServer?: CliApiServerConfig;
662
+ /** When set with `enabled: true`, enables the `http` built-in HTTP server. */
663
+ httpServer?: CliHttpServerConfig;
480
664
  /** Opt-out and defaults for `configure`. */
481
665
  configure?: CliConfigureConfig;
482
666
  /** Opt-out for shell completion generation (`completion bash|zsh|fish`). */
483
667
  completion?: CliCompletionConfig;
484
- /** When set with `enabled: true`, enables the `docs` built-in command group. */
668
+ /** Opt-out and optional topics for the `docs` built-in (default: enabled). */
485
669
  docs?: CliDocsConfig;
670
+ /** Invoke and error hooks for user commands on CLI, HTTP, and MCP. */
671
+ hooks?: CliProgramHooks;
672
+ /** Optional readiness probe for HTTP/MCP `GET /health/ready` only. */
673
+ readiness?: (ctx: ReadinessContext) => boolean | Promise<boolean>;
674
+ /** Framework logging (stderr + optional file). */
675
+ log?: CliLogConfig;
486
676
  };
487
677
 
488
678
  /** True when the node is a leaf (has a handler). */
@@ -500,9 +690,9 @@ export function isCliRouter(node: CliNode): node is CliRouter {
500
690
  return "commands" in node && Array.isArray(node.commands);
501
691
  }
502
692
 
503
- /** Resolves structured stdout schema from the leaf (prefers leaf field over legacy `mcpTool.outputSchema`). */
693
+ /** Resolves structured stdout schema from the leaf. */
504
694
  export function leafOutputSchema(leaf: CliLeaf): Record<string, unknown> | undefined {
505
- return leaf.outputSchema ?? leaf.mcpTool?.outputSchema;
695
+ return leaf.outputSchema;
506
696
  }
507
697
 
508
698
  /**
@@ -2,12 +2,12 @@
2
2
  This module validates CLI schemas before execution.
3
3
  */
4
4
 
5
- import { reservedCommandNames, resolveCapabilities } from "./capabilities.ts";
6
- import { reservedDocsTopicResourceUris } from "./docs/mcp-resources.ts";
7
- import { DOCS_BUILTIN_TOPIC_KEYS } from "./docs/resolve.ts";
5
+ import { AGENT_PAIRS, MCP_KEYS, mcpServerRequiredForArtifact } from "~/configure/artifacts/target-registry.ts";
6
+ import { reservedDocsTopicResourceUris } from "~/docs/mcp-resources.ts";
7
+ import { DOCS_BUILTIN_TOPIC_KEYS, docsEnabled } from "~/docs/resolve.ts";
8
+ import { resolveMcpSchemaUri } from "~/mcp/tools.ts";
9
+ import { reservedCommandNames, resolveCapabilities } from "~/runtime/capabilities.ts";
8
10
  import { validateFormatValue } from "./formats.ts";
9
- import { AGENT_PAIRS, MCP_KEYS, mcpServerRequiredForArtifact } from "./install/target-registry.ts";
10
- import { resolveMcpSchemaUri } from "./mcp/tools.ts";
11
11
  import {
12
12
  type CliLeaf,
13
13
  type CliNode,
@@ -24,20 +24,15 @@ import {
24
24
 
25
25
  /** Validates `docs` configuration on the program root. */
26
26
  function validateDocsConfig(docs: import("./types.ts").CliDocsConfig): void {
27
- const keys = Object.keys(docs.topics);
28
- if (keys.length === 0) {
29
- throw new CliSchemaValidationError("docs.topics must be non-empty");
30
- }
27
+ const topics = docs.topics ?? {};
28
+ const keys = Object.keys(topics);
31
29
  for (const reserved of DOCS_BUILTIN_TOPIC_KEYS) {
32
- if (reserved in docs.topics) {
30
+ if (reserved in topics) {
33
31
  throw new CliSchemaValidationError(`docs.topics key '${reserved}' is reserved for the docs built-in`);
34
32
  }
35
33
  }
36
- if (docs.defaultTopic !== undefined && !(docs.defaultTopic in docs.topics)) {
37
- throw new CliSchemaValidationError(`docs.defaultTopic '${docs.defaultTopic}' is not a key in docs.topics`);
38
- }
39
34
  for (const key of keys) {
40
- const text = docs.topics[key]?.text;
35
+ const text = topics[key]?.text;
41
36
  if (text === undefined || text.length === 0) {
42
37
  throw new CliSchemaValidationError(`docs.topics['${key}'].text must be non-empty`);
43
38
  }
@@ -181,15 +176,11 @@ export function cliValidateProgram(program: CliProgram): void {
181
176
  throw new CliSchemaValidationError("mcpServer requires enabled: true; omit mcpServer to disable MCP");
182
177
  }
183
178
 
184
- if (program.apiServer !== undefined && program.apiServer.enabled !== true) {
185
- throw new CliSchemaValidationError("apiServer requires enabled: true; omit apiServer to disable HTTP API");
186
- }
187
-
188
- if (program.docs !== undefined && program.docs.enabled !== true) {
189
- throw new CliSchemaValidationError("docs requires enabled: true; omit docs to disable bundled documentation");
179
+ if (program.httpServer !== undefined && program.httpServer.enabled !== true) {
180
+ throw new CliSchemaValidationError("httpServer requires enabled: true; omit httpServer to disable HTTP API");
190
181
  }
191
182
 
192
- if (program.docs?.enabled === true) {
183
+ if (docsEnabled(program) && program.docs?.topics !== undefined) {
193
184
  validateDocsConfig(program.docs);
194
185
  }
195
186
 
@@ -215,14 +206,20 @@ export function cliValidateProgram(program: CliProgram): void {
215
206
  walkNode(program, program, true);
216
207
  }
217
208
 
209
+ const PARAM_ROUTER_KEY = /^:[a-zA-Z][a-zA-Z0-9_]*$/;
210
+
211
+ function isParamRouterKey(key: string): boolean {
212
+ return key.startsWith(":");
213
+ }
214
+
218
215
  function walkNode(node: CliNode, program: CliProgram, isRoot: boolean): void {
219
216
  if (!isRoot) {
220
217
  const rogue = node as CliProgram;
221
218
  if (rogue.mcpServer !== undefined) {
222
219
  throw new CliSchemaValidationError(`mcpServer is only supported on the program root (not on ${node.key})`);
223
220
  }
224
- if (rogue.apiServer !== undefined) {
225
- throw new CliSchemaValidationError(`apiServer is only supported on the program root (not on ${node.key})`);
221
+ if (rogue.httpServer !== undefined) {
222
+ throw new CliSchemaValidationError(`httpServer is only supported on the program root (not on ${node.key})`);
226
223
  }
227
224
  if (rogue.configure !== undefined) {
228
225
  throw new CliSchemaValidationError(`configure is only supported on the program root (not on ${node.key})`);
@@ -251,12 +248,10 @@ function walkNode(node: CliNode, program: CliProgram, isRoot: boolean): void {
251
248
  }
252
249
  }
253
250
  const outputSchema = node.outputSchema;
254
- const legacyOutputSchema = node.mcpTool?.outputSchema;
255
- if (outputSchema !== undefined && legacyOutputSchema !== undefined) {
256
- throw new CliSchemaValidationError("Set outputSchema on the leaf only, not under mcpTool");
257
- }
258
- const resolved = outputSchema ?? legacyOutputSchema;
259
- if (resolved !== undefined && (typeof resolved !== "object" || resolved === null || Array.isArray(resolved))) {
251
+ if (
252
+ outputSchema !== undefined &&
253
+ (typeof outputSchema !== "object" || outputSchema === null || Array.isArray(outputSchema))
254
+ ) {
260
255
  throw new CliSchemaValidationError("outputSchema must be a JSON Schema object (not null or an array)");
261
256
  }
262
257
  const inputSchema = node.inputSchema;
@@ -308,11 +303,26 @@ function walkNode(node: CliNode, program: CliProgram, isRoot: boolean): void {
308
303
 
309
304
  if (isCliRouter(node)) {
310
305
  const seenNames = new Set<string>();
306
+ let paramRouterCount = 0;
311
307
  for (const child of node.commands) {
312
308
  if (seenNames.has(child.key)) {
313
309
  throw new CliSchemaValidationError(`Duplicate command name: ${child.key}`);
314
310
  }
315
311
  seenNames.add(child.key);
312
+ if (isParamRouterKey(child.key)) {
313
+ if (!PARAM_ROUTER_KEY.test(child.key)) {
314
+ throw new CliSchemaValidationError(
315
+ `Param router key '${child.key}' must match :[a-zA-Z][a-zA-Z0-9_]* on '${node.key}'`,
316
+ );
317
+ }
318
+ if (!isCliRouter(child)) {
319
+ throw new CliSchemaValidationError(`Param router '${child.key}' must be a router with subcommands`);
320
+ }
321
+ paramRouterCount++;
322
+ }
323
+ }
324
+ if (paramRouterCount > 1) {
325
+ throw new CliSchemaValidationError(`At most one param router per level on '${node.key}'`);
316
326
  }
317
327
 
318
328
  if (node.fallbackMode !== undefined && node.fallbackCommand === undefined) {
@@ -1,16 +1,8 @@
1
- import { docsSkillTopicDescription } from "../builtins/configure-copy.ts";
2
- import { resolveCapabilities } from "../capabilities.ts";
3
- import {
4
- CliFallbackMode,
5
- type CliLeaf,
6
- type CliOption,
7
- CliOptionKind,
8
- type CliProgram,
9
- type CliRouter,
10
- } from "../types.ts";
1
+ import { docsSkillTopicDescription } from "~/builtins/configure-copy.ts";
2
+ import { type CliLeaf, type CliOption, CliOptionKind, type CliProgram, type CliRouter } from "~/core/types.ts";
3
+ import { resolveCapabilities } from "~/runtime/capabilities.ts";
11
4
  import {
12
5
  DOCS_ROUTER_DESCRIPTION,
13
- docsEffectiveDefaultTopic,
14
6
  docsEnabled,
15
7
  docsIncludesHttpTopic,
16
8
  docsIncludesMcpTopic,
@@ -18,6 +10,7 @@ import {
18
10
  docsTopicDescription,
19
11
  docsUserTopicKeys,
20
12
  printDocsTopic,
13
+ resolveDocsConfig,
21
14
  } from "./resolve.ts";
22
15
  import { saveDocsTopic } from "./save.ts";
23
16
 
@@ -54,14 +47,12 @@ function docsRouterNotes(): string {
54
47
 
55
48
  /** Built-in `docs` router with bundled topic subcommands. */
56
49
  export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
57
- const docs = program.docs;
58
- if (!docs) {
59
- throw new Error("docs not enabled");
60
- }
50
+ const docs = resolveDocsConfig(program);
51
+ const topics = docs.topics ?? {};
61
52
  const leaves: CliLeaf[] = [];
62
53
 
63
54
  for (const key of docsUserTopicKeys(docs)) {
64
- const topic = docs.topics[key];
55
+ const topic = topics[key];
65
56
  if (!topic) {
66
57
  throw new Error(`docs topic missing: ${key}`);
67
58
  }
@@ -82,7 +73,7 @@ export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
82
73
 
83
74
  leaves.push(
84
75
  docsLeaf(program, "cli-schema", "Print the full CLI command tree as JSON."),
85
- docsLeaf(program, "api", "Print the full command reference as markdown."),
76
+ docsLeaf(program, "cli", "Print the full command reference as markdown."),
86
77
  docsLeaf(program, "skill", docsSkillTopicDescription(program, resolveCapabilities(program))),
87
78
  );
88
79
 
@@ -91,8 +82,6 @@ export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
91
82
  description: docs.description ?? DOCS_ROUTER_DESCRIPTION,
92
83
  notes: docsRouterNotes(),
93
84
  options: [DOCS_SAVE_OPTION],
94
- fallbackCommand: docsEffectiveDefaultTopic(docs),
95
- fallbackMode: CliFallbackMode.MissingOnly,
96
85
  commands: leaves,
97
86
  };
98
87
  }