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
@@ -3,7 +3,7 @@ Tests for docs/mcp-resources module behavior.
3
3
  */
4
4
 
5
5
  import { expect, test } from "bun:test";
6
- import type { CliProgram } from "../types.ts";
6
+ import type { CliProgram } from "~/core/types.ts";
7
7
  import {
8
8
  defaultDocsTopicResourceUri,
9
9
  docsMcpResources,
@@ -21,14 +21,13 @@ function fixture(opts?: { docs?: boolean; mcp?: boolean }): CliProgram {
21
21
  ...(docs
22
22
  ? {
23
23
  docs: {
24
- enabled: true,
25
24
  topics: {
26
25
  readme: { text: "# Readme\n", description: "User guide." },
27
26
  arch: { text: "# Arch\n" },
28
27
  },
29
28
  },
30
29
  }
31
- : {}),
30
+ : { docs: { enabled: false } }),
32
31
  ...(mcp ? { mcpServer: { enabled: true } } : {}),
33
32
  handler: () => {},
34
33
  };
@@ -2,8 +2,8 @@
2
2
  Auto MCP resources for user docs.topics when docs and MCP are both enabled.
3
3
  */
4
4
 
5
- import type { CliProgram } from "../types.ts";
6
- import { docsEnabled, docsTopicDescription, docsTopicText, docsUserTopicKeys } from "./resolve.ts";
5
+ import type { CliProgram } from "~/core/types.ts";
6
+ import { docsEnabled, docsTopicDescription, docsTopicText, docsUserTopicKeys, resolveDocsConfig } from "./resolve.ts";
7
7
 
8
8
  /** Default URI pattern for a docs topic MCP resource (`<mcpId>://docs/<topicKey>`). */
