padrone 1.8.2 → 2.0.0

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 (333) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +2 -2
  3. package/dist/codegen/code-builder.d.mts +10 -0
  4. package/dist/codegen/code-builder.d.mts.map +1 -0
  5. package/dist/codegen/code-builder.mjs +185 -0
  6. package/dist/codegen/code-builder.mjs.map +1 -0
  7. package/dist/codegen/discovery.d.mts +44 -0
  8. package/dist/codegen/discovery.d.mts.map +1 -0
  9. package/dist/codegen/discovery.mjs +179 -0
  10. package/dist/codegen/discovery.mjs.map +1 -0
  11. package/dist/codegen/file-emitter.d.mts +10 -0
  12. package/dist/codegen/file-emitter.d.mts.map +1 -0
  13. package/dist/codegen/file-emitter.mjs +58 -0
  14. package/dist/codegen/file-emitter.mjs.map +1 -0
  15. package/dist/codegen/generators/barrel-file.d.mts +10 -0
  16. package/dist/codegen/generators/barrel-file.d.mts.map +1 -0
  17. package/dist/codegen/generators/barrel-file.mjs +16 -0
  18. package/dist/codegen/generators/barrel-file.mjs.map +1 -0
  19. package/dist/codegen/generators/command-file.d.mts +25 -0
  20. package/dist/codegen/generators/command-file.d.mts.map +1 -0
  21. package/dist/codegen/generators/command-file.mjs +130 -0
  22. package/dist/codegen/generators/command-file.mjs.map +1 -0
  23. package/dist/codegen/generators/command-tree.d.mts +17 -0
  24. package/dist/codegen/generators/command-tree.d.mts.map +1 -0
  25. package/dist/codegen/generators/command-tree.mjs +72 -0
  26. package/dist/codegen/generators/command-tree.mjs.map +1 -0
  27. package/dist/codegen/index.d.mts +15 -330
  28. package/dist/codegen/index.mjs +13 -1507
  29. package/dist/codegen/parsers/bash.d.mts +19 -0
  30. package/dist/codegen/parsers/bash.d.mts.map +1 -0
  31. package/dist/codegen/parsers/bash.mjs +112 -0
  32. package/dist/codegen/parsers/bash.mjs.map +1 -0
  33. package/dist/codegen/parsers/fish.d.mts +13 -0
  34. package/dist/codegen/parsers/fish.d.mts.map +1 -0
  35. package/dist/codegen/parsers/fish.mjs +108 -0
  36. package/dist/codegen/parsers/fish.mjs.map +1 -0
  37. package/dist/codegen/parsers/help.d.mts +15 -0
  38. package/dist/codegen/parsers/help.d.mts.map +1 -0
  39. package/dist/codegen/parsers/help.mjs +271 -0
  40. package/dist/codegen/parsers/help.mjs.map +1 -0
  41. package/dist/codegen/parsers/merge.d.mts +14 -0
  42. package/dist/codegen/parsers/merge.d.mts.map +1 -0
  43. package/dist/codegen/parsers/merge.mjs +76 -0
  44. package/dist/codegen/parsers/merge.mjs.map +1 -0
  45. package/dist/codegen/parsers/zsh.d.mts +16 -0
  46. package/dist/codegen/parsers/zsh.d.mts.map +1 -0
  47. package/dist/codegen/parsers/zsh.mjs +161 -0
  48. package/dist/codegen/parsers/zsh.mjs.map +1 -0
  49. package/dist/codegen/schema-to-code.d.mts +24 -0
  50. package/dist/codegen/schema-to-code.d.mts.map +1 -0
  51. package/dist/codegen/schema-to-code.mjs +147 -0
  52. package/dist/codegen/schema-to-code.mjs.map +1 -0
  53. package/dist/codegen/template.d.mts +18 -0
  54. package/dist/codegen/template.d.mts.map +1 -0
  55. package/dist/codegen/template.mjs +37 -0
  56. package/dist/codegen/template.mjs.map +1 -0
  57. package/dist/codegen/types.d.mts +144 -0
  58. package/dist/codegen/types.d.mts.map +1 -0
  59. package/dist/completion.d.mts +2 -0
  60. package/dist/completion.mjs +2 -0
  61. package/dist/core/args.d.mts +3 -0
  62. package/dist/{args-DrCXxXeP.mjs → core/args.mjs} +49 -113
  63. package/dist/core/args.mjs.map +1 -0
  64. package/dist/core/commands.d.mts +13 -0
  65. package/dist/core/commands.d.mts.map +1 -0
  66. package/dist/{commands-DLR0rFgq.mjs → core/commands.mjs} +4 -232
  67. package/dist/core/commands.mjs.map +1 -0
  68. package/dist/core/create.d.mts +63 -0
  69. package/dist/core/create.d.mts.map +1 -0
  70. package/dist/core/create.mjs +230 -0
  71. package/dist/core/create.mjs.map +1 -0
  72. package/dist/core/default-runtime.mjs +198 -0
  73. package/dist/core/default-runtime.mjs.map +1 -0
  74. package/dist/core/errors.d.mts +107 -0
  75. package/dist/core/errors.d.mts.map +1 -0
  76. package/dist/{errors-DA4KzK1M.mjs → core/errors.mjs} +2 -2
  77. package/dist/core/errors.mjs.map +1 -0
  78. package/dist/core/exec.mjs +191 -0
  79. package/dist/core/exec.mjs.map +1 -0
  80. package/dist/core/interceptors.d.mts +33 -0
  81. package/dist/core/interceptors.d.mts.map +1 -0
  82. package/dist/core/interceptors.mjs +253 -0
  83. package/dist/core/interceptors.mjs.map +1 -0
  84. package/dist/core/parse.mjs +181 -0
  85. package/dist/core/parse.mjs.map +1 -0
  86. package/dist/core/program-methods.mjs +256 -0
  87. package/dist/core/program-methods.mjs.map +1 -0
  88. package/dist/core/results.d.mts +13 -0
  89. package/dist/core/results.d.mts.map +1 -0
  90. package/dist/core/results.mjs +150 -0
  91. package/dist/core/results.mjs.map +1 -0
  92. package/dist/core/runtime.d.mts +213 -0
  93. package/dist/core/runtime.d.mts.map +1 -0
  94. package/dist/core/runtime.mjs +10 -0
  95. package/dist/core/runtime.mjs.map +1 -0
  96. package/dist/core/validate.mjs +220 -0
  97. package/dist/core/validate.mjs.map +1 -0
  98. package/dist/docs/index.d.mts +1 -1
  99. package/dist/docs/index.d.mts.map +1 -1
  100. package/dist/docs/index.mjs +2 -2
  101. package/dist/docs/index.mjs.map +1 -1
  102. package/dist/extension/auto-output.d.mts +53 -0
  103. package/dist/extension/auto-output.d.mts.map +1 -0
  104. package/dist/extension/auto-output.mjs +116 -0
  105. package/dist/extension/auto-output.mjs.map +1 -0
  106. package/dist/extension/color.d.mts +19 -0
  107. package/dist/extension/color.d.mts.map +1 -0
  108. package/dist/extension/color.mjs +37 -0
  109. package/dist/extension/color.mjs.map +1 -0
  110. package/dist/extension/completion.d.mts +25 -0
  111. package/dist/extension/completion.d.mts.map +1 -0
  112. package/dist/extension/completion.mjs +40 -0
  113. package/dist/extension/completion.mjs.map +1 -0
  114. package/dist/extension/config.d.mts +55 -0
  115. package/dist/extension/config.d.mts.map +1 -0
  116. package/dist/extension/config.mjs +162 -0
  117. package/dist/extension/config.mjs.map +1 -0
  118. package/dist/extension/env.d.mts +42 -0
  119. package/dist/extension/env.d.mts.map +1 -0
  120. package/dist/extension/env.mjs +44 -0
  121. package/dist/extension/env.mjs.map +1 -0
  122. package/dist/extension/help.d.mts +29 -0
  123. package/dist/extension/help.d.mts.map +1 -0
  124. package/dist/extension/help.mjs +161 -0
  125. package/dist/extension/help.mjs.map +1 -0
  126. package/dist/extension/index.d.mts +18 -0
  127. package/dist/extension/ink.d.mts +31 -0
  128. package/dist/extension/ink.d.mts.map +1 -0
  129. package/dist/extension/ink.mjs +69 -0
  130. package/dist/extension/ink.mjs.map +1 -0
  131. package/dist/extension/interactive.d.mts +16 -0
  132. package/dist/extension/interactive.d.mts.map +1 -0
  133. package/dist/extension/interactive.mjs +89 -0
  134. package/dist/extension/interactive.mjs.map +1 -0
  135. package/dist/extension/logger.d.mts +67 -0
  136. package/dist/extension/logger.d.mts.map +1 -0
  137. package/dist/extension/logger.mjs +188 -0
  138. package/dist/extension/logger.mjs.map +1 -0
  139. package/dist/extension/man.d.mts +25 -0
  140. package/dist/extension/man.d.mts.map +1 -0
  141. package/dist/extension/man.mjs +41 -0
  142. package/dist/extension/man.mjs.map +1 -0
  143. package/dist/extension/mcp.d.mts +28 -0
  144. package/dist/extension/mcp.d.mts.map +1 -0
  145. package/dist/extension/mcp.mjs +44 -0
  146. package/dist/extension/mcp.mjs.map +1 -0
  147. package/dist/extension/progress-renderer.d.mts +13 -0
  148. package/dist/extension/progress-renderer.d.mts.map +1 -0
  149. package/dist/extension/progress-renderer.mjs +351 -0
  150. package/dist/extension/progress-renderer.mjs.map +1 -0
  151. package/dist/extension/progress.d.mts +76 -0
  152. package/dist/extension/progress.d.mts.map +1 -0
  153. package/dist/extension/progress.mjs +225 -0
  154. package/dist/extension/progress.mjs.map +1 -0
  155. package/dist/extension/repl.d.mts +26 -0
  156. package/dist/extension/repl.d.mts.map +1 -0
  157. package/dist/extension/repl.mjs +67 -0
  158. package/dist/extension/repl.mjs.map +1 -0
  159. package/dist/extension/serve.d.mts +27 -0
  160. package/dist/extension/serve.d.mts.map +1 -0
  161. package/dist/extension/serve.mjs +41 -0
  162. package/dist/extension/serve.mjs.map +1 -0
  163. package/dist/extension/signal.d.mts +19 -0
  164. package/dist/extension/signal.d.mts.map +1 -0
  165. package/dist/extension/signal.mjs +81 -0
  166. package/dist/extension/signal.mjs.map +1 -0
  167. package/dist/extension/stdin.d.mts +19 -0
  168. package/dist/extension/stdin.d.mts.map +1 -0
  169. package/dist/extension/stdin.mjs +52 -0
  170. package/dist/extension/stdin.mjs.map +1 -0
  171. package/dist/extension/suggestions.d.mts +6 -0
  172. package/dist/extension/suggestions.d.mts.map +1 -0
  173. package/dist/extension/suggestions.mjs +113 -0
  174. package/dist/extension/suggestions.mjs.map +1 -0
  175. package/dist/extension/timing.d.mts +27 -0
  176. package/dist/extension/timing.d.mts.map +1 -0
  177. package/dist/extension/timing.mjs +71 -0
  178. package/dist/extension/timing.mjs.map +1 -0
  179. package/dist/extension/tracing.d.mts +85 -0
  180. package/dist/extension/tracing.d.mts.map +1 -0
  181. package/dist/extension/tracing.mjs +107 -0
  182. package/dist/extension/tracing.mjs.map +1 -0
  183. package/dist/extension/update-check.d.mts +20 -0
  184. package/dist/extension/update-check.d.mts.map +1 -0
  185. package/dist/extension/update-check.mjs +62 -0
  186. package/dist/extension/update-check.mjs.map +1 -0
  187. package/dist/extension/utils.mjs +43 -0
  188. package/dist/extension/utils.mjs.map +1 -0
  189. package/dist/extension/version.d.mts +20 -0
  190. package/dist/extension/version.d.mts.map +1 -0
  191. package/dist/extension/version.mjs +51 -0
  192. package/dist/extension/version.mjs.map +1 -0
  193. package/dist/{completion-UnBKfGuk.mjs → feature/completion.mjs} +4 -3
  194. package/dist/feature/completion.mjs.map +1 -0
  195. package/dist/feature/interactive.mjs +145 -0
  196. package/dist/feature/interactive.mjs.map +1 -0
  197. package/dist/feature/mcp.d.mts +18 -0
  198. package/dist/feature/mcp.d.mts.map +1 -0
  199. package/dist/{mcp-D6PdtjIs.mjs → feature/mcp.mjs} +6 -6
  200. package/dist/feature/mcp.mjs.map +1 -0
  201. package/dist/feature/repl-loop.mjs +217 -0
  202. package/dist/feature/repl-loop.mjs.map +1 -0
  203. package/dist/feature/serve.d.mts +18 -0
  204. package/dist/feature/serve.d.mts.map +1 -0
  205. package/dist/{serve-PaCLsNoD.mjs → feature/serve.mjs} +7 -7
  206. package/dist/feature/serve.mjs.map +1 -0
  207. package/dist/feature/test.d.mts +109 -0
  208. package/dist/feature/test.d.mts.map +1 -0
  209. package/dist/feature/test.mjs +116 -0
  210. package/dist/feature/test.mjs.map +1 -0
  211. package/dist/feature/update-check.d.mts +35 -0
  212. package/dist/feature/update-check.d.mts.map +1 -0
  213. package/dist/{update-check-CZ2VqjnV.mjs → feature/update-check.mjs} +2 -2
  214. package/dist/feature/update-check.mjs.map +1 -0
  215. package/dist/feature/wrap.d.mts +58 -0
  216. package/dist/feature/wrap.d.mts.map +1 -0
  217. package/dist/feature/wrap.mjs +77 -0
  218. package/dist/feature/wrap.mjs.map +1 -0
  219. package/dist/index.d.mts +38 -901
  220. package/dist/index.mjs +25 -4337
  221. package/dist/ink.d.mts +2 -0
  222. package/dist/ink.mjs +2 -0
  223. package/dist/man.d.mts +2 -0
  224. package/dist/man.mjs +2 -0
  225. package/dist/mcp.d.mts +3 -0
  226. package/dist/mcp.mjs +2 -0
  227. package/dist/output/colorizer.d.mts +28 -0
  228. package/dist/output/colorizer.d.mts.map +1 -0
  229. package/dist/output/colorizer.mjs +94 -0
  230. package/dist/output/colorizer.mjs.map +1 -0
  231. package/dist/{formatter-CY3KrOEd.d.mts → output/formatter.d.mts} +2 -28
  232. package/dist/output/formatter.d.mts.map +1 -0
  233. package/dist/output/formatter.mjs +360 -0
  234. package/dist/output/formatter.mjs.map +1 -0
  235. package/dist/output/help.d.mts +18 -0
  236. package/dist/output/help.d.mts.map +1 -0
  237. package/dist/output/help.mjs +267 -0
  238. package/dist/output/help.mjs.map +1 -0
  239. package/dist/output/output-indicator.d.mts +27 -0
  240. package/dist/output/output-indicator.d.mts.map +1 -0
  241. package/dist/output/output-indicator.mjs +54 -0
  242. package/dist/output/output-indicator.mjs.map +1 -0
  243. package/dist/output/primitives.d.mts +33 -0
  244. package/dist/output/primitives.d.mts.map +1 -0
  245. package/dist/output/primitives.mjs +206 -0
  246. package/dist/output/primitives.mjs.map +1 -0
  247. package/dist/output/styling.d.mts +38 -0
  248. package/dist/output/styling.d.mts.map +1 -0
  249. package/dist/output/styling.mjs +146 -0
  250. package/dist/output/styling.mjs.map +1 -0
  251. package/dist/schema/zod.d.mts +6 -0
  252. package/dist/schema/zod.d.mts.map +1 -0
  253. package/dist/schema/zod.mjs +50 -0
  254. package/dist/schema/zod.mjs.map +1 -0
  255. package/dist/serve.d.mts +2 -0
  256. package/dist/serve.mjs +2 -0
  257. package/dist/test.d.mts +2 -109
  258. package/dist/test.mjs +1 -115
  259. package/dist/tracing.d.mts +2 -0
  260. package/dist/tracing.mjs +2 -0
  261. package/dist/types/args-meta.d.mts +129 -0
  262. package/dist/types/args-meta.d.mts.map +1 -0
  263. package/dist/types/builder.d.mts +212 -0
  264. package/dist/types/builder.d.mts.map +1 -0
  265. package/dist/types/command.d.mts +112 -0
  266. package/dist/types/command.d.mts.map +1 -0
  267. package/dist/types/index.d.mts +8 -0
  268. package/dist/types/interceptor.d.mts +232 -0
  269. package/dist/types/interceptor.d.mts.map +1 -0
  270. package/dist/types/preferences.d.mts +79 -0
  271. package/dist/types/preferences.d.mts.map +1 -0
  272. package/dist/types/result.d.mts +61 -0
  273. package/dist/types/result.d.mts.map +1 -0
  274. package/dist/types/schema.d.mts +24 -0
  275. package/dist/types/schema.d.mts.map +1 -0
  276. package/dist/util/dotenv.mjs +174 -0
  277. package/dist/util/dotenv.mjs.map +1 -0
  278. package/dist/util/shell-utils.mjs +80 -0
  279. package/dist/util/shell-utils.mjs.map +1 -0
  280. package/dist/util/stream.d.mts +33 -0
  281. package/dist/util/stream.d.mts.map +1 -0
  282. package/dist/{stream-DC4H8YTx.mjs → util/stream.mjs} +2 -2
  283. package/dist/util/stream.mjs.map +1 -0
  284. package/dist/util/type-helpers.d.mts +74 -0
  285. package/dist/util/type-helpers.d.mts.map +1 -0
  286. package/dist/util/type-utils.d.mts +186 -0
  287. package/dist/util/type-utils.d.mts.map +1 -0
  288. package/dist/util/utils.mjs +43 -0
  289. package/dist/util/utils.mjs.map +1 -0
  290. package/dist/zod.d.mts +1 -32
  291. package/dist/zod.mjs +1 -49
  292. package/package.json +65 -17
  293. package/src/completion.ts +2 -0
  294. package/src/core/args.ts +63 -37
  295. package/src/core/runtime.ts +11 -2
  296. package/src/extension/completion.ts +3 -0
  297. package/src/extension/index.ts +0 -12
  298. package/src/extension/ink.ts +2 -1
  299. package/src/extension/man.ts +3 -0
  300. package/src/extension/mcp.ts +3 -0
  301. package/src/extension/progress-renderer.ts +32 -8
  302. package/src/extension/progress.ts +19 -9
  303. package/src/extension/serve.ts +3 -0
  304. package/src/extension/tracing.ts +2 -0
  305. package/src/index.ts +1 -20
  306. package/src/ink.ts +2 -0
  307. package/src/man.ts +2 -0
  308. package/src/mcp.ts +3 -0
  309. package/src/serve.ts +2 -0
  310. package/src/tracing.ts +9 -0
  311. package/src/types/builder.ts +2 -2
  312. package/src/zod.ts +1 -1
  313. package/dist/args-DrCXxXeP.mjs.map +0 -1
  314. package/dist/codegen/index.d.mts.map +0 -1
  315. package/dist/codegen/index.mjs.map +0 -1
  316. package/dist/commands-DLR0rFgq.mjs.map +0 -1
  317. package/dist/completion-UnBKfGuk.mjs.map +0 -1
  318. package/dist/errors-DA4KzK1M.mjs.map +0 -1
  319. package/dist/formatter-CY3KrOEd.d.mts.map +0 -1
  320. package/dist/help-B-ZMYyn-.mjs +0 -892
  321. package/dist/help-B-ZMYyn-.mjs.map +0 -1
  322. package/dist/index-D-Dpz7l_.d.mts +0 -1427
  323. package/dist/index-D-Dpz7l_.d.mts.map +0 -1
  324. package/dist/index.d.mts.map +0 -1
  325. package/dist/index.mjs.map +0 -1
  326. package/dist/mcp-D6PdtjIs.mjs.map +0 -1
  327. package/dist/serve-PaCLsNoD.mjs.map +0 -1
  328. package/dist/stream-DC4H8YTx.mjs.map +0 -1
  329. package/dist/test.d.mts.map +0 -1
  330. package/dist/test.mjs.map +0 -1
  331. package/dist/update-check-CZ2VqjnV.mjs.map +0 -1
  332. package/dist/zod.d.mts.map +0 -1
  333. package/dist/zod.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.mts","names":[],"sources":["../../src/extension/version.ts"],"mappings":";;;;KAUY,cAAA,GAAiB,cAAc,gBAAgB,aAAA;AAAA,KAE/C,WAAA,MAAiB,WAAA,CAAY,CAAA,aAAc,cAAA;;AAFvD;;;;AAAwE;AAExE;;;;iBAwCgB,cAAA,eAA6B,gBAAA,EAAkB,OAAA,EAAS,CAAA,KAAM,WAAA,CAAY,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { resolveCommand } from "../core/commands.mjs";
