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,19 @@
1
+ import { CommandMeta } from "../types.mjs";
2
+
3
+ //#region src/codegen/parsers/bash.d.ts
4
+ /**
5
+ * Parse bash completion scripts into CommandMeta.
6
+ *
7
+ * Bash completions typically use `complete -F <func> <command>` and define
8
+ * a function that sets COMPREPLY. Common patterns:
9
+ *
10
+ * local commands="init build deploy"
11
+ * local args="--verbose --output --format"
12
+ * case "$prev" in --format) COMPREPLY=($(compgen -W "json yaml toml" ...)) ;;
13
+ * COMPREPLY=($(compgen -W "$commands" ...))
14
+ * COMPREPLY=($(compgen -W "$args" ...))
15
+ */
16
+ declare function parseBashCompletions(text: string): CommandMeta;
17
+ //#endregion
18
+ export { parseBashCompletions };
19
+ //# sourceMappingURL=bash.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash.d.mts","names":[],"sources":["../../../src/codegen/parsers/bash.ts"],"mappings":";;;;;AAcA;;;;AAA+D;;;;;;iBAA/C,oBAAA,CAAqB,IAAA,WAAe,WAAW"}
@@ -0,0 +1,112 @@
1
+ //#region src/codegen/parsers/bash.ts
2
+ /**
3
+ * Parse bash completion scripts into CommandMeta.
4
+ *
5
+ * Bash completions typically use `complete -F <func> <command>` and define
6
+ * a function that sets COMPREPLY. Common patterns:
7
+ *
8
+ * local commands="init build deploy"
9
+ * local args="--verbose --output --format"
10
+ * case "$prev" in --format) COMPREPLY=($(compgen -W "json yaml toml" ...)) ;;
11
+ * COMPREPLY=($(compgen -W "$commands" ...))
12
+ * COMPREPLY=($(compgen -W "$args" ...))
13
+ */
14
+ function parseBashCompletions(text) {
15
+ const result = {
16
+ name: "",
17
+ arguments: [],
18
+ subcommands: []
19
+ };
20
+ const completeMatch = text.match(/complete\s+(?:[^-]|-[^F])*-F\s+\S+\s+(\S+)/);
21
+ if (completeMatch) result.name = completeMatch[1];
22
+ if (!result.name) {
23
+ const markerMatch = text.match(/###-begin-(\S+)-completion-###/);
24
+ if (markerMatch) result.name = markerMatch[1];
25
+ }
26
+ const joined = text.replace(/\\\n\s*/g, " ");
27
+ const variables = extractVariables(joined);
28
+ const commandWords = variables.get("commands") ?? variables.get("cmds") ?? variables.get("subcommands");
29
+ if (commandWords) for (const name of splitWords(commandWords)) result.subcommands.push({ name });
30
+ const argWords = variables.get("args") ?? variables.get("opts") ?? variables.get("options") ?? variables.get("flags");
31
+ const optionNames = /* @__PURE__ */ new Set();
32
+ if (argWords) {
33
+ for (const word of splitWords(argWords)) if (word.startsWith("-")) optionNames.add(word);
34
+ }
35
+ const compgenRegex = /compgen\s+-W\s+["']([^"']+)["']/g;
36
+ let compgenMatch;
37
+ while ((compgenMatch = compgenRegex.exec(joined)) !== null) {
38
+ if (compgenMatch[1].startsWith("$")) continue;
39
+ for (const word of splitWords(compgenMatch[1])) if (word.startsWith("-")) optionNames.add(word);
40
+ }
41
+ const enumMap = parseCaseStatement(joined);
42
+ const seenArgs = /* @__PURE__ */ new Set();
43
+ for (const rawName of optionNames) {
44
+ if (rawName === "--help" || rawName === "-h" || rawName === "--version" || rawName === "-V") continue;
45
+ const name = rawName.replace(/^-+/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
46
+ if (!name || seenArgs.has(name)) continue;
47
+ seenArgs.add(name);
48
+ const enumValues = enumMap.get(rawName);
49
+ const isShort = rawName.startsWith("-") && !rawName.startsWith("--") && rawName.length === 2;
50
+ const field = {
51
+ name,
52
+ type: enumValues ? "enum" : "string",
53
+ enumValues,
54
+ ambiguous: !enumValues
55
+ };
56
+ if (isShort) field.aliases = [rawName];
57
+ result.arguments.push(field);
58
+ }
59
+ if (result.arguments.length === 0) delete result.arguments;
60
+ if (result.subcommands.length === 0) delete result.subcommands;
61
+ return result;
62
+ }
63
+ /**
64
+ * Extract variable assignments: `local VAR="value"`, `VAR="value"`, `local VAR='value'`
65
+ */
66
+ function extractVariables(text) {
67
+ const vars = /* @__PURE__ */ new Map();
68
+ const regex = /(?:local\s+)?(\w+)=["']([^"']+)["']/g;
69
+ let match;
70
+ while ((match = regex.exec(text)) !== null) vars.set(match[1], match[2]);
71
+ return vars;
72
+ }
73
+ /**
74
+ * Parse case statements to find option → enum value mappings.
75
+ *
76
+ * Matches patterns like:
77
+ * case "$prev" in
78
+ * --format) COMPREPLY=($(compgen -W "json yaml toml" ...)) ;;
79
+ * --env|--environment) COMPREPLY=($(compgen -W "dev staging prod" ...)) ;;
80
+ */
81
+ function parseCaseStatement(text) {
82
+ const result = /* @__PURE__ */ new Map();
83
+ const caseRegex = /case\s+[^i]*\bin\b\s*([\s\S]*?)esac/g;
84
+ let caseMatch;
85
+ while ((caseMatch = caseRegex.exec(text)) !== null) {
86
+ const branches = caseMatch[1].split(";;");
87
+ for (const branch of branches) {
88
+ const patternMatch = branch.match(/^\s*([-\w|]+)\)/);
89
+ if (!patternMatch) continue;
90
+ const compgenMatch = branch.match(/compgen\s+-W\s+["']([^"']+)["']/);
91
+ if (!compgenMatch) continue;
92
+ const values = compgenMatch[1];
93
+ const words = splitWords(values).filter((w) => !w.startsWith("$"));
94
+ if (words.length === 0) continue;
95
+ for (const pattern of patternMatch[1].split("|")) {
96
+ const trimmed = pattern.trim();
97
+ if (trimmed.startsWith("-")) result.set(trimmed, words);
98
+ }
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+ /**
104
+ * Split a space-separated word list, filtering empty strings.
105
+ */
106
+ function splitWords(text) {
107
+ return text.split(/\s+/).filter(Boolean);
108
+ }
109
+ //#endregion
110
+ export { parseBashCompletions };
111
+
112
+ //# sourceMappingURL=bash.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash.mjs","names":[],"sources":["../../../src/codegen/parsers/bash.ts"],"sourcesContent":["import type { CommandMeta, FieldMeta } from '../types.ts';\n\n/**\n * Parse bash completion scripts into CommandMeta.\n *\n * Bash completions typically use `complete -F <func> <command>` and define\n * a function that sets COMPREPLY. Common patterns:\n *\n * local commands=\"init build deploy\"\n * local args=\"--verbose --output --format\"\n * case \"$prev\" in --format) COMPREPLY=($(compgen -W \"json yaml toml\" ...)) ;;\n * COMPREPLY=($(compgen -W \"$commands\" ...))\n * COMPREPLY=($(compgen -W \"$args\" ...))\n */\nexport function parseBashCompletions(text: string): CommandMeta {\n const result: CommandMeta = {\n name: '',\n arguments: [],\n subcommands: [],\n };\n\n // Detect command name from `complete -F _func <command>` or `complete -o ... -F _func <command>`\n const completeMatch = text.match(/complete\\s+(?:[^-]|-[^F])*-F\\s+\\S+\\s+(\\S+)/);\n if (completeMatch) {\n result.name = completeMatch[1]!;\n }\n\n // Fallback: detect from marker comments like ###-begin-<name>-completion-###\n if (!result.name) {\n const markerMatch = text.match(/###-begin-(\\S+)-completion-###/);\n if (markerMatch) {\n result.name = markerMatch[1]!;\n }\n }\n\n // Join continuation lines for easier parsing\n const joined = text.replace(/\\\\\\n\\s*/g, ' ');\n\n // Collect variable values: local commands=\"...\" or local args=\"...\"\n const variables = extractVariables(joined);\n\n // Extract subcommand names from commands variable or compgen -W \"cmd1 cmd2\"\n const commandWords = variables.get('commands') ?? variables.get('cmds') ?? variables.get('subcommands');\n if (commandWords) {\n for (const name of splitWords(commandWords)) {\n result.subcommands!.push({ name });\n }\n }\n\n // Extract option names from args/opts/options variable\n const argWords = variables.get('args') ?? variables.get('opts') ?? variables.get('options') ?? variables.get('flags');\n const optionNames = new Set<string>();\n\n if (argWords) {\n for (const word of splitWords(argWords)) {\n if (word.startsWith('-')) {\n optionNames.add(word);\n }\n }\n }\n\n // Also scan for compgen -W patterns not tied to a case statement\n const compgenRegex = /compgen\\s+-W\\s+[\"']([^\"']+)[\"']/g;\n let compgenMatch: RegExpExecArray | null;\n while ((compgenMatch = compgenRegex.exec(joined)) !== null) {\n // Skip variable references like $args, $commands\n if (compgenMatch[1]!.startsWith('$')) continue;\n for (const word of splitWords(compgenMatch[1]!)) {\n if (word.startsWith('-')) {\n optionNames.add(word);\n }\n }\n }\n\n // Parse case statement for option value completions (enum detection)\n const enumMap = parseCaseStatement(joined);\n\n // Build argument fields\n const seenArgs = new Set<string>();\n\n for (const rawName of optionNames) {\n // Skip builtins\n if (rawName === '--help' || rawName === '-h' || rawName === '--version' || rawName === '-V') continue;\n\n const name = rawName.replace(/^-+/, '').replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n if (!name || seenArgs.has(name)) continue;\n seenArgs.add(name);\n\n // Check if this option has enum values from case statement\n const enumValues = enumMap.get(rawName);\n\n const isShort = rawName.startsWith('-') && !rawName.startsWith('--') && rawName.length === 2;\n const field: FieldMeta = {\n name,\n type: enumValues ? 'enum' : 'string',\n enumValues,\n ambiguous: !enumValues,\n };\n\n // If there's a corresponding long-form, record alias\n if (isShort) {\n field.aliases = [rawName];\n }\n\n result.arguments!.push(field);\n }\n\n if (result.arguments!.length === 0) delete result.arguments;\n if (result.subcommands!.length === 0) delete result.subcommands;\n\n return result;\n}\n\n/**\n * Extract variable assignments: `local VAR=\"value\"`, `VAR=\"value\"`, `local VAR='value'`\n */\nfunction extractVariables(text: string): Map<string, string> {\n const vars = new Map<string, string>();\n const regex = /(?:local\\s+)?(\\w+)=[\"']([^\"']+)[\"']/g;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(text)) !== null) {\n vars.set(match[1]!, match[2]!);\n }\n\n return vars;\n}\n\n/**\n * Parse case statements to find option → enum value mappings.\n *\n * Matches patterns like:\n * case \"$prev\" in\n * --format) COMPREPLY=($(compgen -W \"json yaml toml\" ...)) ;;\n * --env|--environment) COMPREPLY=($(compgen -W \"dev staging prod\" ...)) ;;\n */\nfunction parseCaseStatement(text: string): Map<string, string[]> {\n const result = new Map<string, string[]>();\n\n // Find case blocks on $prev or similar variables\n const caseRegex = /case\\s+[^i]*\\bin\\b\\s*([\\s\\S]*?)esac/g;\n let caseMatch: RegExpExecArray | null;\n\n while ((caseMatch = caseRegex.exec(text)) !== null) {\n const body = caseMatch[1]!;\n\n // Split into branches by ;; delimiter\n const branches = body.split(';;');\n for (const branch of branches) {\n // Match pattern: --opt1|--opt2) ... compgen -W \"values\" ...\n const patternMatch = branch.match(/^\\s*([-\\w|]+)\\)/);\n if (!patternMatch) continue;\n\n // Find compgen -W \"values\" within this branch\n const compgenMatch = branch.match(/compgen\\s+-W\\s+[\"']([^\"']+)[\"']/);\n if (!compgenMatch) continue;\n\n const values = compgenMatch[1]!;\n const words = splitWords(values).filter((w) => !w.startsWith('$'));\n if (words.length === 0) continue;\n\n for (const pattern of patternMatch[1]!.split('|')) {\n const trimmed = pattern.trim();\n if (trimmed.startsWith('-')) {\n result.set(trimmed, words);\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Split a space-separated word list, filtering empty strings.\n */\nfunction splitWords(text: string): string[] {\n return text.split(/\\s+/).filter(Boolean);\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { CommandMeta } from "../types.mjs";
2
+
3
+ //#region src/codegen/parsers/fish.d.ts
4
+ /**
5
+ * Parse fish shell completion scripts into CommandMeta.
6
+ *
7
+ * Fish completions use the `complete` builtin:
8
+ * complete -c <command> -s <short> -l <long> -d <description> -a <arguments> -r -f
9
+ */
10
+ declare function parseFishCompletions(text: string): CommandMeta;
11
+ //#endregion
12
+ export { parseFishCompletions };
13
+ //# sourceMappingURL=fish.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fish.d.mts","names":[],"sources":["../../../src/codegen/parsers/fish.ts"],"mappings":";;;;;AAQA;;;;iBAAgB,oBAAA,CAAqB,IAAA,WAAe,WAAW"}
@@ -0,0 +1,108 @@
1
+ //#region src/codegen/parsers/fish.ts
2
+ /**
3
+ * Parse fish shell completion scripts into CommandMeta.
4
+ *
5
+ * Fish completions use the `complete` builtin:
6
+ * complete -c <command> -s <short> -l <long> -d <description> -a <arguments> -r -f
7
+ */
8
+ function parseFishCompletions(text) {
9
+ const lines = text.split("\n");
10
+ const result = {
11
+ name: "",
12
+ arguments: [],
13
+ subcommands: []
14
+ };
15
+ const subcommandMap = /* @__PURE__ */ new Map();
16
+ for (const line of lines) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed || trimmed.startsWith("#")) continue;
19
+ if (!trimmed.match(/^complete\s+/)) continue;
20
+ const parts = parseCompleteLine(trimmed);
21
+ if (!parts) continue;
22
+ if (!result.name && parts.command) result.name = parts.command;
23
+ const subcommandCondition = parts.condition?.match(/__fish_seen_subcommand_from\s+(\S+)/);
24
+ if (subcommandCondition) {
25
+ const subName = subcommandCondition[1];
26
+ let sub = subcommandMap.get(subName);
27
+ if (!sub) {
28
+ sub = {
29
+ name: subName,
30
+ arguments: []
31
+ };
32
+ subcommandMap.set(subName, sub);
33
+ }
34
+ if (parts.longFlag || parts.shortFlag) {
35
+ const field = completionToField(parts);
36
+ if (field) sub.arguments.push(field);
37
+ }
38
+ continue;
39
+ }
40
+ if (parts.arguments && !parts.longFlag && !parts.shortFlag) {
41
+ const names = parts.arguments.split(/\s+/);
42
+ for (const name of names) {
43
+ if (!name || name.startsWith("(")) continue;
44
+ if (!subcommandMap.has(name)) subcommandMap.set(name, {
45
+ name,
46
+ description: parts.description,
47
+ arguments: []
48
+ });
49
+ else if (parts.description) subcommandMap.get(name).description = parts.description;
50
+ }
51
+ continue;
52
+ }
53
+ if (parts.longFlag || parts.shortFlag) {
54
+ const field = completionToField(parts);
55
+ if (field) result.arguments.push(field);
56
+ }
57
+ }
58
+ for (const sub of subcommandMap.values()) {
59
+ if (sub.arguments.length === 0) delete sub.arguments;
60
+ result.subcommands.push(sub);
61
+ }
62
+ if (result.arguments.length === 0) delete result.arguments;
63
+ if (result.subcommands.length === 0) delete result.subcommands;
64
+ return result;
65
+ }
66
+ function parseCompleteLine(line) {
67
+ const parts = {};
68
+ const cmdMatch = line.match(/-c\s+(\S+)/);
69
+ if (cmdMatch) parts.command = cmdMatch[1];
70
+ const shortMatch = line.match(/-s\s+(\S+)/);
71
+ if (shortMatch) parts.shortFlag = shortMatch[1];
72
+ const longMatch = line.match(/-l\s+(\S+)/);
73
+ if (longMatch) parts.longFlag = longMatch[1];
74
+ const descMatch = line.match(/-d\s+['"]([^'"]+)['"]/) || line.match(/-d\s+(\S+)/);
75
+ if (descMatch) parts.description = descMatch[1];
76
+ const argsMatch = line.match(/-a\s+['"]([^'"]+)['"]/) || line.match(/-a\s+(\S+)/);
77
+ if (argsMatch) parts.arguments = argsMatch[1];
78
+ const condMatch = line.match(/-n\s+['"]([^'"]+)['"]/) || line.match(/-n\s+(\S+)/);
79
+ if (condMatch) parts.condition = condMatch[1];
80
+ parts.requiresArg = /-r\b/.test(line);
81
+ parts.noFiles = /-f\b/.test(line);
82
+ return parts;
83
+ }
84
+ function completionToField(parts) {
85
+ const name = parts.longFlag ? parts.longFlag.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) : parts.shortFlag || "";
86
+ if (!name) return null;
87
+ let type = parts.requiresArg ? "string" : "boolean";
88
+ let enumValues;
89
+ if (parts.arguments) {
90
+ const values = parts.arguments.split(/\s+/).filter((v) => !v.startsWith("("));
91
+ if (values.length > 0 && values.length <= 20) {
92
+ enumValues = values;
93
+ type = "enum";
94
+ }
95
+ }
96
+ const aliases = parts.shortFlag && parts.longFlag ? [`-${parts.shortFlag}`] : void 0;
97
+ return {
98
+ name,
99
+ type,
100
+ description: parts.description,
101
+ aliases,
102
+ enumValues
103
+ };
104
+ }
105
+ //#endregion
106
+ export { parseFishCompletions };
107
+
108
+ //# sourceMappingURL=fish.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fish.mjs","names":[],"sources":["../../../src/codegen/parsers/fish.ts"],"sourcesContent":["import type { CommandMeta, FieldMeta } from '../types.ts';\n\n/**\n * Parse fish shell completion scripts into CommandMeta.\n *\n * Fish completions use the `complete` builtin:\n * complete -c <command> -s <short> -l <long> -d <description> -a <arguments> -r -f\n */\nexport function parseFishCompletions(text: string): CommandMeta {\n const lines = text.split('\\n');\n const result: CommandMeta = {\n name: '',\n arguments: [],\n subcommands: [],\n };\n\n const subcommandMap = new Map<string, CommandMeta>();\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n // Match: complete -c <command> [options]\n const completeMatch = trimmed.match(/^complete\\s+/);\n if (!completeMatch) continue;\n\n const parts = parseCompleteLine(trimmed);\n if (!parts) continue;\n\n // Set root command name\n if (!result.name && parts.command) {\n result.name = parts.command;\n }\n\n // If this completion has a condition like \"__fish_seen_subcommand_from <sub>\"\n // it belongs to a subcommand\n const subcommandCondition = parts.condition?.match(/__fish_seen_subcommand_from\\s+(\\S+)/);\n if (subcommandCondition) {\n const subName = subcommandCondition[1]!;\n let sub = subcommandMap.get(subName);\n if (!sub) {\n sub = { name: subName, arguments: [] };\n subcommandMap.set(subName, sub);\n }\n\n if (parts.longFlag || parts.shortFlag) {\n const field = completionToField(parts);\n if (field) sub.arguments!.push(field);\n }\n continue;\n }\n\n // If this defines a subcommand (has -a with no flags)\n if (parts.arguments && !parts.longFlag && !parts.shortFlag) {\n // Arguments list could be subcommand names\n const names = parts.arguments.split(/\\s+/);\n for (const name of names) {\n if (!name || name.startsWith('(')) continue;\n if (!subcommandMap.has(name)) {\n subcommandMap.set(name, {\n name,\n description: parts.description,\n arguments: [],\n });\n } else if (parts.description) {\n subcommandMap.get(name)!.description = parts.description;\n }\n }\n continue;\n }\n\n // Global option\n if (parts.longFlag || parts.shortFlag) {\n const field = completionToField(parts);\n if (field) result.arguments!.push(field);\n }\n }\n\n // Add subcommands\n for (const sub of subcommandMap.values()) {\n if (sub.arguments!.length === 0) delete sub.arguments;\n result.subcommands!.push(sub);\n }\n\n if (result.arguments!.length === 0) delete result.arguments;\n if (result.subcommands!.length === 0) delete result.subcommands;\n\n return result;\n}\n\ninterface CompleteParts {\n command?: string;\n shortFlag?: string;\n longFlag?: string;\n description?: string;\n arguments?: string;\n condition?: string;\n requiresArg?: boolean;\n noFiles?: boolean;\n}\n\nfunction parseCompleteLine(line: string): CompleteParts | null {\n const parts: CompleteParts = {};\n\n // Extract -c <command>\n const cmdMatch = line.match(/-c\\s+(\\S+)/);\n if (cmdMatch) parts.command = cmdMatch[1];\n\n // Extract -s <short>\n const shortMatch = line.match(/-s\\s+(\\S+)/);\n if (shortMatch) parts.shortFlag = shortMatch[1];\n\n // Extract -l <long>\n const longMatch = line.match(/-l\\s+(\\S+)/);\n if (longMatch) parts.longFlag = longMatch[1];\n\n // Extract -d '<description>' or -d \"<description>\"\n const descMatch = line.match(/-d\\s+['\"]([^'\"]+)['\"]/) || line.match(/-d\\s+(\\S+)/);\n if (descMatch) parts.description = descMatch[1];\n\n // Extract -a '<arguments>'\n const argsMatch = line.match(/-a\\s+['\"]([^'\"]+)['\"]/) || line.match(/-a\\s+(\\S+)/);\n if (argsMatch) parts.arguments = argsMatch[1];\n\n // Extract -n '<condition>'\n const condMatch = line.match(/-n\\s+['\"]([^'\"]+)['\"]/) || line.match(/-n\\s+(\\S+)/);\n if (condMatch) parts.condition = condMatch[1];\n\n // -r means requires argument\n parts.requiresArg = /-r\\b/.test(line);\n // -f means no file completion\n parts.noFiles = /-f\\b/.test(line);\n\n return parts;\n}\n\nfunction completionToField(parts: CompleteParts): FieldMeta | null {\n const name = parts.longFlag ? parts.longFlag.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()) : parts.shortFlag || '';\n\n if (!name) return null;\n\n let type: FieldMeta['type'] = parts.requiresArg ? 'string' : 'boolean';\n let enumValues: string[] | undefined;\n\n // If -a provides specific values, treat as enum\n if (parts.arguments) {\n const values = parts.arguments.split(/\\s+/).filter((v) => !v.startsWith('('));\n if (values.length > 0 && values.length <= 20) {\n enumValues = values;\n type = 'enum';\n }\n }\n\n const aliases = parts.shortFlag && parts.longFlag ? [`-${parts.shortFlag}`] : undefined;\n\n return {\n name,\n type,\n description: parts.description,\n aliases,\n enumValues,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { CommandMeta } from "../types.mjs";
2
+
3
+ //#region src/codegen/parsers/help.d.ts
4
+ interface ParseHelpOptions {
5
+ /** Name to use for the root command if not detected from the help text */
6
+ name?: string;
7
+ }
8
+ /**
9
+ * Parse --help text output into CommandMeta.
10
+ * Handles common styles: GNU coreutils, Go cobra, Python argparse, Node commander/yargs, gh CLI.
11
+ */
12
+ declare function parseHelpOutput(text: string, options?: ParseHelpOptions): CommandMeta;
13
+ //#endregion
14
+ export { parseHelpOutput };
15
+ //# sourceMappingURL=help.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.mts","names":[],"sources":["../../../src/codegen/parsers/help.ts"],"mappings":";;;UAEU,gBAAA;;EAER,IAAI;AAAA;;;AAAA;AASN;iBAAgB,eAAA,CAAgB,IAAA,UAAc,OAAA,GAAU,gBAAA,GAAmB,WAAW"}
@@ -0,0 +1,271 @@
1
+ //#region src/codegen/parsers/help.ts
2
+ /**
3
+ * Parse --help text output into CommandMeta.
4
+ * Handles common styles: GNU coreutils, Go cobra, Python argparse, Node commander/yargs, gh CLI.
5
+ */
6
+ function parseHelpOutput(text, options) {
7
+ const lines = text.split("\n");
8
+ const result = {
9
+ name: options?.name || "",
10
+ arguments: [],
11
+ positionals: [],
12
+ subcommands: []
13
+ };
14
+ let section = "none";
15
+ const usageMatch = text.match(/^[Uu](?:SAGE|sage):?\s*(\S+)/m) || text.match(/^USAGE\n\s+(\S+)/m);
16
+ if (usageMatch && !result.name) result.name = usageMatch[1];
17
+ const descriptionLines = [];
18
+ for (const line of lines) {
19
+ const trimmed = line.trim();
20
+ if (!trimmed) {
21
+ if (descriptionLines.length > 0) break;
22
+ continue;
23
+ }
24
+ if (isSectionHeader(trimmed)) break;
25
+ if (descriptionLines.length === 0 || descriptionLines.length > 0) descriptionLines.push(trimmed);
26
+ }
27
+ if (descriptionLines.length > 0) result.description = descriptionLines.join(" ");
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ const trimmed = line.trim();
31
+ if (!trimmed) continue;
32
+ const sectionType = detectSection(trimmed);
33
+ if (sectionType) {
34
+ section = sectionType;
35
+ continue;
36
+ }
37
+ switch (section) {
38
+ case "commands": {
39
+ const cmd = parseCommandLine(line);
40
+ if (cmd) result.subcommands.push(cmd);
41
+ break;
42
+ }
43
+ case "options": {
44
+ const field = parseOptionLine(line);
45
+ if (field) result.arguments.push(field);
46
+ break;
47
+ }
48
+ case "arguments":
49
+ case "positional": {
50
+ const field = parsePositionalLine(line);
51
+ if (field) result.positionals.push(field);
52
+ break;
53
+ }
54
+ case "aliases":
55
+ if (trimmed.match(/^\S+(?:\s+\S+)*$/)) {
56
+ const parts = trimmed.split(/\s+/);
57
+ if (parts.length >= 2) {
58
+ const alias = parts[parts.length - 1];
59
+ if (!result.aliases) result.aliases = [];
60
+ result.aliases.push(alias);
61
+ }
62
+ }
63
+ break;
64
+ }
65
+ }
66
+ if (result.arguments.length === 0) delete result.arguments;
67
+ if (result.positionals.length === 0) delete result.positionals;
68
+ if (result.subcommands.length === 0) delete result.subcommands;
69
+ if (result.aliases?.length === 0) delete result.aliases;
70
+ return result;
71
+ }
72
+ function isSectionHeader(line) {
73
+ return /^[A-Z][A-Za-z\s]*:?\s*$/i.test(line) || /^[A-Z][A-Z\s]+$/i.test(line);
74
+ }
75
+ function detectSection(line) {
76
+ const lower = line.toLowerCase().replace(/:$/, "").trim();
77
+ if (/^(?:available\s+)?(?:commands|subcommands)$/.test(lower)) return "commands";
78
+ if (/^(?:\w+\s+)*commands$/.test(lower) && !/alias/.test(lower)) return "commands";
79
+ if (/^(?:global\s+|inherited\s+)?(?:options|flags)$/.test(lower)) return "options";
80
+ if (/^(?:positional\s+)?(?:arguments|args|positionals)$/.test(lower)) return "positional";
81
+ if (/^alias(?:es)?(?:\s+commands)?$/.test(lower)) return "aliases";
82
+ if (lower === "usage") return "usage";
83
+ if (/^(?:help\s+topics|examples?|learn\s+more|json\s+fields|see\s+also|notes?)$/.test(lower)) return "skip";
84
+ return null;
85
+ }
86
+ function parseCommandLine(line) {
87
+ const colonMatch = line.match(/^\s{2,}([\w][\w-]*):?\s{2,}(.+)$/);
88
+ if (colonMatch) return {
89
+ name: colonMatch[1].replace(/:$/, ""),
90
+ description: colonMatch[2].trim()
91
+ };
92
+ const nameOnly = line.match(/^\s{2,}([\w][\w-]*):?\s*$/);
93
+ if (nameOnly) return { name: nameOnly[1].replace(/:$/, "") };
94
+ return null;
95
+ }
96
+ function parseOptionLine(line) {
97
+ const cobraMatch = line.match(/^\s{2,}(?:(-\w),\s+)?(-{1,2}[\w-]+)(?:\s+([^\s<>]+))?\s{2,}(.+)$/);
98
+ if (cobraMatch) {
99
+ const shortFlag = cobraMatch[1];
100
+ const longFlag = cobraMatch[2];
101
+ const typeHint = cobraMatch[3];
102
+ const description = cobraMatch[4]?.trim();
103
+ const name = normalizeOptionName(longFlag);
104
+ const aliases = shortFlag ? [normalizeAlias(shortFlag)] : void 0;
105
+ const { type, ambiguous } = resolveType(typeHint);
106
+ const { defaultValue, resolvedType } = extractDefault(description, type, ambiguous);
107
+ const { enumValues, resolvedType: finalType } = extractEnum(description, resolvedType);
108
+ return reconcileField({
109
+ name,
110
+ type: finalType,
111
+ description,
112
+ required: type === "boolean" ? void 0 : true,
113
+ aliases,
114
+ default: defaultValue,
115
+ enumValues,
116
+ ambiguous: ambiguous || void 0
117
+ });
118
+ }
119
+ const gnuMatch = line.match(/^\s{2,}(?:(-\w),?\s+)?(-{1,2}[\w-]+)(?:\s*[=\s]\s*(?:<([^>]+)>|\[([^\]]+)\]|(\w+)))?\s{2,}(.+)$/);
120
+ if (gnuMatch) {
121
+ const shortFlag = gnuMatch[1];
122
+ const longFlag = gnuMatch[2];
123
+ const valueName = gnuMatch[3] || gnuMatch[4] || gnuMatch[5];
124
+ const description = gnuMatch[6]?.trim();
125
+ const name = normalizeOptionName(longFlag);
126
+ const aliases = shortFlag ? [normalizeAlias(shortFlag)] : void 0;
127
+ const { type, ambiguous } = resolveType(valueName);
128
+ const { defaultValue, resolvedType } = extractDefault(description, type, ambiguous);
129
+ const { enumValues, resolvedType: finalType } = extractEnum(description, resolvedType);
130
+ const required = !gnuMatch[4];
131
+ return reconcileField({
132
+ name,
133
+ type: finalType,
134
+ description,
135
+ required: finalType === "boolean" ? void 0 : required,
136
+ aliases,
137
+ default: defaultValue,
138
+ enumValues,
139
+ ambiguous: ambiguous || void 0
140
+ });
141
+ }
142
+ const simple = line.match(/^\s{2,}(-{1,2}[\w-]+)\s{2,}(.+)$/);
143
+ if (simple) return {
144
+ name: normalizeOptionName(simple[1]),
145
+ type: "boolean",
146
+ description: simple[2].trim()
147
+ };
148
+ return null;
149
+ }
150
+ /**
151
+ * Resolve a type hint string to a FieldMeta type.
152
+ */
153
+ function resolveType(hint) {
154
+ if (!hint) return {
155
+ type: "boolean",
156
+ ambiguous: false
157
+ };
158
+ const lower = hint.toLowerCase();
159
+ if (/^(num|number|int|integer|port|count|float|duration)$/.test(lower)) return {
160
+ type: "number",
161
+ ambiguous: false
162
+ };
163
+ if (/^(str|string|text|name|path|file|dir|url|host|query|expression|template)$/.test(lower)) return {
164
+ type: "string",
165
+ ambiguous: false
166
+ };
167
+ if (/^(bool|boolean)$/.test(lower)) return {
168
+ type: "boolean",
169
+ ambiguous: false
170
+ };
171
+ if (/^(strings|fields)$/.test(lower)) return {
172
+ type: "array",
173
+ ambiguous: false
174
+ };
175
+ return {
176
+ type: "string",
177
+ ambiguous: true
178
+ };
179
+ }
180
+ /**
181
+ * Extract default value from description text.
182
+ */
183
+ function extractDefault(description, type, ambiguous) {
184
+ let resolvedType = type;
185
+ let defaultValue;
186
+ const defaultMatch = description?.match(/\(default[:\s]+([^)]+)\)/i) || description?.match(/\[default[:\s]+([^\]]+)\]/i);
187
+ if (defaultMatch) {
188
+ const raw = defaultMatch[1].trim();
189
+ if (raw === "true" || raw === "false") {
190
+ defaultValue = raw === "true";
191
+ resolvedType = "boolean";
192
+ } else if (/^\d+$/.test(raw)) {
193
+ defaultValue = parseInt(raw, 10);
194
+ if (resolvedType === "string" && !ambiguous) resolvedType = "number";
195
+ } else if (/^\d+\.\d+$/.test(raw)) {
196
+ defaultValue = parseFloat(raw);
197
+ if (resolvedType === "string" && !ambiguous) resolvedType = "number";
198
+ } else defaultValue = raw.replace(/^["']|["']$/g, "");
199
+ }
200
+ return {
201
+ defaultValue,
202
+ resolvedType
203
+ };
204
+ }
205
+ /**
206
+ * Extract enum values from description text.
207
+ */
208
+ function extractEnum(description, type) {
209
+ const choiceMatch = description?.match(/\((?:one of|choices?)[:\s]+([^)]+)\)/i);
210
+ if (choiceMatch) return {
211
+ enumValues: choiceMatch[1].split(/[,|]/).map((v) => v.trim().replace(/^["']|["']$/g, "")),
212
+ resolvedType: "enum"
213
+ };
214
+ const inlineMatch = description?.match(/\{(\w+(?:\|[\w-]+)+)\}/);
215
+ if (inlineMatch) return {
216
+ enumValues: inlineMatch[1].split("|"),
217
+ resolvedType: "enum"
218
+ };
219
+ return {
220
+ enumValues: void 0,
221
+ resolvedType: type
222
+ };
223
+ }
224
+ /**
225
+ * Fix up a parsed field for edge cases:
226
+ * - Enum default not in the listed values → add it
227
+ * - Default value type doesn't match declared type → reset to type default + mark ambiguous
228
+ */
229
+ function reconcileField(field) {
230
+ if (field.type === "enum" && field.enumValues && field.default !== void 0) {
231
+ const defStr = String(field.default);
232
+ if (!field.enumValues.includes(defStr)) field.enumValues = [...field.enumValues, defStr];
233
+ }
234
+ if (field.default !== void 0) {
235
+ if (field.type === "boolean" && typeof field.default !== "boolean") {
236
+ field.default = false;
237
+ field.ambiguous = true;
238
+ } else if (field.type === "number" && typeof field.default !== "number") {
239
+ field.default = 0;
240
+ field.ambiguous = true;
241
+ }
242
+ }
243
+ return field;
244
+ }
245
+ function parsePositionalLine(line) {
246
+ const match = line.match(/^\s{2,}<?(\w[\w-]*)>?\s{2,}(.+)$/);
247
+ if (!match) return null;
248
+ return {
249
+ name: match[1],
250
+ type: "string",
251
+ description: match[2].trim(),
252
+ positional: true,
253
+ ambiguous: true
254
+ };
255
+ }
256
+ /**
257
+ * Strip leading dashes from a flag name, preserving kebab-case.
258
+ */
259
+ function normalizeOptionName(flag) {
260
+ return flag.replace(/^-+/, "");
261
+ }
262
+ /**
263
+ * Strip leading dash from a short alias flag (e.g. '-v' → 'v').
264
+ */
265
+ function normalizeAlias(alias) {
266
+ return alias.replace(/^-/, "");
267
+ }
268
+ //#endregion
269
+ export { parseHelpOutput };
270
+
271
+ //# sourceMappingURL=help.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.mjs","names":[],"sources":["../../../src/codegen/parsers/help.ts"],"sourcesContent":["import type { CommandMeta, FieldMeta } from '../types.ts';\n\ninterface ParseHelpOptions {\n /** Name to use for the root command if not detected from the help text */\n name?: string;\n}\n\ntype Section = 'none' | 'usage' | 'commands' | 'options' | 'arguments' | 'positional' | 'aliases' | 'skip';\n\n/**\n * Parse --help text output into CommandMeta.\n * Handles common styles: GNU coreutils, Go cobra, Python argparse, Node commander/yargs, gh CLI.\n */\nexport function parseHelpOutput(text: string, options?: ParseHelpOptions): CommandMeta {\n const lines = text.split('\\n');\n const result: CommandMeta = {\n name: options?.name || '',\n arguments: [],\n positionals: [],\n subcommands: [],\n };\n\n let section: Section = 'none';\n\n // Try to extract name from USAGE line\n // Matches: \"Usage: mycli\", \"USAGE\\n gh <command>\", \"Usage:\\n myapp deploy [flags]\"\n const usageMatch = text.match(/^[Uu](?:SAGE|sage):?\\s*(\\S+)/m) || text.match(/^USAGE\\n\\s+(\\S+)/m);\n if (usageMatch && !result.name) {\n result.name = usageMatch[1]!;\n }\n\n // Extract description from lines before first section header\n const descriptionLines: string[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) {\n if (descriptionLines.length > 0) break;\n continue;\n }\n if (isSectionHeader(trimmed)) break;\n if (descriptionLines.length === 0 || descriptionLines.length > 0) {\n descriptionLines.push(trimmed);\n }\n }\n if (descriptionLines.length > 0) {\n result.description = descriptionLines.join(' ');\n }\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const trimmed = line.trim();\n\n if (!trimmed) continue;\n\n // Detect section headers\n const sectionType = detectSection(trimmed);\n if (sectionType) {\n section = sectionType;\n continue;\n }\n\n // Parse content based on current section\n switch (section) {\n case 'commands': {\n const cmd = parseCommandLine(line);\n if (cmd) {\n result.subcommands!.push(cmd);\n }\n break;\n }\n case 'options': {\n const field = parseOptionLine(line);\n if (field) {\n result.arguments!.push(field);\n }\n break;\n }\n case 'arguments':\n case 'positional': {\n const field = parsePositionalLine(line);\n if (field) {\n result.positionals!.push(field);\n }\n break;\n }\n case 'aliases': {\n const aliasMatch = trimmed.match(/^\\S+(?:\\s+\\S+)*$/);\n if (aliasMatch) {\n // Extract alias from lines like \"gh pr ls\" — the last word is the alias name\n const parts = trimmed.split(/\\s+/);\n if (parts.length >= 2) {\n const alias = parts[parts.length - 1]!;\n if (!result.aliases) result.aliases = [];\n result.aliases.push(alias);\n }\n }\n break;\n }\n // 'skip', 'usage', 'none' — do nothing\n }\n }\n\n // Clean up empty arrays\n if (result.arguments!.length === 0) delete result.arguments;\n if (result.positionals!.length === 0) delete result.positionals;\n if (result.subcommands!.length === 0) delete result.subcommands;\n if (result.aliases?.length === 0) delete result.aliases;\n\n return result;\n}\n\nfunction isSectionHeader(line: string): boolean {\n // \"USAGE\", \"FLAGS\", \"CORE COMMANDS\", \"Options:\", \"Available Commands:\"\n return /^[A-Z][A-Za-z\\s]*:?\\s*$/i.test(line) || /^[A-Z][A-Z\\s]+$/i.test(line);\n}\n\nfunction detectSection(line: string): Section | null {\n const lower = line.toLowerCase().replace(/:$/, '').trim();\n\n // Commands: \"commands\", \"available commands\", \"subcommands\",\n // and gh-style: \"core commands\", \"general commands\", \"additional commands\", etc.\n // Match anything ending in \"commands\" or \"subcommands\", but NOT \"alias commands\"\n if (/^(?:available\\s+)?(?:commands|subcommands)$/.test(lower)) return 'commands';\n if (/^(?:\\w+\\s+)*commands$/.test(lower) && !/alias/.test(lower)) return 'commands';\n\n // Options/Flags: \"options\", \"flags\", \"global options\", \"inherited flags\"\n if (/^(?:global\\s+|inherited\\s+)?(?:options|flags)$/.test(lower)) return 'options';\n\n // Positional arguments\n if (/^(?:positional\\s+)?(?:arguments|args|positionals)$/.test(lower)) return 'positional';\n\n // Aliases section\n if (/^alias(?:es)?(?:\\s+commands)?$/.test(lower)) return 'aliases';\n\n // Usage section\n if (lower === 'usage') return 'usage';\n\n // Sections to skip entirely\n if (/^(?:help\\s+topics|examples?|learn\\s+more|json\\s+fields|see\\s+also|notes?)$/.test(lower)) return 'skip';\n\n return null;\n}\n\nfunction parseCommandLine(line: string): CommandMeta | null {\n // Pattern: \" command-name: Description text\" (gh-style with colon)\n const colonMatch = line.match(/^\\s{2,}([\\w][\\w-]*):?\\s{2,}(.+)$/);\n if (colonMatch) {\n const name = colonMatch[1]!.replace(/:$/, '');\n return {\n name,\n description: colonMatch[2]!.trim(),\n };\n }\n\n // Pattern: \" command-name\" (no description)\n const nameOnly = line.match(/^\\s{2,}([\\w][\\w-]*):?\\s*$/);\n if (nameOnly) {\n return { name: nameOnly[1]!.replace(/:$/, '') };\n }\n\n return null;\n}\n\nfunction parseOptionLine(line: string): FieldMeta | null {\n // Try cobra-style first: \" -s, --flag type Description\" or \" --flag type Description\"\n // The type hint can be a bare word (string, int) or a complex placeholder ([HOST/]OWNER/REPO)\n // but NOT angle-bracket values like <string> (those fall through to GNU pattern)\n const cobraMatch = line.match(/^\\s{2,}(?:(-\\w),\\s+)?(-{1,2}[\\w-]+)(?:\\s+([^\\s<>]+))?\\s{2,}(.+)$/);\n\n if (cobraMatch) {\n const shortFlag = cobraMatch[1];\n const longFlag = cobraMatch[2]!;\n const typeHint = cobraMatch[3];\n const description = cobraMatch[4]?.trim();\n\n const name = normalizeOptionName(longFlag);\n const aliases = shortFlag ? [normalizeAlias(shortFlag)] : undefined;\n\n const { type, ambiguous } = resolveType(typeHint);\n\n const { defaultValue, resolvedType } = extractDefault(description, type, ambiguous);\n const { enumValues, resolvedType: finalType } = extractEnum(description, resolvedType);\n\n return reconcileField({\n name,\n type: finalType,\n description,\n required: type === 'boolean' ? undefined : true,\n aliases,\n default: defaultValue,\n enumValues,\n ambiguous: ambiguous || undefined,\n });\n }\n\n // GNU/argparse-style: \" -s, --long-name <value> Description\"\n // \" --long-name=<value> Description\"\n const gnuMatch = line.match(/^\\s{2,}(?:(-\\w),?\\s+)?(-{1,2}[\\w-]+)(?:\\s*[=\\s]\\s*(?:<([^>]+)>|\\[([^\\]]+)\\]|(\\w+)))?\\s{2,}(.+)$/);\n\n if (gnuMatch) {\n const shortFlag = gnuMatch[1];\n const longFlag = gnuMatch[2]!;\n const valueName = gnuMatch[3] || gnuMatch[4] || gnuMatch[5];\n const description = gnuMatch[6]?.trim();\n\n const name = normalizeOptionName(longFlag);\n const aliases = shortFlag ? [normalizeAlias(shortFlag)] : undefined;\n\n const { type, ambiguous } = resolveType(valueName);\n\n const { defaultValue, resolvedType } = extractDefault(description, type, ambiguous);\n const { enumValues, resolvedType: finalType } = extractEnum(description, resolvedType);\n\n // [value] means optional, <value> means required\n const required = !gnuMatch[4];\n\n return reconcileField({\n name,\n type: finalType,\n description,\n required: finalType === 'boolean' ? undefined : required,\n aliases,\n default: defaultValue,\n enumValues,\n ambiguous: ambiguous || undefined,\n });\n }\n\n // Simplest pattern: \" --name Description\"\n const simple = line.match(/^\\s{2,}(-{1,2}[\\w-]+)\\s{2,}(.+)$/);\n if (simple) {\n const name = normalizeOptionName(simple[1]!);\n return {\n name,\n type: 'boolean',\n description: simple[2]!.trim(),\n };\n }\n\n return null;\n}\n\n/**\n * Resolve a type hint string to a FieldMeta type.\n */\nfunction resolveType(hint: string | undefined): { type: FieldMeta['type']; ambiguous: boolean } {\n if (!hint) return { type: 'boolean', ambiguous: false };\n\n const lower = hint.toLowerCase();\n\n if (/^(num|number|int|integer|port|count|float|duration)$/.test(lower)) {\n return { type: 'number', ambiguous: false };\n }\n if (/^(str|string|text|name|path|file|dir|url|host|query|expression|template)$/.test(lower)) {\n return { type: 'string', ambiguous: false };\n }\n if (/^(bool|boolean)$/.test(lower)) {\n return { type: 'boolean', ambiguous: false };\n }\n if (/^(strings|fields)$/.test(lower)) {\n return { type: 'array', ambiguous: false };\n }\n\n // Unknown type hint — default to string but mark ambiguous\n return { type: 'string', ambiguous: true };\n}\n\n/**\n * Extract default value from description text.\n */\nfunction extractDefault(\n description: string | undefined,\n type: FieldMeta['type'],\n ambiguous: boolean,\n): { defaultValue: unknown; resolvedType: FieldMeta['type'] } {\n let resolvedType = type;\n let defaultValue: unknown;\n\n const defaultMatch = description?.match(/\\(default[:\\s]+([^)]+)\\)/i) || description?.match(/\\[default[:\\s]+([^\\]]+)\\]/i);\n if (defaultMatch) {\n const raw = defaultMatch[1]!.trim();\n if (raw === 'true' || raw === 'false') {\n defaultValue = raw === 'true';\n resolvedType = 'boolean';\n } else if (/^\\d+$/.test(raw)) {\n defaultValue = parseInt(raw, 10);\n if (resolvedType === 'string' && !ambiguous) resolvedType = 'number';\n } else if (/^\\d+\\.\\d+$/.test(raw)) {\n defaultValue = parseFloat(raw);\n if (resolvedType === 'string' && !ambiguous) resolvedType = 'number';\n } else {\n defaultValue = raw.replace(/^[\"']|[\"']$/g, '');\n }\n }\n\n return { defaultValue, resolvedType };\n}\n\n/**\n * Extract enum values from description text.\n */\nfunction extractEnum(\n description: string | undefined,\n type: FieldMeta['type'],\n): { enumValues: string[] | undefined; resolvedType: FieldMeta['type'] } {\n // Explicit choices: (choices: a, b, c) or (one of: a|b|c)\n const choiceMatch = description?.match(/\\((?:one of|choices?)[:\\s]+([^)]+)\\)/i);\n if (choiceMatch) {\n const values = choiceMatch[1]!.split(/[,|]/).map((v) => v.trim().replace(/^[\"']|[\"']$/g, ''));\n return { enumValues: values, resolvedType: 'enum' };\n }\n\n // Inline enum in description: {open|closed|merged|all} (gh-style)\n const inlineMatch = description?.match(/\\{(\\w+(?:\\|[\\w-]+)+)\\}/);\n if (inlineMatch) {\n const values = inlineMatch[1]!.split('|');\n return { enumValues: values, resolvedType: 'enum' };\n }\n\n return { enumValues: undefined, resolvedType: type };\n}\n\n/**\n * Fix up a parsed field for edge cases:\n * - Enum default not in the listed values → add it\n * - Default value type doesn't match declared type → reset to type default + mark ambiguous\n */\nfunction reconcileField(field: FieldMeta): FieldMeta {\n // If enum has a default that isn't in the value list, add it\n if (field.type === 'enum' && field.enumValues && field.default !== undefined) {\n const defStr = String(field.default);\n if (!field.enumValues.includes(defStr)) {\n field.enumValues = [...field.enumValues, defStr];\n }\n }\n\n // If the default value doesn't match the declared type, reset + mark ambiguous\n if (field.default !== undefined) {\n if (field.type === 'boolean' && typeof field.default !== 'boolean') {\n field.default = false;\n field.ambiguous = true;\n } else if (field.type === 'number' && typeof field.default !== 'number') {\n field.default = 0;\n field.ambiguous = true;\n }\n }\n\n return field;\n}\n\nfunction parsePositionalLine(line: string): FieldMeta | null {\n // Pattern: \" <name> Description\"\n // Pattern: \" name Description\"\n const match = line.match(/^\\s{2,}<?(\\w[\\w-]*)>?\\s{2,}(.+)$/);\n if (!match) return null;\n\n return {\n name: match[1]!,\n type: 'string',\n description: match[2]!.trim(),\n positional: true,\n ambiguous: true,\n };\n}\n\n/**\n * Strip leading dashes from a flag name, preserving kebab-case.\n */\nfunction normalizeOptionName(flag: string): string {\n return flag.replace(/^-+/, '');\n}\n\n/**\n * Strip leading dash from a short alias flag (e.g. '-v' → 'v').\n */\nfunction normalizeAlias(alias: string): string {\n return alias.replace(/^-/, '');\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { CommandMeta } from "../types.mjs";
2
+
3
+ //#region src/codegen/parsers/merge.d.ts
4
+ /**
5
+ * Deep-merge multiple CommandMeta from different sources.
6
+ * Deduplicates fields, resolves conflicts, and combines subcommands.
7
+ *
8
+ * Later sources take precedence for descriptions and types,
9
+ * unless the earlier source was more specific (non-ambiguous).
10
+ */
11
+ declare function mergeCommandMeta(...sources: CommandMeta[]): CommandMeta;
12
+ //#endregion
13
+ export { mergeCommandMeta };
14
+ //# sourceMappingURL=merge.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.mts","names":[],"sources":["../../../src/codegen/parsers/merge.ts"],"mappings":";;;;;AASA;;;;;iBAAgB,gBAAA,IAAoB,OAAA,EAAS,WAAA,KAAgB,WAAW"}