9
9
  export function defaultDocsTopicResourceUri(mcpId: string, topicKey: string): string {
@@ -31,12 +31,10 @@ export function docsMcpResources(program: CliProgram): {
31
31
  if (!docsEnabled(program) || program.mcpServer?.enabled !== true) {
32
32
  return [];
33
33
  }
34
- const docs = program.docs;
35
- if (!docs) {
36
- return [];
37
- }
34
+ const docs = resolveDocsConfig(program);
35
+ const topics = docs.topics ?? {};
38
36
  return docsUserTopicKeys(docs).map((key) => {
39
- const topic = docs.topics[key];
37
+ const topic = topics[key];
40
38
  if (!topic) {
41
39
  throw new Error(`docs topic missing: ${key}`);
42
40
  }
@@ -58,9 +56,6 @@ export function reservedDocsTopicResourceUris(program: CliProgram): string[] {
58
56
  if (!docsEnabled(program) || program.mcpServer?.enabled !== true) {
59
57
  return [];
60
58
  }
61
- const docs = program.docs;
62
- if (!docs) {
63
- return [];
64
- }
59
+ const docs = resolveDocsConfig(program);
65
60
  return docsUserTopicKeys(docs).map((key) => resolveDocsTopicResourceUri(program, key));
66
61
  }
@@ -1,13 +1,13 @@
1
- import { openApiJson } from "../api/openapi.ts";
2
- import { cliSchemaJson } from "../schema.ts";
3
- import { generateSkillBundle } from "../skill/generate.ts";
4
- import type { CliDocsConfig, CliProgram } from "../types.ts";
5
- import { generateApiGuide } from "./api-guide.ts";
1
+ import { cliSchemaJson } from "~/core/schema.ts";
2
+ import type { CliDocsConfig, CliProgram } from "~/core/types.ts";
3
+ import { openApiJson } from "~/http/openapi.ts";
4
+ import { generateSkillBundle } from "~/skill/generate.ts";
5
+ import { generateCliGuide } from "./cli-guide.ts";
6
6
  import { generateHttpGuide } from "./http-guide.ts";
7
7
  import { generateMcpGuide } from "./mcp-guide.ts";
8
8
 
9
9
  /** Built-in docs subcommand keys not allowed in `docs.topics`. */
10
- export const DOCS_BUILTIN_TOPIC_KEYS = ["http", "mcp", "all", "cli-schema", "api", "skill", "openapi"] as const;
10
+ export const DOCS_BUILTIN_TOPIC_KEYS = ["http", "mcp", "all", "cli-schema", "cli", "skill", "openapi"] as const;
11
11
 
12
12
  export type DocsBuiltinTopicKey = (typeof DOCS_BUILTIN_TOPIC_KEYS)[number];
13
13
 
@@ -16,28 +16,20 @@ export const DOCS_ROUTER_DESCRIPTION = "Print bundled CLI documentation.";
16
16
 
17
17
  /** Returns whether bundled docs are enabled on the program root. */
18
18
  export function docsEnabled(program: CliProgram): boolean {
19
- return program.docs?.enabled === true;
19
+ return program.docs?.enabled !== false;
20
20
  }
21
21
 
22
- /** User topic keys in declaration order. */
23
- export function docsUserTopicKeys(docs: CliDocsConfig): string[] {
24
- return Object.keys(docs.topics);
22
+ /** Normalized docs config with defaults applied. */
23
+ export function resolveDocsConfig(program: CliProgram): CliDocsConfig {
24
+ return {
25
+ description: program.docs?.description,
26
+ topics: program.docs?.topics ?? {},
27
+ };
25
28
  }
26
29
 
27
- /** Subcommand used when argv is bare `myapp docs`. */
28
- export function docsEffectiveDefaultTopic(docs: CliDocsConfig): string {
29
- if (docs.defaultTopic !== undefined) {
30
- return docs.defaultTopic;
31
- }
32
- const keys = docsUserTopicKeys(docs);
33
- if (keys.length === 0) {
34
- throw new Error("docs.topics must be non-empty");
35
- }
36
- const first = keys[0];
37
- if (first === undefined) {
38
- throw new Error("docs.topics must be non-empty");
39
- }
40
- return first;
30
+ /** User topic keys in declaration order. */
31
+ export function docsUserTopicKeys(docs: CliDocsConfig): string[] {
32
+ return Object.keys(docs.topics ?? {});
41
33
  }
42
34
 
43
35
  /** Whether MCP auto-guide topic is included. */
@@ -47,7 +39,7 @@ export function docsIncludesMcpTopic(program: CliProgram): boolean {
47
39
 
48
40
  /** Whether HTTP auto-guide topic is included. */
49
41
  export function docsIncludesHttpTopic(program: CliProgram): boolean {
50
- return docsEnabled(program) && program.apiServer?.enabled === true;
42
+ return docsEnabled(program) && program.httpServer?.enabled === true;
51
43
  }
52
44
 
53
45
  /** Whether OpenAPI export topic is included. */
@@ -69,8 +61,7 @@ export function docsTopicDescription(key: string, custom?: string): string {
69
61
 
70
62
  /** Markdown body for one docs topic key. */
71
63
  export function docsTopicText(program: CliProgram, topic: string): string {
72
- const docs = program.docs;
73
- if (!docs) {
64
+ if (!docsEnabled(program)) {
74
65
  throw new Error("docs not enabled");
75
66
  }
76
67
  if (topic === "mcp") {
@@ -85,7 +76,8 @@ export function docsTopicText(program: CliProgram, topic: string): string {
85
76
  }
86
77
  return generateHttpGuide(program);
87
78
  }
88
- const entry = docs.topics[topic];
79
+ const topics = program.docs?.topics ?? {};
80
+ const entry = topics[topic];
89
81
  if (!entry) {
90
82
  throw new Error(`Unknown docs topic '${topic}'.`);
91
83
  }
@@ -103,8 +95,8 @@ export function docsTopicContent(program: CliProgram, topic: string): string {
103
95
  }
104
96
  return openApiJson(program);
105
97
  }
106
- if (topic === "api") {
107
- return generateApiGuide(program);
98
+ if (topic === "cli") {
99
+ return generateCliGuide(program);
108
100
  }
109
101
  if (topic === "skill") {
110
102
  return `${generateSkillBundle(program, "cursor").skillMd}\n`;
package/src/docs/save.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { generatedFileHtmlComment, insertGeneratedHint } from "../skill/hint.ts";
4
- import type { CliProgram } from "../types.ts";
3
+ import type { CliProgram } from "~/core/types.ts";
4
+ import { generatedFileHtmlComment, insertGeneratedHint } from "~/skill/hint.ts";
5
5
  import { docsTopicContent } from "./resolve.ts";
6
6
 
7
7
  /** Relative output directory for `docs --save`. */
8
8
  export const DOCS_SAVE_DIR = "docs";
9
9
 
10
10
  /** Builtin docs topics generated by argsbarg (not consumer `docs.topics`). */
11
- export const DOCS_GENERATED_SAVE_TOPICS = ["mcp", "api", "skill"] as const;
11
+ export const DOCS_GENERATED_SAVE_TOPICS = ["mcp", "cli", "skill"] as const;
12
12
 
13
13
  /** Whether `--save` should prepend a generated-file hint (argsbarg writers only). */
14
14
  export function docsTopicIsGeneratedByArgsbarg(topic: string): boolean {
@@ -0,0 +1,47 @@
1
+ /*
2
+ Public CLI runtime export (`argsbarg/cli`).
3
+ */
4
+
5
+ export { displayAppConfigPath, resolveAppConfigPath } from "~/config/file.ts";
6
+ export type { CliLeafInputs } from "~/core/context.ts";
7
+ export { CliContext } from "~/core/context.ts";
8
+ export {
9
+ parseCommaList,
10
+ parseDate,
11
+ parseDateTime,
12
+ parseDurationMs,
13
+ } from "~/core/formats.ts";
14
+ export type {
15
+ CliAppConfig,
16
+ CliAppConfigEntry,
17
+ CliConfigureConfig,
18
+ CliConfigureTargets,
19
+ CliDocsConfig,
20
+ CliDocsTopic,
21
+ CliFallbackMode,
22
+ CliHandler,
23
+ CliHttpResponseConfig,
24
+ CliHttpServerConfig,
25
+ CliInvocation,
26
+ CliLeafKind,
27
+ CliMcpResource,
28
+ CliMcpServerConfig,
29
+ CliMcpToolConfig,
30
+ CliOption,
31
+ CliPositional,
32
+ CliProgram,
33
+ CliRespondBody,
34
+ CliRespondOptions,
35
+ InstallAgentIntegration,
36
+ InstallTargetSpec,
37
+ ResolvedInstallTarget,
38
+ } from "~/core/types.ts";
39
+ export {
40
+ CliOptionKind,
41
+ CliSchemaValidationError,
42
+ CliValueFormat,
43
+ isJsonLeaf,
44
+ } from "~/core/types.ts";
45
+ export { Cli, type CliInvokeKind, type CliInvokeResult } from "~/runtime/cli.ts";
46
+ export { cliErrWithHelp } from "~/runtime/cli-errors.ts";
47
+ export { isInteractiveTty } from "~/utils.ts";
@@ -0,0 +1,13 @@
1
+ /*
2
+ Headless routing helpers (`argsbarg/headless`).
3
+ */
4
+
5
+ export type { HeadlessContext } from "~/headless/routing.ts";
6
+ export {
7
+ formatDryRunMessage,
8
+ requireYesInNonTty,
9
+ shouldRunHeadless,
10
+ shouldRunHeadlessWithPositionals,
11
+ shouldRunHeadlessWithYes,
12
+ wantsExplicitJson,
13
+ } from "~/headless/routing.ts";
@@ -0,0 +1,6 @@
1
+ /*
2
+ HTTP/OpenAPI export (`argsbarg/http`).
3
+ */
4
+
5
+ export { generateOpenApi, openApiJson } from "~/http/openapi.ts";
6
+ export { handleApiRequest, httpServeHttp, resolveHttpListenAddress } from "~/http/server.ts";
@@ -0,0 +1,6 @@
1
+ /*
2
+ MCP bundle export (`argsbarg/mcp`). @experimental
3
+ */
4
+
5
+ export type { McpBundlePaths, PackMcpBundleOpts } from "~/mcp/bundle.ts";
6
+ export { defaultMcpBundlePaths, generateMcpManifest, packMcpBundle } from "~/mcp/bundle.ts";
@@ -10,18 +10,18 @@ import {
10
10
  shouldRunHeadlessWithPositionals,
11
11
  shouldRunHeadlessWithYes,
12
12
  wantsExplicitJson,
13
- } from "./headless.ts";
13
+ } from "./routing.ts";
14
14
 
15
15
  test("wantsExplicitJson includes MCP and API invocation", () => {
16
16
  expect(wantsExplicitJson({ invocation: "cli" }, false)).toBe(false);
17
17
  expect(wantsExplicitJson({ invocation: "mcp" }, false)).toBe(true);
18
- expect(wantsExplicitJson({ invocation: "api" }, false)).toBe(true);
18
+ expect(wantsExplicitJson({ invocation: "http" }, false)).toBe(true);
19
19
  expect(wantsExplicitJson({ invocation: "cli" }, true)).toBe(true);
20
20
  });
21
21
 
22
22
  test("shouldRunHeadless is true for MCP, API, and json", () => {
23
23
  expect(shouldRunHeadless({ invocation: "mcp" }, false)).toBe(true);
24
- expect(shouldRunHeadless({ invocation: "api" }, false)).toBe(true);
24
+ expect(shouldRunHeadless({ invocation: "http" }, false)).toBe(true);
25
25
  expect(shouldRunHeadless({ invocation: "cli" }, true)).toBe(true);
26
26
  expect(shouldRunHeadless({ invocation: "cli" }, false, true)).toBe(true);
27
27
  expect(shouldRunHeadless({ invocation: "cli" }, false, false, false)).toBe(true);
@@ -1,12 +1,12 @@
1
- import type { CliContext } from "./context.ts";
2
- import { isInteractiveTty } from "./utils.ts";
1
+ import type { CliContext } from "~/core/context.ts";
2
+ import { isInteractiveTty } from "~/utils.ts";
3
3
 
4
4
  /** Minimal context for headless routing helpers. */
5
5
  export type HeadlessContext = Pick<CliContext, "invocation">;
6
6
 
7
7
  /** True when the handler was invoked via MCP or HTTP API. */
8
8
  function isToolInvocation(invocation: CliContext["invocation"]): boolean {
9
- return invocation === "mcp" || invocation === "api";
9
+ return invocation === "mcp" || invocation === "http";
10
10
  }
11
11
 
12
12
  /** True when `--json` was passed or the handler was invoked headlessly over MCP/HTTP. */
@@ -2,13 +2,16 @@
2
2
  Shared headless tool dispatch for MCP and HTTP: config bootstrap, argv conversion, and invoke.
3
3
  */
4
4
 
5
- import { apiErrorResponse, apiSuccessResponse, firstErrorLine } from "../api/result.ts";
6
- import type { Cli, CliInvokeResult } from "../cli.ts";
7
- import { bootstrapAppConfig } from "../config/bootstrap.ts";
8
- import { formatMcpMissingConfigMessage, missingRequiredConfig } from "../config/resolve.ts";
9
- import { buildToolCallSuccessFromResponse } from "../mcp/result.ts";
10
- import { collectMcpTools, type McpToolDef, mcpToolCallToArgv } from "../mcp/tools.ts";
11
- import type { CliInvocation, CliProgram } from "../types.ts";
5
+ import { bootstrapAppConfig } from "~/config/bootstrap.ts";
6
+ import { formatMcpMissingConfigMessage, missingRequiredConfig } from "~/config/resolve.ts";
7
+ import type { CliInvocation, CliProgram, InvokeFailureKind } from "~/core/types.ts";
8
+ import { failureKindHttpStatus } from "~/hooks/run.ts";
9
+ import { apiErrorResponse, apiSuccessResponse, firstErrorLine } from "~/http/result.ts";
10
+ import { type HttpRouteDef, httpRequestToArgv } from "~/http/routes.ts";
11
+ import { obscureUnexpectedClientMessage } from "~/log/emitter.ts";
12
+ import { buildToolCallSuccessFromResponse } from "~/mcp/result.ts";
13
+ import { collectMcpTools, type McpToolDef, mcpToolCallToArgv } from "~/mcp/tools.ts";
14
+ import type { Cli, CliInvokeResult } from "~/runtime/cli.ts";
12
15
 
13
16
  /** Outcome of resolving a tool name against the program schema. */
14
17
  export type ToolLookupResult =
@@ -31,20 +34,16 @@ export interface HeadlessToolCallFailure {
31
34
  exitCode: number;
32
35
  stdout: string;
33
36
  stderr: string;
37
+ failureKind?: InvokeFailureKind;
34
38
  invokeResult?: CliInvokeResult;
35
39
  }
36
40
 
37
41
  export type HeadlessToolCallResult = HeadlessToolCallSuccess | HeadlessToolCallFailure;
38
42
 
39
- /** Finds an exposed tool by MCP or HTTP API tool name. */
40
- export function lookupHeadlessTool(
41
- program: CliProgram,
42
- toolName: string,
43
- invocation: CliInvocation = "mcp",
44
- ): ToolLookupResult {
43
+ /** Finds an exposed MCP tool by name. */
44
+ export function lookupHeadlessTool(program: CliProgram, toolName: string): ToolLookupResult {
45
45
  const tools = collectMcpTools(program);
46
- const tool =
47
- invocation === "api" ? tools.find((t) => t.apiName === toolName) : tools.find((t) => t.name === toolName);
46
+ const tool = tools.find((t) => t.name === toolName);
48
47
  if (!tool) {
49
48
  return { ok: false, kind: "unknown", message: `Unknown tool: ${toolName}` };
50
49
  }
@@ -60,6 +59,33 @@ export function lookupHeadlessTool(
60
59
  return { ok: true, tool };
61
60
  }
62
61
 
62
+ function invokeFailure(result: CliInvokeResult): HeadlessToolCallFailure {
63
+ const message = result.errorMsg ?? (result.stderr.trim() || `Exit code ${result.exitCode}`);
64
+ return {
65
+ ok: false,
66
+ kind: result.kind === "help" ? "help" : "invoke",
67
+ message,
68
+ exitCode: result.exitCode,
69
+ stdout: result.stdout,
70
+ stderr: result.stderr,
71
+ failureKind: result.failureKind,
72
+ invokeResult: result,
73
+ };
74
+ }
75
+
76
+ function noResponseFailure(result: CliInvokeResult): HeadlessToolCallFailure {
77
+ return {
78
+ ok: false,
79
+ kind: "invoke",
80
+ message: "Handler did not call ctx.respond() or return a value",
81
+ exitCode: 1,
82
+ stdout: result.stdout,
83
+ stderr: result.stderr,
84
+ failureKind: "unexpected",
85
+ invokeResult: result,
86
+ };
87
+ }
88
+
63
89
  /**
64
90
  * Converts flat tool arguments to argv and invokes the leaf handler headlessly.
65
91
  */
@@ -68,6 +94,7 @@ export async function executeHeadlessToolCall(
68
94
  tool: McpToolDef,
69
95
  args: Record<string, unknown>,
70
96
  invocation: CliInvocation,
97
+ mcp?: { rpcMethod: string; toolName?: string; requestId: string },
71
98
  ): Promise<HeadlessToolCallResult> {
72
99
  const argvResult = mcpToolCallToArgv(cli.program, tool, args);
73
100
  if ("error" in argvResult) {
@@ -78,20 +105,13 @@ export async function executeHeadlessToolCall(
78
105
  exitCode: 1,
79
106
  stdout: "",
80
107
  stderr: "",
108
+ failureKind: "validation",
81
109
  };
82
110
  }
83
111
 
84
- const invokeResult = await cli.invoke(argvResult, { invocation, toolArgs: args });
112
+ const invokeResult = await cli.invoke(argvResult, { invocation, toolArgs: args, mcp });
85
113
  if (invokeResult.kind === "help") {
86
- return {
87
- ok: false,
88
- kind: "help",
89
- message: invokeResult.errorMsg ?? "Help is not available via tool calls.",
90
- exitCode: invokeResult.exitCode,
91
- stdout: invokeResult.stdout,
92
- stderr: invokeResult.stderr,
93
- invokeResult,
94
- };
114
+ return invokeFailure(invokeResult);
95
115
  }
96
116
 
97
117
  if (invokeResult.kind === "ok" && invokeResult.exitCode === 0 && invokeResult.response) {
@@ -104,46 +124,81 @@ export async function executeHeadlessToolCall(
104
124
  }
105
125
 
106
126
  if (invokeResult.kind === "ok" && invokeResult.exitCode === 0) {
127
+ return noResponseFailure(invokeResult);
128
+ }
129
+
130
+ return invokeFailure(invokeResult);
131
+ }
132
+
133
+ /**
134
+ * Invokes a matched HTTP REST route headlessly (query + body → argv → invoke).
135
+ */
136
+ export async function executeHttpRouteCall(
137
+ cli: Cli,
138
+ route: HttpRouteDef,
139
+ pathParams: Record<string, string>,
140
+ query: Record<string, string>,
141
+ body: Record<string, unknown>,
142
+ http?: { request: Request; clientIp: string; requestId: string },
143
+ ): Promise<HeadlessToolCallResult> {
144
+ const argvResult = httpRequestToArgv(cli.program, route, pathParams, query, body);
145
+ if ("error" in argvResult) {
107
146
  return {
108
147
  ok: false,
109
- kind: "invoke",
110
- message: "Handler did not call ctx.respond() or return a value",
148
+ kind: "argv",
149
+ message: argvResult.error,
111
150
  exitCode: 1,
112
- stdout: invokeResult.stdout,
113
- stderr: invokeResult.stderr,
114
- invokeResult,
151
+ stdout: "",
152
+ stderr: "",
153
+ failureKind: "validation",
115
154
  };
116
155
  }
117
156
 
118
- const message = invokeResult.errorMsg ?? (invokeResult.stderr.trim() || `Exit code ${invokeResult.exitCode}`);
119
- return {
120
- ok: false,
121
- kind: "invoke",
122
- message,
123
- exitCode: invokeResult.exitCode,
124
- stdout: invokeResult.stdout,
125
- stderr: invokeResult.stderr,
126
- invokeResult,
127
- };
157
+ const toolArgs = { ...body, ...query, ...pathParams };
158
+ const invokeResult = await cli.invoke(argvResult, { invocation: "http", toolArgs, http });
159
+ if (invokeResult.kind === "help") {
160
+ return invokeFailure(invokeResult);
161
+ }
162
+
163
+ if (invokeResult.kind === "ok" && invokeResult.exitCode === 0 && invokeResult.response) {
164
+ const mcpResult = buildToolCallSuccessFromResponse(invokeResult.response);
165
+ return {
166
+ ok: true,
167
+ response: invokeResult.response,
168
+ mcpResult,
169
+ };
170
+ }
171
+
172
+ if (invokeResult.kind === "ok" && invokeResult.exitCode === 0) {
173
+ return noResponseFailure(invokeResult);
174
+ }
175
+
176
+ return invokeFailure(invokeResult);
128
177
  }
129
178
 
130
179
  /** Maps a headless success result to an HTTP Response. */
131
180
  export function headlessSuccessToHttpResponse(
132
181
  result: HeadlessToolCallSuccess,
133
- leafApiResponse?: import("../types.ts").CliApiResponseConfig,
182
+ leafApiResponse?: import("~/core/types.ts").CliHttpResponseConfig,
183
+ defaultStatus?: number,
134
184
  ): Response {
135
- return apiSuccessResponse(result.response, leafApiResponse);
185
+ return apiSuccessResponse(result.response, leafApiResponse, defaultStatus);
136
186
  }
137
187
 
138
188
  /** Maps a headless failure result to a JSON HTTP error Response. */
139
- export function headlessFailureToHttpResponse(result: HeadlessToolCallFailure): Response {
189
+ export function headlessFailureToHttpResponse(result: HeadlessToolCallFailure, obscureUnexpected = false): Response {
140
190
  const status = resolveHttpErrorStatus(result);
141
- return apiErrorResponse(status, {
142
- error: firstErrorLine(result.message),
143
- });
191
+ let message = firstErrorLine(result.message);
192
+ if (obscureUnexpected && result.failureKind === "unexpected") {
193
+ message = obscureUnexpectedClientMessage();
194
+ }
195
+ return apiErrorResponse(status, { error: message });
144
196
  }
145
197
 
146
198
  function resolveHttpErrorStatus(result: HeadlessToolCallFailure): number {
199
+ if (result.failureKind) {
200
+ return failureKindHttpStatus(result.failureKind);
201
+ }
147
202
  if (result.kind === "argv" || result.kind === "help") {
148
203
  return 400;
149
204
  }
@@ -155,3 +210,16 @@ function resolveHttpErrorStatus(result: HeadlessToolCallFailure): number {
155
210
  }
156
211
  return 500;
157
212
  }
213
+
214
+ /** Maps invoke failure kind to MCP tools/call error text (respects obscureUnexpected). */
215
+ export function headlessFailureMcpMessage(result: HeadlessToolCallFailure, obscureUnexpected = false): string {
216
+ if (obscureUnexpected && result.failureKind === "unexpected") {
217
+ return obscureUnexpectedClientMessage();
218
+ }
219
+ return firstErrorLine(result.message);
220
+ }
221
+
222
+ /** Missing-config lookup failures as MCP/HTTP pre-invoke errors. */
223
+ export function missingConfigFailureKind(): InvokeFailureKind {
224
+ return "missing_config";
225
+ }