2
+ import { thenMaybe } from "../core/results.mjs";
3
+ import { defineInterceptor } from "../core/interceptors.mjs";
4
+ import { getRootCommand, getVersion } from "../util/utils.mjs";
5
+ //#region src/extension/version.ts
6
+ const versionInterceptor = defineInterceptor({
7
+ id: "padrone:version",
8
+ name: "padrone:version",
9
+ order: -1e3
10
+ }, () => ({ parse(_ctx, next) {
11
+ return thenMaybe(next(), (res) => {
12
+ if ((res.rawArgs.version || res.rawArgs.v || res.rawArgs.V) && !res.command.parent) {
13
+ delete res.rawArgs.version;
14
+ delete res.rawArgs.v;
15
+ delete res.rawArgs.V;
16
+ const versionCmd = res.command.commands?.find((c) => c.name === "version");
17
+ if (versionCmd) {
18
+ resolveCommand(versionCmd);
19
+ return {
20
+ ...res,
21
+ command: versionCmd,
22
+ rawArgs: {},
23
+ positionalArgs: []
24
+ };
25
+ }
26
+ }
27
+ return res;
28
+ });
29
+ } }));
30
+ /**
31
+ * Extension that adds version support:
32
+ * - `version` command
33
+ * - `--version` / `-v` / `-V` flags (root command only)
34
+ *
35
+ * Usage:
36
+ * ```ts
37
+ * createPadrone('my-cli').extend(padroneVersion())
38
+ * ```
39
+ */
40
+ function padroneVersion() {
41
+ return ((builder) => builder.command("version", (c) => c.configure({
42
+ description: "Display the version number",
43
+ hidden: true
44
+ }).action((_args, ctx) => {
45
+ return getVersion(getRootCommand(ctx.command).version);
46
+ })).intercept(versionInterceptor));
47
+ }
48
+ //#endregion
49
+ export { padroneVersion };
50
+
51
+ //# sourceMappingURL=version.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.mjs","names":[],"sources":["../../src/extension/version.ts"],"sourcesContent":["import { thenMaybe } from '#src/core/results.ts';\nimport { resolveCommand } from '../core/commands.ts';\nimport { defineInterceptor } from '../core/interceptors.ts';\nimport type { AnyPadroneBuilder, CommandTypesBase, PadroneCommand } from '../types/index.ts';\nimport type { PadroneSchema } from '../types/schema.ts';\nimport type { WithCommand } from '../util/type-utils.ts';\nimport { getRootCommand, getVersion } from '../util/utils.ts';\n\n// ── Types ────────────────────────────────────────────────────────────────\n\nexport type VersionCommand = PadroneCommand<'version', '', PadroneSchema<void>, string, [], [], false>;\n\nexport type WithVersion<T> = WithCommand<T, 'version', VersionCommand>;\n\n// ── Interceptor ─────────────────────────────────────────────────────────\n\nconst versionInterceptor = defineInterceptor({ id: 'padrone:version', name: 'padrone:version', order: -1000 }, () => ({\n parse(_ctx, next) {\n return thenMaybe(next(), (res) => {\n const hasVersionFlag = res.rawArgs.version || res.rawArgs.v || res.rawArgs.V;\n\n // Only show version for root command (no subcommand matched)\n if (hasVersionFlag && !res.command.parent) {\n delete res.rawArgs.version;\n delete res.rawArgs.v;\n delete res.rawArgs.V;\n\n // Route to the version command so its action handles the rest\n const versionCmd = res.command.commands?.find((c) => c.name === 'version');\n if (versionCmd) {\n resolveCommand(versionCmd);\n return { ...res, command: versionCmd, rawArgs: {}, positionalArgs: [] };\n }\n }\n\n return res;\n });\n },\n}));\n\n// ── Extension ────────────────────────────────────────────────────────────\n\n/**\n * Extension that adds version support:\n * - `version` command\n * - `--version` / `-v` / `-V` flags (root command only)\n *\n * Usage:\n * ```ts\n * createPadrone('my-cli').extend(padroneVersion())\n * ```\n */\nexport function padroneVersion(): <T extends CommandTypesBase>(builder: T) => WithVersion<T> {\n return ((builder: AnyPadroneBuilder) =>\n builder\n .command('version', (c) =>\n c.configure({ description: 'Display the version number', hidden: true }).action((_args, ctx) => {\n const rootCommand = getRootCommand(ctx.command);\n return getVersion(rootCommand.version);\n }),\n )\n .intercept(versionInterceptor)) as any;\n}\n"],"mappings":";;;;;AAgBA,MAAM,qBAAqB,kBAAkB;CAAE,IAAI;CAAmB,MAAM;CAAmB,OAAO;AAAM,UAAU,EACpH,MAAM,MAAM,MAAM;CAChB,OAAO,UAAU,KAAK,IAAI,QAAQ;EAIhC,KAHuB,IAAI,QAAQ,WAAW,IAAI,QAAQ,KAAK,IAAI,QAAQ,MAGrD,CAAC,IAAI,QAAQ,QAAQ;GACzC,OAAO,IAAI,QAAQ;GACnB,OAAO,IAAI,QAAQ;GACnB,OAAO,IAAI,QAAQ;GAGnB,MAAM,aAAa,IAAI,QAAQ,UAAU,MAAM,MAAM,EAAE,SAAS,SAAS;GACzE,IAAI,YAAY;IACd,eAAe,UAAU;IACzB,OAAO;KAAE,GAAG;KAAK,SAAS;KAAY,SAAS,CAAC;KAAG,gBAAgB,CAAC;IAAE;GACxE;EACF;EAEA,OAAO;CACT,CAAC;AACH,EACF,EAAE;;;;;;;;;;;AAcF,SAAgB,iBAA6E;CAC3F,SAAS,YACP,QACG,QAAQ,YAAY,MACnB,EAAE,UAAU;EAAE,aAAa;EAA8B,QAAQ;CAAK,CAAC,CAAC,CAAC,QAAQ,OAAO,QAAQ;EAE9F,OAAO,WADa,eAAe,IAAI,OACX,CAAC,CAAC,OAAO;CACvC,CAAC,CACH,CAAC,CACA,UAAU,kBAAkB;AACnC"}
@@ -1,4 +1,5 @@
1
- import { a as getJsonSchema, d as detectShell, i as extractSchemaMetadata, m as writeToRcFile, p as getRcFile } from "./args-DrCXxXeP.mjs";
1
+ import { detectShell, getRcFile, writeToRcFile } from "../util/shell-utils.mjs";
2
+ import { extractSchemaMetadata, getJsonSchema } from "../core/args.mjs";
2
3
  //#region src/feature/completion.ts
3
4
  /**
4
5
  * Collects all commands from a program recursively.
@@ -390,6 +391,6 @@ function buildSetupSnippet(programName, shell, beginMarker, endMarker) {
390
391
  }
391
392
  }
392
393
  //#endregion
393
- export { detectShell, generateCompletionOutput, setupCompletions };
394
+ export { detectShell, generateBashCompletion, generateCompletion, generateCompletionOutput, generateFishCompletion, generatePowerShellCompletion, generateZshCompletion, getCompletionInstallInstructions, setupCompletions };
394
395
 
395
- //# sourceMappingURL=completion-UnBKfGuk.mjs.map
396
+ //# sourceMappingURL=completion.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.mjs","names":[],"sources":["../../src/feature/completion.ts"],"sourcesContent":["import { extractSchemaMetadata, getJsonSchema } from '../core/args.ts';\nimport type { AnyPadroneCommand } from '../types/index.ts';\nimport { detectShell, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';\n\nexport { detectShell, escapeRegExp, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';\n\n/**\n * Collects all commands from a program recursively.\n */\nfunction collectAllCommands(cmd: AnyPadroneCommand): AnyPadroneCommand[] {\n const result: AnyPadroneCommand[] = [];\n\n if (cmd.commands) {\n for (const subcmd of cmd.commands) {\n if (!subcmd.hidden) {\n result.push(subcmd);\n result.push(...collectAllCommands(subcmd));\n }\n }\n }\n\n return result;\n}\n\ninterface ExtractedArg {\n name: string;\n alias?: string;\n isBoolean: boolean;\n enum?: string[];\n description?: string;\n}\n\n/**\n * Extracts all argument names from a command's schema.\n */\nfunction extractArguments(cmd: AnyPadroneCommand): ExtractedArg[] {\n const argList: ExtractedArg[] = [];\n\n if (!cmd.argsSchema) return argList;\n\n try {\n const argsMeta = cmd.meta?.fields;\n const { aliases } = extractSchemaMetadata(cmd.argsSchema, argsMeta, cmd.meta?.autoAlias);\n\n // Build reverse map: argName → aliasName\n const argToAlias: Record<string, string> = {};\n for (const [aliasName, argName] of Object.entries(aliases)) {\n if (!argToAlias[argName]) argToAlias[argName] = aliasName;\n }\n\n const jsonSchema = getJsonSchema(cmd.argsSchema) as Record<string, any>;\n\n if (jsonSchema.type === 'object' && jsonSchema.properties) {\n for (const [key, prop] of Object.entries(jsonSchema.properties as Record<string, any>)) {\n const enumValues = (prop.enum ?? prop.items?.enum) as string[] | undefined;\n const optMeta = argsMeta?.[key];\n argList.push({\n name: key,\n alias: argToAlias[key],\n isBoolean: prop?.type === 'boolean',\n enum: enumValues,\n description: optMeta?.description ?? prop.description,\n });\n }\n }\n } catch {\n // Ignore schema parsing errors\n }\n\n return argList;\n}\n\n/**\n * Collects unique args across all commands, preserving first-seen enum values.\n */\nfunction collectUniqueArgs(program: AnyPadroneCommand, commands: AnyPadroneCommand[]): Map<string, ExtractedArg> {\n const seen = new Map<string, ExtractedArg>();\n\n for (const cmd of [program, ...commands]) {\n for (const arg of extractArguments(cmd)) {\n if (!seen.has(arg.name)) {\n seen.set(arg.name, arg);\n }\n }\n }\n\n return seen;\n}\n\n/**\n * Generates a Bash completion script for the program.\n */\nexport function generateBashCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n const commandNames = commands.map((c) => c.name).join(' ');\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n // Collect all option names\n const allArguments = new Set<string>();\n allArguments.add('--help');\n allArguments.add('--version');\n\n for (const arg of uniqueArgs.values()) {\n allArguments.add(`--${arg.name}`);\n if (arg.alias) allArguments.add(`--${arg.alias}`);\n }\n\n const argsList = Array.from(allArguments).join(' ');\n\n // Build case branches for options with enum values\n const enumCases: string[] = [];\n for (const arg of uniqueArgs.values()) {\n if (!arg.enum || arg.enum.length === 0) continue;\n const values = arg.enum.join(' ');\n const patterns = [`--${arg.name}`];\n if (arg.alias) patterns.push(`--${arg.alias}`);\n enumCases.push(` ${patterns.join('|')}) COMPREPLY=($(compgen -W \"${values}\" -- \"$cur\")); return 0 ;;`);\n }\n\n const enumBlock =\n enumCases.length > 0\n ? `\n # Complete option values\n case \"$prev\" in\n${enumCases.join('\\n')}\n esac\n\n`\n : '\\n';\n\n return `###-begin-${programName}-completion-###\n#\n# ${programName} command completion script\n#\n# Installation: ${programName} completion >> ~/.bashrc (or ~/.zshrc)\n# Or, maybe: ${programName} completion > /usr/local/etc/bash_completion.d/${programName}\n#\n\nif type complete &>/dev/null; then\n _${programName}_completion() {\n local cur prev words cword\n if type _get_comp_words_by_ref &>/dev/null; then\n _get_comp_words_by_ref -n = -n @ -n : -w words -i cword\n else\n cword=\"$COMP_CWORD\"\n words=(\"\\${COMP_WORDS[@]}\")\n fi\n\n cur=\"\\${words[cword]}\"\n prev=\"\\${words[cword-1]}\"\n\n local commands=\"${commandNames}\"\n local args=\"${argsList}\"\n${enumBlock} # Complete args when current word starts with -\n if [[ \"$cur\" == -* ]]; then\n COMPREPLY=($(compgen -W \"$args\" -- \"$cur\"))\n return 0\n fi\n\n # Complete commands\n COMPREPLY=($(compgen -W \"$commands\" -- \"$cur\"))\n }\n complete -o bashdefault -o default -o nospace -F _${programName}_completion ${programName}\nelif type compdef &>/dev/null; then\n _${programName}_completion() {\n local si=$IFS\n local commands=\"${commandNames}\"\n local args=\"${argsList}\"\n\n if [[ \"\\${words[CURRENT]}\" == -* ]]; then\n compadd -- \\${=args}\n else\n compadd -- \\${=commands}\n fi\n IFS=$si\n }\n compdef _${programName}_completion ${programName}\nelif type compctl &>/dev/null; then\n _${programName}_completion() {\n local commands=\"${commandNames}\"\n local args=\"${argsList}\"\n\n if [[ \"\\${words[CURRENT]}\" == -* ]]; then\n reply=(\\${=args})\n else\n reply=(\\${=commands})\n fi\n }\n compctl -K _${programName}_completion ${programName}\nfi\n###-end-${programName}-completion-###`;\n}\n\n/**\n * Generates a Zsh completion script for the program.\n */\nexport function generateZshCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n\n // Generate command completions with descriptions\n const commandCompletions = commands\n .map((cmd) => {\n const desc = cmd.description || cmd.title || '';\n const escapedDesc = desc.replace(/'/g, \"'\\\\''\").replace(/:/g, '\\\\:');\n return ` '${cmd.name}:${escapedDesc}'`;\n })\n .join('\\n');\n\n // Collect all args with descriptions and enum values\n const argumentCompletions: string[] = [];\n argumentCompletions.push(\" '--help[Show help information]'\");\n argumentCompletions.push(\" '--version[Show version number]'\");\n\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n for (const arg of uniqueArgs.values()) {\n const desc = arg.description || '';\n const escapedDesc = desc.replace(/'/g, \"'\\\\''\").replace(/\\[/g, '\\\\[').replace(/\\]/g, '\\\\]');\n\n // Zsh action spec for enum values: :label:(val1 val2 val3)\n const valueAction = arg.enum?.length ? `: :(${arg.enum.join(' ')})` : '';\n\n if (arg.alias) {\n argumentCompletions.push(` {--${arg.alias},--${arg.name}}'[${escapedDesc}]${valueAction}'`);\n } else {\n argumentCompletions.push(` '--${arg.name}[${escapedDesc}]${valueAction}'`);\n }\n }\n\n return `#compdef ${programName}\n###-begin-${programName}-completion-###\n#\n# ${programName} command completion script for Zsh\n#\n# Installation: ${programName} completion >> ~/.zshrc\n# Or: ${programName} completion > ~/.zsh/completions/_${programName}\n#\n\n_${programName}() {\n local -a commands\n local -a args\n\n commands=(\n${commandCompletions}\n )\n\n args=(\n${argumentCompletions.join('\\n')}\n )\n\n _arguments -s \\\\\n $args \\\\\n '1: :->command' \\\\\n '*::arg:->args'\n\n case \"$state\" in\n command)\n _describe 'command' commands\n ;;\n esac\n}\n\n_${programName}\n###-end-${programName}-completion-###`;\n}\n\n/**\n * Generates a Fish completion script for the program.\n */\nexport function generateFishCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n\n const lines: string[] = [\n `###-begin-${programName}-completion-###`,\n '#',\n `# ${programName} command completion script for Fish`,\n '#',\n `# Installation: ${programName} completion > ~/.config/fish/completions/${programName}.fish`,\n '#',\n '',\n `# Clear existing completions`,\n `complete -c ${programName} -e`,\n '',\n '# Commands',\n ];\n\n for (const cmd of commands) {\n const desc = cmd.description || cmd.title || '';\n const escapedDesc = desc.replace(/'/g, \"\\\\'\");\n lines.push(`complete -c ${programName} -n \"__fish_use_subcommand\" -a \"${cmd.name}\" -d '${escapedDesc}'`);\n }\n\n lines.push('');\n lines.push('# Global arguments');\n lines.push(`complete -c ${programName} -l help -d 'Show help information'`);\n lines.push(`complete -c ${programName} -l version -d 'Show version number'`);\n\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n for (const arg of uniqueArgs.values()) {\n const desc = arg.description || '';\n const escapedDesc = desc.replace(/'/g, \"\\\\'\");\n // Fish: -xa 'val1 val2' provides exclusive value completions\n const valueFlag = arg.enum?.length ? ` -xa '${arg.enum.join(' ')}'` : '';\n\n if (arg.alias) {\n lines.push(`complete -c ${programName} -l ${arg.name} -s ${arg.alias} -d '${escapedDesc}'${valueFlag}`);\n } else {\n lines.push(`complete -c ${programName} -l ${arg.name} -d '${escapedDesc}'${valueFlag}`);\n }\n }\n\n lines.push(`###-end-${programName}-completion-###`);\n\n return lines.join('\\n');\n}\n\n/**\n * Generates a PowerShell completion script for the program.\n */\nexport function generatePowerShellCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n const commandNames = commands.map((c) => `'${c.name}'`).join(', ');\n\n // Collect all option names\n const argNames: string[] = [\"'--help'\", \"'--version'\"];\n for (const arg of uniqueArgs.values()) {\n argNames.push(`'--${arg.name}'`);\n if (arg.alias) argNames.push(`'--${arg.alias}'`);\n }\n\n // Build switch cases for option value completion\n const enumCases: string[] = [];\n for (const arg of uniqueArgs.values()) {\n if (!arg.enum || arg.enum.length === 0) continue;\n const values = arg.enum.map((v) => `'${v}'`).join(', ');\n const patterns = [`'--${arg.name}'`];\n if (arg.alias) patterns.push(`'--${arg.alias}'`);\n enumCases.push(` ${patterns.join(', ')} { @(${values}) | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }; return }`);\n }\n\n const enumBlock =\n enumCases.length > 0\n ? `\n # Complete option values\n $prevWord = $commandAst.CommandElements | Select-Object -Last 2 | Select-Object -First 1\n switch ($prevWord) {\n${enumCases.join('\\n')}\n }\n\n`\n : '\\n';\n\n return `###-begin-${programName}-completion-###\n#\n# ${programName} command completion script for PowerShell\n#\n# Installation: ${programName} completion >> $PROFILE\n#\n\nRegister-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {\n param($wordToComplete, $commandAst, $cursorPosition)\n\n $commands = @(${commandNames})\n $args = @(${argNames.join(', ')})\n${enumBlock} if ($wordToComplete -like '-*') {\n $args | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }\n } else {\n $commands | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }\n }\n}\n###-end-${programName}-completion-###`;\n}\n\n/**\n * Generates a completion script for the specified shell.\n */\nexport function generateCompletion(program: AnyPadroneCommand, shell: ShellType): string {\n switch (shell) {\n case 'bash':\n return generateBashCompletion(program);\n case 'zsh':\n return generateZshCompletion(program);\n case 'fish':\n return generateFishCompletion(program);\n case 'powershell':\n return generatePowerShellCompletion(program);\n default:\n throw new Error(`Unsupported shell: ${shell}`);\n }\n}\n\n/**\n * Gets the installation instructions for a shell completion script.\n */\nexport function getCompletionInstallInstructions(programName: string, shell: ShellType): string {\n switch (shell) {\n case 'bash':\n return `# Add to ~/.bashrc:\n${programName} completion bash >> ~/.bashrc\n\n# Or install system-wide:\n${programName} completion bash > /usr/local/etc/bash_completion.d/${programName}`;\n\n case 'zsh':\n return `# Add to ~/.zshrc:\n${programName} completion zsh >> ~/.zshrc\n\n# Or add to completions directory:\n${programName} completion zsh > ~/.zsh/completions/_${programName}`;\n\n case 'fish':\n return `# Install to Fish completions:\n${programName} completion fish > ~/.config/fish/completions/${programName}.fish`;\n\n case 'powershell':\n return `# Add to PowerShell profile:\n${programName} completion powershell >> $PROFILE`;\n\n default:\n return `# Run: ${programName} completion <shell>\n# Supported shells: bash, zsh, fish, powershell`;\n }\n}\n\n/**\n * Generates the completion output with automatic shell detection.\n * If shell is not specified, detects the current shell and provides instructions.\n */\nexport async function generateCompletionOutput(program: AnyPadroneCommand, shell?: ShellType): Promise<string> {\n const programName = program.name;\n\n if (shell) {\n return generateCompletion(program, shell);\n }\n\n // Auto-detect shell and provide instructions\n const detectedShell = await detectShell();\n\n if (detectedShell) {\n const instructions = getCompletionInstallInstructions(programName, detectedShell);\n const script = generateCompletion(program, detectedShell);\n\n return `# Detected shell: ${detectedShell}\n#\n${instructions}\n#\n# Or evaluate directly (temporary, for current session only):\n# eval \"$(${programName} completion ${detectedShell})\"\n\n${script}`;\n }\n\n // Could not detect shell - provide usage info\n return `# Shell auto-detection failed.\n#\n# Usage: ${programName} completion <shell>\n#\n# Supported shells:\n# bash - Bash completion script\n# zsh - Zsh completion script\n# fish - Fish completion script\n# powershell - PowerShell completion script\n#\n# Example:\n# ${programName} completion bash >> ~/.bashrc\n# ${programName} completion zsh >> ~/.zshrc\n# ${programName} completion fish > ~/.config/fish/completions/${programName}.fish\n# ${programName} completion powershell >> $PROFILE`;\n}\n\nexport interface SetupCompletionsResult {\n /** The file that was written to. */\n file: string;\n /** Whether an existing completion block was replaced (true) or a new one was appended (false). */\n updated: boolean;\n}\n\n/**\n * Sets up shell completions by writing an eval snippet to the appropriate shell config file.\n * Uses marker comments for idempotency — re-running replaces the existing block.\n */\nexport async function setupCompletions(programName: string, shell: ShellType): Promise<SetupCompletionsResult> {\n const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { homedir } = await import('node:os');\n\n const beginMarker = `###-begin-${programName}-completion-###`;\n const endMarker = `###-end-${programName}-completion-###`;\n const snippet = buildSetupSnippet(programName, shell, beginMarker, endMarker);\n\n if (shell === 'fish') {\n const completionsDir = join(homedir(), '.config', 'fish', 'completions');\n const filePath = join(completionsDir, `${programName}.fish`);\n mkdirSync(completionsDir, { recursive: true });\n const existed = existsSync(filePath);\n writeFileSync(filePath, `${snippet}\\n`);\n return { file: filePath, updated: existed };\n }\n\n const rcFile = await getRcFile(shell);\n if (!rcFile) {\n throw new Error(`Could not determine config file for ${shell}.`);\n }\n\n return writeToRcFile(rcFile, snippet, beginMarker, endMarker);\n}\n\nfunction buildSetupSnippet(programName: string, shell: ShellType, beginMarker: string, endMarker: string): string {\n const evalCmd = `${programName} completion ${shell}`;\n\n switch (shell) {\n case 'bash':\n case 'zsh':\n return `${beginMarker}\\neval \"$(${evalCmd})\"\\n${endMarker}`;\n case 'fish':\n return `${beginMarker}\\n${evalCmd} | source\\n${endMarker}`;\n case 'powershell':\n return `${beginMarker}\\n${evalCmd} | Invoke-Expression\\n${endMarker}`;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ import { getJsonSchema } from "../core/args.mjs";
2
+ //#region src/feature/interactive.ts
3
+ /**
4
+ * Auto-detect the prompt type for a field based on its JSON schema property definition.
5
+ */
6
+ function detectPromptConfig(name, propSchema, description) {
7
+ const message = description || propSchema?.description || name;
8
+ if (!propSchema) return {
9
+ name,
10
+ message,
11
+ type: "input"
12
+ };
13
+ if (propSchema.type === "boolean") return {
14
+ name,
15
+ message,
16
+ type: "confirm",
17
+ default: propSchema.default
18
+ };
19
+ if (propSchema.enum) return {
20
+ name,
21
+ message,
22
+ type: "select",
23
+ choices: propSchema.enum.map((v) => ({
24
+ label: String(v),
25
+ value: v
26
+ })),
27
+ default: propSchema.default
28
+ };
29
+ if (propSchema.type === "array" && propSchema.items?.enum) return {
30
+ name,
31
+ message,
32
+ type: "multiselect",
33
+ choices: propSchema.items.enum.map((v) => ({
34
+ label: String(v),
35
+ value: v
36
+ })),
37
+ default: propSchema.default
38
+ };
39
+ if (propSchema.format === "password") return {
40
+ name,
41
+ message,
42
+ type: "password",
43
+ default: propSchema.default
44
+ };
45
+ return {
46
+ name,
47
+ message,
48
+ type: "input",
49
+ default: propSchema.default
50
+ };
51
+ }
52
+ /**
53
+ * Prompt a single field and validate it against the command's schema.
54
+ * Re-prompts with a warning until the user provides a valid value.
55
+ */
56
+ async function promptWithValidation(field, config, currentData, command, runtime) {
57
+ let promptConfig = config;
58
+ while (true) {
59
+ const value = await runtime.prompt(promptConfig);
60
+ if (!command.argsSchema) return value;
61
+ const testData = {
62
+ ...currentData,
63
+ [field]: value
64
+ };
65
+ const validated = await command.argsSchema["~standard"].validate(testData);
66
+ if (!validated.issues) return value;
67
+ const fieldIssues = validated.issues.filter((issue) => {
68
+ const rootKey = issue.path?.[0];
69
+ return rootKey !== void 0 && String(rootKey) === field;
70
+ });
71
+ if (fieldIssues.length === 0) return value;
72
+ const messages = fieldIssues.map((i) => i.message).join("; ");
73
+ runtime.error(`Invalid value for "${field}": ${messages}`);
74
+ promptConfig = {
75
+ ...config,
76
+ default: value
77
+ };
78
+ }
79
+ }
80
+ /**
81
+ * Prompt for missing interactive fields.
82
+ * Runs after env/config preprocessing and before schema validation.
83
+ *
84
+ * When `force` is true, all configured interactive fields are prompted even if they already
85
+ * have values. The current values are used as defaults in the prompts.
86
+ */
87
+ async function promptInteractiveFields(data, command, runtime, force) {
88
+ if (!runtime.prompt) return data;
89
+ const meta = command.meta;
90
+ const interactiveConfig = meta?.interactive;
91
+ const optionalInteractiveConfig = meta?.optionalInteractive;
92
+ if (!interactiveConfig && !optionalInteractiveConfig) return data;
93
+ let jsonProperties = {};
94
+ let requiredFields = /* @__PURE__ */ new Set();
95
+ if (command.argsSchema) try {
96
+ const jsonSchema = getJsonSchema(command.argsSchema);
97
+ if (jsonSchema.type === "object" && jsonSchema.properties) jsonProperties = jsonSchema.properties;
98
+ if (Array.isArray(jsonSchema.required)) requiredFields = new Set(jsonSchema.required);
99
+ } catch {}
100
+ const fieldDescriptions = {};
101
+ if (meta?.fields) {
102
+ for (const [key, value] of Object.entries(meta.fields)) if (value?.description) fieldDescriptions[key] = value.description;
103
+ }
104
+ const result = { ...data };
105
+ let fieldsToPrompt = [];
106
+ if (interactiveConfig === true) if (force) fieldsToPrompt = [...requiredFields];
107
+ else fieldsToPrompt = [...requiredFields].filter((name) => result[name] === void 0);
108
+ else if (Array.isArray(interactiveConfig)) if (force) fieldsToPrompt = [...interactiveConfig];
109
+ else fieldsToPrompt = interactiveConfig.filter((name) => result[name] === void 0);
110
+ for (const field of fieldsToPrompt) {
111
+ const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);
112
+ if (force && result[field] !== void 0) config.default = result[field];
113
+ result[field] = await promptWithValidation(field, config, result, command, runtime);
114
+ }
115
+ let optionalFields = [];
116
+ if (optionalInteractiveConfig === true) if (force) optionalFields = Object.keys(jsonProperties).filter((name) => !requiredFields.has(name));
117
+ else optionalFields = Object.keys(jsonProperties).filter((name) => !requiredFields.has(name) && result[name] === void 0);
118
+ else if (Array.isArray(optionalInteractiveConfig)) if (force) optionalFields = [...optionalInteractiveConfig];
119
+ else optionalFields = optionalInteractiveConfig.filter((name) => result[name] === void 0);
120
+ if (optionalFields.length > 0) {
121
+ const selected = await runtime.prompt({
122
+ name: "_optionalFields",
123
+ message: "Would you also like to configure:",
124
+ type: "multiselect",
125
+ choices: optionalFields.map((f) => {
126
+ const label = fieldDescriptions[f] || jsonProperties[f]?.description || f;
127
+ const currentValue = result[f];
128
+ return {
129
+ label: force && currentValue !== void 0 ? `${label} (current: ${currentValue})` : label,
130
+ value: f
131
+ };
132
+ })
133
+ });
134
+ if (Array.isArray(selected)) for (const field of selected) {
135
+ const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);
136
+ if (force && result[field] !== void 0) config.default = result[field];
137
+ result[field] = await promptWithValidation(field, config, result, command, runtime);
138
+ }
139
+ }
140
+ return result;
141
+ }
142
+ //#endregion
143
+ export { detectPromptConfig, promptInteractiveFields };
144
+
145
+ //# sourceMappingURL=interactive.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.mjs","names":[],"sources":["../../src/feature/interactive.ts"],"sourcesContent":["import { getJsonSchema } from '../core/args.ts';\nimport type { InteractivePromptConfig, ResolvedPadroneRuntime } from '../core/runtime.ts';\nimport type { AnyPadroneCommand } from '../types/index.ts';\n\n/**\n * Auto-detect the prompt type for a field based on its JSON schema property definition.\n */\nexport function detectPromptConfig(\n name: string,\n propSchema: Record<string, any> | undefined,\n description?: string,\n): InteractivePromptConfig {\n const message = description || propSchema?.description || name;\n\n if (!propSchema) return { name, message, type: 'input' };\n\n if (propSchema.type === 'boolean') {\n return { name, message, type: 'confirm', default: propSchema.default };\n }\n\n if (propSchema.enum) {\n return {\n name,\n message,\n type: 'select',\n choices: propSchema.enum.map((v: unknown) => ({ label: String(v), value: v })),\n default: propSchema.default,\n };\n }\n\n if (propSchema.type === 'array' && propSchema.items?.enum) {\n return {\n name,\n message,\n type: 'multiselect',\n choices: propSchema.items.enum.map((v: unknown) => ({ label: String(v), value: v })),\n default: propSchema.default,\n };\n }\n\n if (propSchema.format === 'password') {\n return { name, message, type: 'password', default: propSchema.default };\n }\n\n return { name, message, type: 'input', default: propSchema.default };\n}\n\n/**\n * Prompt a single field and validate it against the command's schema.\n * Re-prompts with a warning until the user provides a valid value.\n */\nasync function promptWithValidation(\n field: string,\n config: InteractivePromptConfig,\n currentData: Record<string, unknown>,\n command: AnyPadroneCommand,\n runtime: ResolvedPadroneRuntime,\n): Promise<unknown> {\n let promptConfig = config;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const value = await runtime.prompt!(promptConfig);\n\n if (!command.argsSchema) return value;\n\n // Validate the full object with the new value to catch field-level issues\n const testData = { ...currentData, [field]: value };\n const validated = await command.argsSchema['~standard'].validate(testData);\n\n if (!validated.issues) return value;\n\n // Only keep issues whose path starts with this field\n const fieldIssues = validated.issues.filter((issue: { path?: ReadonlyArray<PropertyKey> }) => {\n const rootKey = issue.path?.[0];\n return rootKey !== undefined && String(rootKey) === field;\n });\n\n if (fieldIssues.length === 0) return value;\n\n // Warn the user and re-prompt with the invalid value as default\n const messages = fieldIssues.map((i: { message: string }) => i.message).join('; ');\n runtime.error(`Invalid value for \"${field}\": ${messages}`);\n promptConfig = { ...config, default: value };\n }\n}\n\n/**\n * Prompt for missing interactive fields.\n * Runs after env/config preprocessing and before schema validation.\n *\n * When `force` is true, all configured interactive fields are prompted even if they already\n * have values. The current values are used as defaults in the prompts.\n */\nexport async function promptInteractiveFields(\n data: Record<string, unknown>,\n command: AnyPadroneCommand,\n runtime: ResolvedPadroneRuntime,\n force?: boolean,\n): Promise<Record<string, unknown>> {\n if (!runtime.prompt) return data;\n\n const meta = command.meta;\n const interactiveConfig = meta?.interactive;\n const optionalInteractiveConfig = meta?.optionalInteractive;\n if (!interactiveConfig && !optionalInteractiveConfig) return data;\n\n // Extract JSON schema properties for prompt type detection\n let jsonProperties: Record<string, any> = {};\n let requiredFields: Set<string> = new Set();\n if (command.argsSchema) {\n try {\n const jsonSchema = getJsonSchema(command.argsSchema) as Record<string, any>;\n if (jsonSchema.type === 'object' && jsonSchema.properties) {\n jsonProperties = jsonSchema.properties;\n }\n if (Array.isArray(jsonSchema.required)) {\n requiredFields = new Set(jsonSchema.required);\n }\n } catch {\n // Ignore schema parsing errors\n }\n }\n\n const fieldDescriptions: Record<string, string | undefined> = {};\n if (meta?.fields) {\n for (const [key, value] of Object.entries(meta.fields)) {\n if (value?.description) fieldDescriptions[key] = value.description;\n }\n }\n\n const result = { ...data };\n\n // Determine which required interactive fields to prompt\n let fieldsToPrompt: string[] = [];\n if (interactiveConfig === true) {\n if (force) {\n // When forced, prompt all required fields regardless of current value\n fieldsToPrompt = [...requiredFields];\n } else {\n // All required fields that are missing\n fieldsToPrompt = [...requiredFields].filter((name) => result[name] === undefined);\n }\n } else if (Array.isArray(interactiveConfig)) {\n if (force) {\n fieldsToPrompt = [...interactiveConfig];\n } else {\n fieldsToPrompt = interactiveConfig.filter((name) => result[name] === undefined);\n }\n }\n\n // Prompt each required interactive field with per-field validation\n for (const field of fieldsToPrompt) {\n const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);\n // When forced, use the current value as the default\n if (force && result[field] !== undefined) {\n config.default = result[field];\n }\n result[field] = await promptWithValidation(field, config, result, command, runtime);\n }\n\n // Determine optional interactive fields\n let optionalFields: string[] = [];\n if (optionalInteractiveConfig === true) {\n if (force) {\n // When forced, include all non-required fields (even those with values)\n const allKeys = Object.keys(jsonProperties);\n optionalFields = allKeys.filter((name) => !requiredFields.has(name));\n } else {\n // All non-required fields that are still missing\n const allKeys = Object.keys(jsonProperties);\n optionalFields = allKeys.filter((name) => !requiredFields.has(name) && result[name] === undefined);\n }\n } else if (Array.isArray(optionalInteractiveConfig)) {\n if (force) {\n optionalFields = [...optionalInteractiveConfig];\n } else {\n optionalFields = optionalInteractiveConfig.filter((name) => result[name] === undefined);\n }\n }\n\n // Show multiselect for optional fields, then prompt selected ones\n if (optionalFields.length > 0) {\n const selected = (await runtime.prompt({\n name: '_optionalFields',\n message: 'Would you also like to configure:',\n type: 'multiselect',\n choices: optionalFields.map((f) => {\n const label = fieldDescriptions[f] || jsonProperties[f]?.description || f;\n const currentValue = result[f];\n // When forced, show current value next to the label for fields that already have values\n const displayLabel = force && currentValue !== undefined ? `${label} (current: ${currentValue})` : label;\n return { label: displayLabel, value: f };\n }),\n })) as string[];\n\n if (Array.isArray(selected)) {\n for (const field of selected) {\n const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);\n // When forced, use the current value as the default\n if (force && result[field] !== undefined) {\n config.default = result[field];\n }\n result[field] = await promptWithValidation(field, config, result, command, runtime);\n }\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ //#region src/feature/mcp.d.ts
2
+ type PadroneMcpPreferences = {
3
+ /** Server name. Defaults to the program name. */name?: string; /** Server version. Defaults to the program version. */
4
+ version?: string;
5
+ /**
6
+ * Transport mode.
7
+ * - `'http'` — Start a Streamable HTTP server (default). Responds with `application/json` or `text/event-stream` based on the client's `Accept` header. Use `port` and `host` to configure.
8
+ * - `'stdio'` — Communicate over stdin/stdout with newline-delimited JSON.
9
+ */
10
+ transport?: 'http' | 'stdio'; /** HTTP port. Defaults to `3000`. Only used with `transport: 'http'`. */
11
+ port?: number; /** HTTP host. Defaults to `'127.0.0.1'`. Only used with `transport: 'http'`. */
12
+ host?: string; /** Base path for the MCP endpoint. Defaults to `'/mcp'`. Only used with `transport: 'http'`. */
13
+ basePath?: string; /** CORS allowed origin. Defaults to `'*'`. Set to a specific origin or `false` to disable CORS headers. Only used with HTTP transports. */
14
+ cors?: string | false;
15
+ };
16
+ //#endregion
17
+ export { PadroneMcpPreferences };
18
+ //# sourceMappingURL=mcp.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.mts","names":[],"sources":["../../src/feature/mcp.ts"],"mappings":";KAKY,qBAAA;mDAEV,IAAA,WAF+B;EAI/B,OAAA;EAJ+B;;;;;EAU/B,SAAA,qBAMA;EAJA,IAAA,WAMI;EAJJ,IAAA;EAEA,QAAA;EAEA,IAAA;AAAA"}
@@ -1,6 +1,6 @@
1
- import { m as serializeArgsToFlags, r as collectEndpoints, t as buildInputSchema } from "./commands-DLR0rFgq.mjs";
2
- import { a as readStreamAsText } from "./stream-DC4H8YTx.mjs";
3
- import { t as generateHelp } from "./help-B-ZMYyn-.mjs";
1
+ import { readStreamAsText } from "../util/stream.mjs";
2
+ import { buildInputSchema, collectEndpoints, serializeArgsToFlags } from "../core/commands.mjs";
3
+ import { generateHelp } from "../output/help.mjs";
4
4
  //#region src/feature/mcp.ts
5
5
  const PROTOCOL_VERSION = "2025-11-25";
6
6
  /** Convert an endpoint dot-path to a valid MCP tool name. Spec allows: [A-Za-z0-9_\-\.] */
@@ -367,11 +367,11 @@ async function startHttpTransport(handleRequest, prefs, log, onSignal) {
367
367
  async function startMcpServer(_program, existingCommand, evalCommand, prefs) {
368
368
  const handleRequest = createMcpHandler(existingCommand, evalCommand, prefs);
369
369
  if ((prefs?.transport ?? "http") === "stdio") return startStdioTransport(handleRequest);
370
- const { getCommandRuntime } = await import("./commands-DLR0rFgq.mjs").then((n) => n.a);
370
+ const { getCommandRuntime } = await import("../core/commands.mjs");
371
371
  const runtime = getCommandRuntime(existingCommand);
372
372
  return startHttpTransport(handleRequest, prefs ?? {}, (msg) => runtime.error(msg), runtime.onSignal);
373
373
  }
374
374
  //#endregion
375
- export { startMcpServer };
375
+ export { createMcpHandler, startMcpServer };
376
376
 
377
- //# sourceMappingURL=mcp-D6PdtjIs.mjs.map
377
+ //# sourceMappingURL=mcp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.mjs","names":[],"sources":["../../src/feature/mcp.ts"],"sourcesContent":["import { buildInputSchema, collectEndpoints, serializeArgsToFlags } from '../core/commands.ts';\nimport { generateHelp } from '../output/help.ts';\nimport type { AnyPadroneCommand, AnyPadroneProgram } from '../types/index.ts';\nimport { readStreamAsText } from '../util/stream.ts';\n\nexport type PadroneMcpPreferences = {\n /** Server name. Defaults to the program name. */\n name?: string;\n /** Server version. Defaults to the program version. */\n version?: string;\n /**\n * Transport mode.\n * - `'http'` — Start a Streamable HTTP server (default). Responds with `application/json` or `text/event-stream` based on the client's `Accept` header. Use `port` and `host` to configure.\n * - `'stdio'` — Communicate over stdin/stdout with newline-delimited JSON.\n */\n transport?: 'http' | 'stdio';\n /** HTTP port. Defaults to `3000`. Only used with `transport: 'http'`. */\n port?: number;\n /** HTTP host. Defaults to `'127.0.0.1'`. Only used with `transport: 'http'`. */\n host?: string;\n /** Base path for the MCP endpoint. Defaults to `'/mcp'`. Only used with `transport: 'http'`. */\n basePath?: string;\n /** CORS allowed origin. Defaults to `'*'`. Set to a specific origin or `false` to disable CORS headers. Only used with HTTP transports. */\n cors?: string | false;\n};\n\nconst PROTOCOL_VERSION = '2025-11-25';\n\ntype JsonRpcRequest = {\n jsonrpc: '2.0';\n id?: string | number;\n method: string;\n params?: Record<string, unknown>;\n};\n\ntype JsonRpcResponse = {\n jsonrpc: '2.0';\n id: string | number | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n};\n\n/** Convert an endpoint dot-path to a valid MCP tool name. Spec allows: [A-Za-z0-9_\\-\\.] */\nfunction toToolName(path: string): string {\n return path.replace(/\\s+/g, '.');\n}\n\n/** Convert a tool name back to a command path (dot → space). */\nfunction toCommandPath(toolName: string): string {\n return toolName.replace(/\\./g, ' ');\n}\n\n/** Build MCP tool annotations from a command's metadata. */\nfunction buildAnnotations(cmd: AnyPadroneCommand) {\n if (cmd.mutation == null) return undefined;\n return {\n destructiveHint: cmd.mutation || undefined,\n readOnlyHint: cmd.mutation === false || undefined,\n };\n}\n\n/** Build an MCP tool definition from a command. */\nfunction buildToolDefinition(name: string, cmd: AnyPadroneCommand) {\n return {\n name: toToolName(name),\n title: cmd.title ?? undefined,\n description: cmd.description || cmd.title || `Run the \"${name}\" command`,\n inputSchema: buildInputSchema(cmd),\n annotations: buildAnnotations(cmd),\n };\n}\n\n/** Create the MCP request handler. Returns an async function that processes a JSON-RPC request and returns a response (or undefined for notifications). */\nexport function createMcpHandler(\n existingCommand: AnyPadroneCommand,\n evalCommand: AnyPadroneProgram['eval'],\n prefs?: PadroneMcpPreferences,\n) {\n const serverName = prefs?.name ?? existingCommand.name;\n const serverVersion = prefs?.version ?? existingCommand.version ?? '0.0.0';\n\n const rootTools = collectEndpoints(existingCommand.commands, '');\n if (existingCommand.action || existingCommand.argsSchema) {\n rootTools.unshift({ name: '', command: existingCommand });\n }\n\n const toolMap = new Map(rootTools.map((t) => [toToolName(t.name), t]));\n\n const helpToolName = 'help';\n const helpToolDef = {\n name: helpToolName,\n title: 'Help',\n description: `Show help for the \"${serverName}\" program or a specific command`,\n inputSchema: {\n type: 'object' as const,\n properties: { command: { type: 'string', description: 'Command name to get help for (omit for program help)' } },\n additionalProperties: false,\n },\n };\n\n return async function handleRequest(req: JsonRpcRequest): Promise<JsonRpcResponse | undefined> {\n const { id, method, params } = req;\n\n switch (method) {\n case 'initialize':\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n result: {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: { name: serverName, version: serverVersion },\n },\n };\n\n case 'notifications/initialized':\n case 'notifications/cancelled':\n return undefined;\n\n case 'ping':\n return { jsonrpc: '2.0', id: id ?? null, result: {} };\n\n case 'tools/list': {\n const tools = [...rootTools.map((t) => buildToolDefinition(t.name, t.command)), helpToolDef];\n return { jsonrpc: '2.0', id: id ?? null, result: { tools } };\n }\n\n case 'tools/call': {\n const toolName = params?.name as string;\n const args = (params?.arguments ?? {}) as Record<string, unknown>;\n\n // Built-in help tool\n if (toolName === helpToolName) {\n const cmdName = args.command as string | undefined;\n const targetCmd = cmdName ? rootTools.find((t) => t.name === cmdName || toToolName(t.name) === cmdName)?.command : undefined;\n const helpText = generateHelp(existingCommand, targetCmd ?? existingCommand, { format: 'text', detail: 'full' });\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n result: { content: [{ type: 'text', text: helpText }], isError: false },\n };\n }\n\n const tool = toolMap.get(toolName);\n if (!tool) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: -32602, message: `Unknown tool: ${toolName}` },\n };\n }\n\n // Build command string: convert tool name back to command path + serialize args as flags\n const commandPath = toCommandPath(tool.name);\n const argParts = serializeArgsToFlags(args);\n const input = [commandPath, ...argParts].filter(Boolean).join(' ') || undefined;\n\n try {\n const output: string[] = [];\n const errors: string[] = [];\n const result = await evalCommand(input as any, {\n caller: 'mcp',\n runtime: {\n output: (...outArgs: unknown[]) => output.push(outArgs.map(String).join(' ')),\n error: (text: string) => errors.push(text),\n interactive: 'unsupported',\n format: 'text',\n },\n });\n\n const content: { type: string; text: string }[] = [];\n\n if (result.error) {\n const errorMsg = result.error instanceof Error ? result.error.message : String(result.error);\n if (errors.length) content.push({ type: 'text', text: errors.join('\\n') });\n content.push({ type: 'text', text: errorMsg });\n return { jsonrpc: '2.0', id: id ?? null, result: { content, isError: true } };\n }\n\n if (result.argsResult?.issues) {\n const issueMessages = result.argsResult.issues.map((i: any) => `${i.path?.join('.') || 'root'}: ${i.message}`).join('\\n');\n content.push({ type: 'text', text: `Validation error:\\n${issueMessages}` });\n return { jsonrpc: '2.0', id: id ?? null, result: { content, isError: true } };\n }\n\n if (output.length) content.push({ type: 'text', text: output.join('\\n') });\n if (result.result !== undefined && result.result !== null) {\n const resultText = typeof result.result === 'string' ? result.result : JSON.stringify(result.result, null, 2);\n content.push({ type: 'text', text: resultText });\n }\n if (content.length === 0) content.push({ type: 'text', text: 'Done.' });\n return { jsonrpc: '2.0', id: id ?? null, result: { content, isError: false } };\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n result: { content: [{ type: 'text', text: errorMsg }], isError: true },\n };\n }\n }\n\n default: {\n if (id !== undefined) {\n return { jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } };\n }\n return undefined;\n }\n }\n };\n}\n\n/** stdio transport: newline-delimited JSON per 2025-11-25 spec. */\nasync function startStdioTransport(handleRequest: (req: JsonRpcRequest) => Promise<JsonRpcResponse | undefined>): Promise<void> {\n const { stdin, stdout } = await import('node:process');\n const { createInterface } = await import('node:readline');\n\n function send(msg: JsonRpcResponse) {\n stdout.write(`${JSON.stringify(msg)}\\n`);\n }\n\n const rl = createInterface({ input: stdin, crlfDelay: Infinity });\n\n for await (const line of rl) {\n if (!line.trim()) continue;\n try {\n const req = JSON.parse(line) as JsonRpcRequest;\n const res = await handleRequest(req);\n if (res) send(res);\n } catch {\n // Ignore malformed JSON\n }\n }\n}\n\n/** Streamable HTTP transport per 2025-11-25 spec. Responds with JSON or SSE based on client's Accept header. */\nasync function startHttpTransport(\n handleRequest: (req: JsonRpcRequest) => Promise<JsonRpcResponse | undefined>,\n prefs: PadroneMcpPreferences,\n log: (msg: string) => void,\n onSignal?: (callback: () => void) => () => void,\n): Promise<void> {\n const http = await import('node:http');\n const crypto = await import('node:crypto');\n\n const port = prefs.port ?? 3000;\n const host = prefs.host ?? '127.0.0.1';\n const endpoint = prefs.basePath ?? '/mcp';\n\n // Session management\n let sessionId: string | undefined;\n let negotiatedVersion: string | undefined;\n\n const corsOrigin = prefs.cors !== false ? (prefs.cors ?? '*') : undefined;\n\n const server = http.createServer(async (req, res) => {\n // CORS headers\n if (corsOrigin) {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, MCP-Session-Id, MCP-Protocol-Version');\n res.setHeader('Access-Control-Expose-Headers', 'MCP-Session-Id');\n }\n\n if (req.method === 'OPTIONS') {\n res.writeHead(corsOrigin ? 204 : 405);\n res.end();\n return;\n }\n\n if (req.url !== endpoint) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not found' }));\n return;\n }\n\n // DELETE: terminate session\n if (req.method === 'DELETE') {\n const reqSessionId = req.headers['mcp-session-id'] as string | undefined;\n if (sessionId && reqSessionId === sessionId) {\n sessionId = undefined;\n negotiatedVersion = undefined;\n res.writeHead(200);\n res.end();\n } else {\n res.writeHead(404);\n res.end();\n }\n return;\n }\n\n // GET: SSE stream (not implemented — return 405)\n if (req.method === 'GET') {\n res.writeHead(405, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32601, message: 'SSE stream not supported' } }));\n return;\n }\n\n if (req.method !== 'POST') {\n res.writeHead(405);\n res.end();\n return;\n }\n\n // Validate session ID on non-initialize requests\n const reqSessionId = req.headers['mcp-session-id'] as string | undefined;\n if (sessionId && reqSessionId && reqSessionId !== sessionId) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32600, message: 'Invalid session' } }));\n return;\n }\n\n // Validate MCP-Protocol-Version header on post-init requests\n const reqProtocolVersion = req.headers['mcp-protocol-version'] as string | undefined;\n if (negotiatedVersion && reqProtocolVersion && reqProtocolVersion !== negotiatedVersion) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32600, message: 'Protocol version mismatch' } }));\n return;\n }\n\n // Read request body\n const body = await readStreamAsText(req as AsyncIterable<Uint8Array>);\n\n let rpcRequest: JsonRpcRequest;\n try {\n rpcRequest = JSON.parse(body);\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }));\n return;\n }\n\n const response = await handleRequest(rpcRequest);\n\n // On initialize response: create session and set header\n if (rpcRequest.method === 'initialize' && response?.result) {\n sessionId = crypto.randomUUID();\n negotiatedVersion = PROTOCOL_VERSION;\n res.setHeader('MCP-Session-Id', sessionId);\n }\n\n if (response) {\n const accept = req.headers.accept ?? '';\n if (accept.includes('text/event-stream')) {\n res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });\n res.write(`event: message\\ndata: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n } else {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(response));\n }\n } else {\n // Notification or response from client — no body\n res.writeHead(202);\n res.end();\n }\n });\n\n return new Promise<void>((resolve, reject) => {\n server.listen(port, host, () => {\n log(`MCP server listening on http://${host}:${port}${endpoint}`);\n });\n server.on('error', reject);\n const unsubscribe = onSignal?.(() => {\n server.close(() => resolve());\n });\n server.on('close', () => unsubscribe?.());\n });\n}\n\nexport async function startMcpServer(\n _program: AnyPadroneProgram,\n existingCommand: AnyPadroneCommand,\n evalCommand: AnyPadroneProgram['eval'],\n prefs?: PadroneMcpPreferences,\n): Promise<void> {\n const handleRequest = createMcpHandler(existingCommand, evalCommand, prefs);\n const transport = prefs?.transport ?? 'http';\n\n if (transport === 'stdio') {\n return startStdioTransport(handleRequest);\n }\n\n const { getCommandRuntime } = await import('../core/commands.ts');\n const runtime = getCommandRuntime(existingCommand);\n return startHttpTransport(handleRequest, prefs ?? {}, (msg) => runtime.error(msg), runtime.onSignal);\n}\n"],"mappings":"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"}
@@ -0,0 +1,217 @@
1
+ import { REPL_SIGINT } from "../core/runtime.mjs";
2
+ import { createTerminalReplSession } from "../core/default-runtime.mjs";
3
+ import { buildReplCompleter, findCommandByName, getCommandRuntime } from "../core/commands.mjs";
4
+ import { getVersion } from "../util/utils.mjs";
5
+ //#region src/feature/repl-loop.ts
6
+ /**
7
+ * Creates a REPL async iterable for running commands interactively.
8
+ */
9
+ function createReplIterator(deps, options) {
10
+ const { existingCommand, evalCommand, replActiveRef } = deps;
11
+ if (replActiveRef.value) {
12
+ getCommandRuntime(existingCommand).error("REPL is already running. Nested REPL sessions are not supported.");
13
+ return (async function* () {})();
14
+ }
15
+ const runtime = getCommandRuntime(existingCommand);
16
+ const programName = existingCommand.name || "padrone";
17
+ const env = runtime.env();
18
+ const useAnsi = runtime.format === "ansi" || runtime.format === "auto" && !env.NO_COLOR && !env.CI && runtime.terminal?.isTTY === true;
19
+ const commandHistory = [];
20
+ const resolveScope = (scope) => {
21
+ const parts = scope.split(/\s+/);
22
+ const stack = [];
23
+ let current = existingCommand;
24
+ for (const part of parts) {
25
+ const found = findCommandByName(part, current.commands);
26
+ if (!found) break;
27
+ stack.push(found);
28
+ current = found;
29
+ }
30
+ return stack;
31
+ };
32
+ async function* replIterator() {
33
+ replActiveRef.value = true;
34
+ const showGreeting = options?.greeting !== false;
35
+ const showHint = options?.hint !== false;
36
+ if (showGreeting || showHint) runtime.output("");
37
+ if (showGreeting) if (options?.greeting) runtime.output(options.greeting);
38
+ else {
39
+ const displayName = existingCommand.title || programName;
40
+ const version = existingCommand.version ? await getVersion(existingCommand.version) : void 0;
41
+ const greeting = version ? `Welcome to ${displayName} v${version}` : `Welcome to ${displayName}`;
42
+ runtime.output(greeting);
43
+ }
44
+ if (showHint) {
45
+ const hintText = (typeof options?.hint === "string" ? options.hint : void 0) ?? "Type \".help\" for more information, \".exit\" to quit.";
46
+ runtime.output(useAnsi ? `\x1b[2m${hintText}\x1b[0m` : hintText);
47
+ }
48
+ if (showGreeting || showHint) runtime.output("");
49
+ const scopeStack = options?.scope ? resolveScope(options.scope) : [];
50
+ const getScopeCommand = () => scopeStack.length ? scopeStack[scopeStack.length - 1] : existingCommand;
51
+ const getScopePath = () => scopeStack.map((c) => c.name).join(" ");
52
+ const buildPrompt = () => {
53
+ if (options?.prompt) return typeof options.prompt === "function" ? options.prompt() : options.prompt;
54
+ const scopePath = getScopePath();
55
+ const label = scopePath ? `${programName}/${scopePath.replace(/ /g, "/")}` : programName;
56
+ return useAnsi ? `\x1b[1m${label}\x1b[0m ❯ ` : `${label} ❯ `;
57
+ };
58
+ const buildScopedCompleter = () => {
59
+ return buildReplCompleter(getScopeCommand(), { inScope: scopeStack.length > 0 });
60
+ };
61
+ const sessionConfig = { history: options?.history };
62
+ if (options?.completion !== false) sessionConfig.completer = buildScopedCompleter();
63
+ const session = runtime.readLine ? void 0 : createTerminalReplSession(sessionConfig);
64
+ const questionFn = session ? (prompt) => session.question(prompt) : runtime.readLine;
65
+ const updateCompleter = () => {
66
+ if (options?.completion === false) return;
67
+ const completer = buildScopedCompleter();
68
+ if (session) session.completer = completer;
69
+ sessionConfig.completer = completer;
70
+ };
71
+ let lastSigintTime = 0;
72
+ try {
73
+ while (true) {
74
+ const input = await questionFn(buildPrompt());
75
+ if (input === null) break;
76
+ if (input === REPL_SIGINT) {
77
+ const now = Date.now();
78
+ if (now - lastSigintTime < 2e3) break;
79
+ lastSigintTime = now;
80
+ runtime.output("(press Ctrl+C again to exit, or Ctrl+D)");
81
+ continue;
82
+ }
83
+ const trimmed = input.trim();
84
+ if (!trimmed) continue;
85
+ lastSigintTime = 0;
86
+ commandHistory.push(trimmed);
87
+ if (trimmed === ".exit" || trimmed === ".quit") break;
88
+ if (trimmed === ".clear") {
89
+ runtime.output("\x1B[2J\x1B[H");
90
+ continue;
91
+ }
92
+ if (trimmed === ".help") {
93
+ const lines = [
94
+ "REPL Commands:",
95
+ " . Execute the current scoped command",
96
+ " .help Print this help message",
97
+ " .exit Exit the REPL",
98
+ " .clear Clear the screen",
99
+ " .history Show command history",
100
+ " .scope <cmd> Scope into a subcommand",
101
+ " .scope .. Go up one scope level"
102
+ ];
103
+ lines.push("", "Keybindings:", " Ctrl+C Cancel current line (press twice to exit)", " Ctrl+D Exit the REPL", " Up/Down Navigate history", " Tab Auto-complete", "", "Type \"help\" to see available commands.");
104
+ runtime.output(lines.join("\n"));
105
+ continue;
106
+ }
107
+ if (trimmed === ".history") {
108
+ const entries = commandHistory.slice(0, -1);
109
+ if (entries.length === 0) runtime.output("No history.");
110
+ else runtime.output(entries.map((entry, i) => `${i + 1} ${entry}`).join("\n"));
111
+ continue;
112
+ }
113
+ if (trimmed.startsWith(".scope ") || trimmed === ".scope") {
114
+ const target = trimmed.slice(6).trim();
115
+ if (target === ".." || target === "") {
116
+ if (scopeStack.length > 0) {
117
+ scopeStack.pop();
118
+ updateCompleter();
119
+ }
120
+ } else {
121
+ const found = findCommandByName(target, getScopeCommand().commands);
122
+ if (found) if (found.commands?.length) {
123
+ scopeStack.push(found);
124
+ updateCompleter();
125
+ } else runtime.error(`"${target}" has no subcommands to scope into.`);
126
+ else runtime.error(`Unknown command: ${target}`);
127
+ }
128
+ continue;
129
+ }
130
+ if (trimmed === "..") {
131
+ if (scopeStack.length > 0) {
132
+ scopeStack.pop();
133
+ updateCompleter();
134
+ }
135
+ continue;
136
+ }
137
+ let evalInput = trimmed;
138
+ if (trimmed === ".") evalInput = "";
139
+ const prefix = options?.outputPrefix;
140
+ const prefixLines = prefix ? (text) => text.split("\n").map((l) => prefix + l).join("\n") : void 0;
141
+ const savedRuntimes = [];
142
+ if (prefixLines) {
143
+ const prefixedRuntime = {
144
+ ...existingCommand.runtime,
145
+ output: (...args) => {
146
+ const first = args[0];
147
+ runtime.output(typeof first === "string" ? prefixLines(first) : first, ...args.slice(1));
148
+ },
149
+ error: (text) => runtime.error(prefixLines(text))
150
+ };
151
+ const patchAll = (cmd) => {
152
+ savedRuntimes.push({
153
+ cmd,
154
+ runtime: cmd.runtime
155
+ });
156
+ cmd.runtime = prefixedRuntime;
157
+ cmd.commands?.forEach(patchAll);
158
+ };
159
+ patchAll(existingCommand);
160
+ }
161
+ const sp = options?.spacing;
162
+ const isSpacingObject = typeof sp === "object" && sp !== null && !Array.isArray(sp);
163
+ const spacingBefore = isSpacingObject ? sp.before : sp;
164
+ const spacingAfter = isSpacingObject ? sp.after : sp;
165
+ const emitSpacingLine = (value) => {
166
+ if (typeof value === "string") {
167
+ const sep = value.length === 1 ? value.repeat(runtime.terminal?.columns ?? 80) : value;
168
+ runtime.output(sep);
169
+ } else if (value) runtime.output("");
170
+ };
171
+ const emitSpacing = (value) => {
172
+ if (!value) return;
173
+ if (Array.isArray(value)) for (const line of value) emitSpacingLine(line);
174
+ else emitSpacingLine(value);
175
+ };
176
+ emitSpacing(spacingBefore);
177
+ const scopePath = getScopePath();
178
+ const scopedInput = scopePath ? evalInput ? `${scopePath} ${evalInput}` : scopePath : evalInput;
179
+ try {
180
+ const result = await evalCommand(scopedInput, { caller: "repl" });
181
+ if (result.error) {
182
+ const msg = result.error instanceof Error ? result.error.message : String(result.error);
183
+ runtime.error(prefixLines ? prefixLines(msg) : msg);
184
+ } else if (result.argsResult?.issues) {
185
+ const msg = `Validation error:\n${result.argsResult.issues.map((i) => ` - ${i.path?.join(".") || "root"}: ${i.message}`).join("\n")}`;
186
+ runtime.error(prefixLines ? prefixLines(msg) : msg);
187
+ }
188
+ yield result;
189
+ } catch (err) {
190
+ const msg = err instanceof Error ? err.message : String(err);
191
+ runtime.error(prefixLines ? prefixLines(msg) : msg);
192
+ } finally {
193
+ for (const { cmd, runtime: saved } of savedRuntimes) cmd.runtime = saved;
194
+ emitSpacing(spacingAfter);
195
+ }
196
+ }
197
+ } finally {
198
+ replActiveRef.value = false;
199
+ session?.close();
200
+ }
201
+ }
202
+ const iterable = replIterator();
203
+ iterable.drain = async () => {
204
+ try {
205
+ const results = [];
206
+ for await (const result of iterable) results.push(result);
207
+ return { value: results };
208
+ } catch (err) {
209
+ return { error: err };
210
+ }
211
+ };
212
+ return iterable;
213
+ }
214
+ //#endregion
215
+ export { createReplIterator };
216
+
217
+ //# sourceMappingURL=repl-loop.mjs.map