@outfitter/cli 0.1.0-rc.1 → 0.1.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/README.md +21 -32
  2. package/dist/actions.js +1 -1
  3. package/dist/borders/index.d.ts +3 -0
  4. package/dist/borders/index.js +13 -0
  5. package/dist/box/index.d.ts +4 -0
  6. package/dist/box/index.js +13 -0
  7. package/dist/cli.d.ts +2 -103
  8. package/dist/cli.js +4 -51
  9. package/dist/colors/index.d.ts +3 -0
  10. package/dist/colors/index.js +18 -0
  11. package/dist/command.d.ts +3 -37
  12. package/dist/command.js +5 -1
  13. package/dist/demo/index.d.ts +78 -0
  14. package/dist/demo/index.js +148 -0
  15. package/dist/demo/registry.d.ts +7 -0
  16. package/dist/demo/registry.js +28 -0
  17. package/dist/demo/renderers/borders.d.ts +7 -0
  18. package/dist/demo/renderers/borders.js +17 -0
  19. package/dist/demo/renderers/box.d.ts +7 -0
  20. package/dist/demo/renderers/box.js +18 -0
  21. package/dist/demo/renderers/colors.d.ts +7 -0
  22. package/dist/demo/renderers/colors.js +18 -0
  23. package/dist/demo/renderers/indicators.d.ts +7 -0
  24. package/dist/demo/renderers/indicators.js +17 -0
  25. package/dist/demo/renderers/list.d.ts +7 -0
  26. package/dist/demo/renderers/list.js +19 -0
  27. package/dist/demo/renderers/markdown.d.ts +7 -0
  28. package/dist/demo/renderers/markdown.js +18 -0
  29. package/dist/demo/renderers/progress.d.ts +7 -0
  30. package/dist/demo/renderers/progress.js +17 -0
  31. package/dist/demo/renderers/spinner.d.ts +7 -0
  32. package/dist/demo/renderers/spinner.js +19 -0
  33. package/dist/demo/renderers/table.d.ts +7 -0
  34. package/dist/demo/renderers/table.js +19 -0
  35. package/dist/demo/renderers/text.d.ts +7 -0
  36. package/dist/demo/renderers/text.js +16 -0
  37. package/dist/demo/renderers/tree.d.ts +7 -0
  38. package/dist/demo/renderers/tree.js +18 -0
  39. package/dist/demo/section.d.ts +5 -0
  40. package/dist/demo/section.js +23 -0
  41. package/dist/demo/templates.d.ts +4 -0
  42. package/dist/demo/templates.js +10 -0
  43. package/dist/demo/types.d.ts +3 -0
  44. package/dist/demo/types.js +8 -0
  45. package/dist/index.d.ts +5 -607
  46. package/dist/index.js +10 -44
  47. package/dist/input.d.ts +9 -123
  48. package/dist/input.js +2 -3
  49. package/dist/list/index.d.ts +3 -0
  50. package/dist/list/index.js +9 -0
  51. package/dist/output.d.ts +2 -68
  52. package/dist/output.js +4 -150
  53. package/dist/pagination.d.ts +1 -34
  54. package/dist/pagination.js +1 -1
  55. package/dist/preset/full.d.ts +14 -0
  56. package/dist/preset/full.js +41 -0
  57. package/dist/preset/standard.d.ts +11 -0
  58. package/dist/preset/standard.js +30 -0
  59. package/dist/prompt/confirm.d.ts +4 -0
  60. package/dist/prompt/confirm.js +9 -0
  61. package/dist/prompt/group.d.ts +4 -0
  62. package/dist/prompt/group.js +9 -0
  63. package/dist/prompt/index.d.ts +7 -0
  64. package/dist/prompt/index.js +32 -0
  65. package/dist/prompt/select.d.ts +4 -0
  66. package/dist/prompt/select.js +11 -0
  67. package/dist/prompt/text.d.ts +4 -0
  68. package/dist/prompt/text.js +11 -0
  69. package/dist/prompt/types.d.ts +3 -0
  70. package/dist/prompt/types.js +8 -0
  71. package/dist/prompt/validators.d.ts +2 -0
  72. package/dist/prompt/validators.js +8 -0
  73. package/dist/render/borders.d.ts +2 -0
  74. package/dist/render/borders.js +15 -0
  75. package/dist/render/box.d.ts +3 -0
  76. package/dist/render/box.js +23 -0
  77. package/dist/render/colors.d.ts +2 -0
  78. package/dist/render/colors.js +20 -0
  79. package/dist/render/date.d.ts +2 -0
  80. package/dist/render/date.js +12 -0
  81. package/dist/render/format-relative.d.ts +2 -0
  82. package/dist/render/format-relative.js +8 -0
  83. package/dist/render/format.d.ts +2 -0
  84. package/dist/render/format.js +10 -0
  85. package/dist/render/heading.d.ts +3 -0
  86. package/dist/render/heading.js +14 -0
  87. package/dist/render/index.d.ts +32 -0
  88. package/dist/render/index.js +235 -0
  89. package/dist/render/indicators.d.ts +2 -0
  90. package/dist/render/indicators.js +14 -0
  91. package/dist/render/json.d.ts +2 -0
  92. package/dist/render/json.js +10 -0
  93. package/dist/render/layout.d.ts +5 -0
  94. package/dist/render/layout.js +25 -0
  95. package/dist/render/list.d.ts +2 -0
  96. package/dist/render/list.js +8 -0
  97. package/dist/render/markdown.d.ts +2 -0
  98. package/dist/render/markdown.js +10 -0
  99. package/dist/render/progress.d.ts +2 -0
  100. package/dist/render/progress.js +8 -0
  101. package/dist/render/separator.d.ts +3 -0
  102. package/dist/render/separator.js +14 -0
  103. package/dist/render/shapes.d.ts +2 -0
  104. package/dist/render/shapes.js +35 -0
  105. package/dist/render/spinner.d.ts +2 -0
  106. package/dist/render/spinner.js +12 -0
  107. package/dist/render/stack.d.ts +3 -0
  108. package/dist/render/stack.js +38 -0
  109. package/dist/render/table.d.ts +3 -0
  110. package/dist/render/table.js +12 -0
  111. package/dist/render/text.d.ts +2 -0
  112. package/dist/render/text.js +27 -0
  113. package/dist/render/tree.d.ts +2 -0
  114. package/dist/render/tree.js +10 -0
  115. package/dist/render/types.d.ts +2 -0
  116. package/dist/render/types.js +1 -0
  117. package/dist/shared/@outfitter/cli-0ggcy7fa.js +20 -0
  118. package/dist/shared/@outfitter/cli-0psys2vm.js +7 -0
  119. package/dist/shared/@outfitter/cli-1bghjef6.js +352 -0
  120. package/dist/shared/@outfitter/cli-1kwbnt86.d.ts +45 -0
  121. package/dist/shared/@outfitter/cli-2g8bx1aq.d.ts +50 -0
  122. package/dist/shared/@outfitter/cli-33e97cjs.d.ts +42 -0
  123. package/dist/shared/@outfitter/cli-34fqr7bp.js +37 -0
  124. package/dist/shared/@outfitter/cli-3b7ed3rm.d.ts +97 -0
  125. package/dist/shared/@outfitter/cli-3dxmmy4c.d.ts +20 -0
  126. package/dist/shared/@outfitter/cli-3f12z5kf.d.ts +83 -0
  127. package/dist/shared/@outfitter/cli-3hp8qwx3.js +11 -0
  128. package/dist/shared/@outfitter/cli-3t2zaenc.d.ts +59 -0
  129. package/dist/shared/@outfitter/cli-4cb5g831.d.ts +147 -0
  130. package/dist/shared/@outfitter/cli-4w2a1rfy.d.ts +23 -0
  131. package/dist/shared/@outfitter/cli-4x6pqnez.js +20 -0
  132. package/dist/shared/@outfitter/cli-671sxkhj.js +146 -0
  133. package/dist/shared/@outfitter/cli-6bztk73z.d.ts +51 -0
  134. package/dist/shared/@outfitter/cli-6fxffp8k.js +1 -0
  135. package/dist/shared/@outfitter/cli-6j9qynm8.js +118 -0
  136. package/dist/shared/@outfitter/cli-6m988kh0.d.ts +61 -0
  137. package/dist/shared/@outfitter/cli-72kg550t.d.ts +53 -0
  138. package/dist/shared/@outfitter/cli-74ba31gz.js +134 -0
  139. package/dist/shared/@outfitter/cli-7gnrb8cr.js +214 -0
  140. package/dist/shared/@outfitter/cli-7na6p4fs.d.ts +59 -0
  141. package/dist/shared/@outfitter/cli-7nm6edvh.d.ts +17 -0
  142. package/dist/shared/@outfitter/cli-85fg2vr5.js +123 -0
  143. package/dist/shared/@outfitter/cli-8a8xrzhy.js +20 -0
  144. package/dist/shared/@outfitter/cli-8aa1vhdn.d.ts +119 -0
  145. package/dist/shared/@outfitter/cli-8bwaw3pz.js +7 -0
  146. package/dist/shared/@outfitter/cli-8j5k6mr3.js +71 -0
  147. package/dist/shared/@outfitter/cli-8rx4g3s5.d.ts +41 -0
  148. package/dist/shared/@outfitter/cli-8xsmsbbd.d.ts +223 -0
  149. package/dist/shared/@outfitter/cli-96b2p4td.d.ts +56 -0
  150. package/dist/shared/@outfitter/cli-9khk3cbq.d.ts +190 -0
  151. package/dist/shared/@outfitter/cli-9mtjjykw.js +67 -0
  152. package/dist/shared/@outfitter/cli-9nbyj2bt.js +128 -0
  153. package/dist/shared/@outfitter/cli-a4q87517.d.ts +64 -0
  154. package/dist/shared/@outfitter/cli-afhjqmg3.js +63 -0
  155. package/dist/shared/@outfitter/cli-an9j0h80.js +117 -0
  156. package/dist/shared/@outfitter/cli-ay411nbr.js +122 -0
  157. package/dist/shared/@outfitter/cli-b0tzqgnf.d.ts +132 -0
  158. package/dist/shared/@outfitter/cli-b5c2k0d7.js +39 -0
  159. package/dist/shared/@outfitter/cli-b5epywry.js +1 -0
  160. package/dist/shared/@outfitter/cli-bc17qeh2.js +19 -0
  161. package/dist/shared/@outfitter/cli-bcmcaz1b.js +23 -0
  162. package/dist/shared/@outfitter/cli-bf3vma4q.js +61 -0
  163. package/dist/shared/@outfitter/cli-c8q4f71g.js +144 -0
  164. package/dist/shared/@outfitter/cli-c9knfqn5.d.ts +30 -0
  165. package/dist/shared/@outfitter/cli-cf1xexgn.d.ts +53 -0
  166. package/dist/shared/@outfitter/cli-cf2s94s1.d.ts +42 -0
  167. package/dist/shared/@outfitter/cli-cs45xd6q.js +59 -0
  168. package/dist/shared/@outfitter/cli-d7jpshq5.d.ts +128 -0
  169. package/dist/shared/@outfitter/cli-d9ad0rqj.js +75 -0
  170. package/dist/shared/@outfitter/cli-daw296mv.js +61 -0
  171. package/dist/shared/@outfitter/cli-e5ms1y0x.d.ts +91 -0
  172. package/dist/shared/@outfitter/cli-e73v3qqy.d.ts +93 -0
  173. package/dist/shared/@outfitter/cli-efy6jfcj.js +52 -0
  174. package/dist/shared/@outfitter/cli-en6zn6sj.js +1 -0
  175. package/dist/shared/@outfitter/cli-ep2cvtk8.js +48 -0
  176. package/dist/shared/@outfitter/cli-evx7qcp1.d.ts +300 -0
  177. package/dist/shared/@outfitter/cli-f75h8e94.js +7 -0
  178. package/dist/shared/@outfitter/cli-fakncnjp.d.ts +106 -0
  179. package/dist/shared/@outfitter/cli-feb5j90n.js +94 -0
  180. package/dist/shared/@outfitter/cli-h20jc0bs.d.ts +66 -0
  181. package/dist/shared/@outfitter/cli-hnpbqmc8.d.ts +328 -0
  182. package/dist/shared/@outfitter/cli-j19a91ck.js +30 -0
  183. package/dist/shared/@outfitter/cli-j4n8gaf3.js +95 -0
  184. package/dist/shared/@outfitter/cli-jejfypgf.js +85 -0
  185. package/dist/shared/@outfitter/cli-jhcdwvpn.js +135 -0
  186. package/dist/shared/@outfitter/cli-jjemfdta.js +85 -0
  187. package/dist/shared/@outfitter/cli-kc84wmch.js +267 -0
  188. package/dist/shared/@outfitter/cli-ktqme80d.js +7 -0
  189. package/dist/shared/@outfitter/cli-mhamvbty.d.ts +34 -0
  190. package/dist/shared/@outfitter/cli-mq0jp15z.js +1 -0
  191. package/dist/shared/@outfitter/cli-mymyavvj.d.ts +26 -0
  192. package/dist/shared/@outfitter/cli-n17gt1dz.js +19 -0
  193. package/dist/shared/@outfitter/cli-n9dbh0hp.js +51 -0
  194. package/dist/shared/@outfitter/cli-nvvc92c8.js +128 -0
  195. package/dist/shared/@outfitter/cli-p1m5dhrs.js +169 -0
  196. package/dist/shared/@outfitter/cli-p38sfxyk.js +25 -0
  197. package/dist/shared/@outfitter/cli-p3dqm1vd.js +22 -0
  198. package/dist/shared/@outfitter/cli-p9j1phge.js +20 -0
  199. package/dist/shared/@outfitter/cli-pkxmzavm.js +62 -0
  200. package/dist/shared/@outfitter/cli-q8r6jarq.d.ts +24 -0
  201. package/dist/shared/@outfitter/cli-qj83y5wj.d.ts +71 -0
  202. package/dist/shared/@outfitter/cli-qjfc3j11.d.ts +112 -0
  203. package/dist/shared/@outfitter/cli-qp4cbhqr.js +70 -0
  204. package/dist/shared/@outfitter/cli-s0kkx9m1.d.ts +164 -0
  205. package/dist/shared/@outfitter/cli-snxj55n6.js +43 -0
  206. package/dist/shared/@outfitter/cli-swwxvjvm.d.ts +24 -0
  207. package/dist/shared/@outfitter/cli-sx67mmfx.d.ts +98 -0
  208. package/dist/shared/@outfitter/cli-tarpsa8a.js +30 -0
  209. package/dist/shared/@outfitter/cli-thvzhjd1.js +126 -0
  210. package/dist/shared/@outfitter/cli-tqewy503.d.ts +36 -0
  211. package/dist/shared/@outfitter/cli-ttt7r0j7.d.ts +253 -0
  212. package/dist/shared/@outfitter/cli-tvw1xrdj.js +20 -0
  213. package/dist/shared/@outfitter/cli-v1tzwxkt.js +32 -0
  214. package/dist/shared/@outfitter/cli-vd60dj65.js +1 -0
  215. package/dist/shared/@outfitter/cli-vp88gxev.js +279 -0
  216. package/dist/shared/@outfitter/cli-vstbkzky.d.ts +74 -0
  217. package/dist/shared/@outfitter/cli-vtg0sqk2.d.ts +54 -0
  218. package/dist/shared/@outfitter/cli-w5y3xepp.js +20 -0
  219. package/dist/shared/@outfitter/cli-x4cavvc0.js +1 -0
  220. package/dist/shared/@outfitter/cli-xep6v2c0.js +52 -0
  221. package/dist/shared/@outfitter/cli-xg5y5fhk.js +86 -0
  222. package/dist/shared/@outfitter/cli-xsaheemc.d.ts +248 -0
  223. package/dist/shared/@outfitter/cli-xvqtqjxk.js +82 -0
  224. package/dist/shared/@outfitter/cli-y25tt8nc.d.ts +48 -0
  225. package/dist/shared/@outfitter/cli-zact3325.js +152 -0
  226. package/dist/shared/@outfitter/cli-zx598p8q.d.ts +26 -0
  227. package/dist/streaming/ansi.d.ts +2 -0
  228. package/dist/streaming/ansi.js +8 -0
  229. package/dist/streaming/index.d.ts +4 -0
  230. package/dist/streaming/index.js +17 -0
  231. package/dist/streaming/spinner.d.ts +3 -0
  232. package/dist/streaming/spinner.js +10 -0
  233. package/dist/streaming/writer.d.ts +2 -0
  234. package/dist/streaming/writer.js +9 -0
  235. package/dist/table/index.d.ts +4 -0
  236. package/dist/table/index.js +13 -0
  237. package/dist/terminal/detection.d.ts +2 -0
  238. package/dist/terminal/detection.js +23 -0
  239. package/dist/terminal/index.d.ts +2 -0
  240. package/dist/terminal/index.js +24 -0
  241. package/dist/theme/context.d.ts +9 -0
  242. package/dist/theme/context.js +14 -0
  243. package/dist/theme/create.d.ts +8 -0
  244. package/dist/theme/create.js +12 -0
  245. package/dist/theme/index.d.ts +17 -0
  246. package/dist/theme/index.js +42 -0
  247. package/dist/theme/presets/bold.d.ts +8 -0
  248. package/dist/theme/presets/bold.js +12 -0
  249. package/dist/theme/presets/default.d.ts +8 -0
  250. package/dist/theme/presets/default.js +11 -0
  251. package/dist/theme/presets/index.d.ts +12 -0
  252. package/dist/theme/presets/index.js +24 -0
  253. package/dist/theme/presets/minimal.d.ts +8 -0
  254. package/dist/theme/presets/minimal.js +12 -0
  255. package/dist/theme/presets/rounded.d.ts +8 -0
  256. package/dist/theme/presets/rounded.js +12 -0
  257. package/dist/theme/resolve.d.ts +8 -0
  258. package/dist/theme/resolve.js +11 -0
  259. package/dist/theme/types.d.ts +7 -0
  260. package/dist/theme/types.js +1 -0
  261. package/dist/tree/index.d.ts +3 -0
  262. package/dist/tree/index.js +11 -0
  263. package/dist/types.d.ts +1 -252
  264. package/dist/types.js +1 -1
  265. package/package.json +228 -20
  266. package/dist/shared/@outfitter/cli-4yy82cmp.js +0 -20
@@ -0,0 +1,122 @@
1
+ // @bun
2
+ import {
3
+ TREE_GUIDES,
4
+ renderTree
5
+ } from "./cli-b5c2k0d7.js";
6
+ import {
7
+ getExample
8
+ } from "./cli-xep6v2c0.js";
9
+ import {
10
+ demoSection
11
+ } from "./cli-34fqr7bp.js";
12
+
13
+ // packages/cli/src/demo/renderers/tree.ts
14
+ function renderTreeDemo(config, _theme) {
15
+ const showCode = config.showCode ?? true;
16
+ const lines = [];
17
+ lines.push(demoSection("Basic Tree"));
18
+ lines.push("");
19
+ if (showCode) {
20
+ lines.push('import { renderTree } from "@outfitter/cli/tree";');
21
+ lines.push("");
22
+ }
23
+ const treeData = getExample("treeData", config.examples);
24
+ if (showCode) {
25
+ lines.push("renderTree({");
26
+ lines.push(" src: {");
27
+ lines.push(" components: { Button: null, Input: null },");
28
+ lines.push(" utils: null,");
29
+ lines.push(" },");
30
+ lines.push(" tests: null,");
31
+ lines.push("})");
32
+ lines.push("");
33
+ }
34
+ lines.push(renderTree(treeData));
35
+ lines.push("");
36
+ lines.push(demoSection("Guide Styles"));
37
+ lines.push("");
38
+ const guideDemo = {
39
+ root: {
40
+ child1: {
41
+ leaf: null
42
+ },
43
+ child2: null
44
+ }
45
+ };
46
+ const guideStyles = [
47
+ "single",
48
+ "rounded",
49
+ "heavy",
50
+ "double"
51
+ ];
52
+ for (const style of guideStyles) {
53
+ const guide = TREE_GUIDES[style];
54
+ lines.push(`${style.toUpperCase()} (fork: "${guide.fork.trim()}", end: "${guide.end.trim()}")`);
55
+ lines.push("");
56
+ lines.push(renderTree(guideDemo, { guide: style }));
57
+ lines.push("");
58
+ }
59
+ lines.push(demoSection("Max Depth"));
60
+ lines.push("");
61
+ const deepTree = {
62
+ level1: {
63
+ level2: {
64
+ level3: {
65
+ level4: null
66
+ }
67
+ }
68
+ }
69
+ };
70
+ if (showCode) {
71
+ lines.push("renderTree(tree, { maxDepth: 2 })");
72
+ lines.push("");
73
+ }
74
+ lines.push("Full tree:");
75
+ lines.push(renderTree(deepTree));
76
+ lines.push("");
77
+ lines.push("With maxDepth: 2:");
78
+ lines.push(renderTree(deepTree, { maxDepth: 2 }));
79
+ lines.push("");
80
+ lines.push(demoSection("Custom Labels"));
81
+ lines.push("");
82
+ const fileTree = {
83
+ src: {
84
+ "index.ts": null,
85
+ components: {
86
+ "Button.tsx": null
87
+ }
88
+ },
89
+ "package.json": null
90
+ };
91
+ if (showCode) {
92
+ lines.push("renderTree(tree, {");
93
+ lines.push(" renderLabel: (key, value) => {");
94
+ lines.push(" if (value && typeof value === 'object') {");
95
+ lines.push(" return `\uD83D\uDCC1 ${key}/`;");
96
+ lines.push(" }");
97
+ lines.push(" return `\uD83D\uDCC4 ${key}`;");
98
+ lines.push(" }");
99
+ lines.push("})");
100
+ lines.push("");
101
+ }
102
+ lines.push(renderTree(fileTree, {
103
+ renderLabel: (key, value) => {
104
+ if (value && typeof value === "object") {
105
+ return `\uD83D\uDCC1 ${key}/`;
106
+ }
107
+ return `\uD83D\uDCC4 ${key}`;
108
+ }
109
+ }));
110
+ lines.push("");
111
+ lines.push(demoSection("Usage Notes"));
112
+ lines.push("");
113
+ lines.push("\u2022 Objects become branches with children");
114
+ lines.push("\u2022 null values become leaf nodes (terminal)");
115
+ lines.push("\u2022 Use guide option to change visual style");
116
+ lines.push("\u2022 Use maxDepth to limit rendering depth");
117
+ lines.push("\u2022 Use renderLabel for custom node formatting");
118
+ return lines.join(`
119
+ `);
120
+ }
121
+
122
+ export { renderTreeDemo };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * List rendering utilities.
3
+ *
4
+ * Renders arrays as bullet lists with optional nesting and multiple styles.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ /**
9
+ * Available list styles for {@link renderList}.
10
+ *
11
+ * - `dash`: Uses - character (default)
12
+ * - `bullet`: Uses • character
13
+ * - `number`: Uses 1. for top-level, a. for nested, i. for deeply nested
14
+ * - `checkbox`: Uses ☐ for unchecked, ☑ for checked
15
+ */
16
+ type ListStyle = "dash" | "bullet" | "number" | "checkbox";
17
+ /**
18
+ * Options for customizing list rendering.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Numbered list with custom indent
23
+ * renderList(items, { style: "number", indent: 4 });
24
+ *
25
+ * // Checkbox list with some items checked
26
+ * renderList(items, { style: "checkbox", checked: new Set([1, 3]) });
27
+ * ```
28
+ */
29
+ interface ListOptions {
30
+ /**
31
+ * The list style to use.
32
+ * @default "dash"
33
+ */
34
+ style?: ListStyle;
35
+ /**
36
+ * Indices of checked top-level items (0-indexed) for checkbox style.
37
+ * Only applies to top-level items. For nested items, use the
38
+ * `checked` property on {@link NestedListItem} instead.
39
+ */
40
+ checked?: Set<number>;
41
+ /**
42
+ * Number of spaces per indentation level.
43
+ * @default 2
44
+ */
45
+ indent?: number;
46
+ }
47
+ /**
48
+ * A list item with optional nested children for {@link renderList}.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const item: NestedListItem = {
53
+ * text: "Parent item",
54
+ * children: ["Child 1", "Child 2"],
55
+ * };
56
+ *
57
+ * // Checkbox item with checked state
58
+ * const checkboxItem: NestedListItem = {
59
+ * text: "Completed task",
60
+ * checked: true,
61
+ * };
62
+ *
63
+ * // Mixed styles: numbered parent with bullet children
64
+ * const mixedItem: NestedListItem = {
65
+ * text: "Section 1",
66
+ * childStyle: "bullet",
67
+ * children: ["Unordered item A", "Unordered item B"],
68
+ * };
69
+ * ```
70
+ */
71
+ interface NestedListItem {
72
+ /** The text content of this list item */
73
+ text: string;
74
+ /** Optional nested child items (strings or nested items) */
75
+ children?: Array<string | NestedListItem>;
76
+ /** Whether this item is checked (for checkbox style) */
77
+ checked?: boolean;
78
+ /** Override style for children (enables mixed numbered/bullet lists) */
79
+ childStyle?: ListStyle;
80
+ }
81
+ /**
82
+ * A list item that can be either a simple string or a nested item with children.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const items: ListItem[] = [
87
+ * "Simple item",
88
+ * { text: "Parent", children: ["Child 1", "Child 2"] },
89
+ * ];
90
+ * ```
91
+ */
92
+ type ListItem = string | NestedListItem;
93
+ /**
94
+ * Renders items as a list with optional nesting and multiple styles.
95
+ *
96
+ * Supports both simple string items and nested items with children.
97
+ * The default style uses dash (-) characters.
98
+ *
99
+ * For numbered lists, child items are indented to align with the parent's
100
+ * content (after the marker), creating proper visual hierarchy.
101
+ *
102
+ * @param items - Array of list items (strings or nested items)
103
+ * @param options - Optional configuration for style, checked items, and indent
104
+ * @returns Formatted list string
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * // Simple dash list (default)
109
+ * console.log(renderList(["First", "Second", "Third"]));
110
+ * // - First
111
+ * // - Second
112
+ * // - Third
113
+ *
114
+ * // Numbered list with nesting
115
+ * console.log(renderList([
116
+ * { text: "First section", children: [
117
+ * { text: "Subsection A", children: ["Detail i", "Detail ii"] },
118
+ * ]},
119
+ * ], { style: "number" }));
120
+ * // 1. First section
121
+ * // a. Subsection A
122
+ * // i. Detail i
123
+ * // ii. Detail ii
124
+ *
125
+ * // Checkbox list
126
+ * console.log(renderList(["Todo 1", "Todo 2"], { style: "checkbox", checked: new Set([1]) }));
127
+ * // ☐ Todo 1
128
+ * // ☑ Todo 2
129
+ * ```
130
+ */
131
+ declare function renderList(items: ListItem[], options?: ListOptions): string;
132
+ export { ListStyle, ListOptions, NestedListItem, ListItem, renderList };
@@ -0,0 +1,39 @@
1
+ // @bun
2
+ // packages/cli/src/render/tree.ts
3
+ var TREE_GUIDES = {
4
+ single: { vertical: "\u2502 ", fork: "\u251C\u2500\u2500 ", end: "\u2514\u2500\u2500 " },
5
+ heavy: { vertical: "\u2503 ", fork: "\u2523\u2501\u2501 ", end: "\u2517\u2501\u2501 " },
6
+ double: { vertical: "\u2551 ", fork: "\u2560\u2550\u2550 ", end: "\u255A\u2550\u2550 " },
7
+ rounded: { vertical: "\u2502 ", fork: "\u251C\u2500\u2500 ", end: "\u2570\u2500\u2500 " }
8
+ };
9
+ function renderTree(tree, options) {
10
+ const guide = TREE_GUIDES[options?.guide ?? "single"];
11
+ const maxDepth = options?.maxDepth;
12
+ const renderLabel = options?.renderLabel ?? ((key) => key);
13
+ const lines = [];
14
+ const renderNode = (key, value, prefix, isLast, depth) => {
15
+ if (maxDepth !== undefined && depth >= maxDepth) {
16
+ return;
17
+ }
18
+ const connector = isLast ? guide.end : guide.fork;
19
+ const label = renderLabel(key, value, depth);
20
+ lines.push(prefix + connector + label);
21
+ if (value !== null && typeof value === "object") {
22
+ const entries2 = Object.entries(value);
23
+ const childPrefix = prefix + (isLast ? " " : guide.vertical);
24
+ entries2.forEach(([childKey, childValue], index) => {
25
+ const childIsLast = index === entries2.length - 1;
26
+ renderNode(childKey, childValue, childPrefix, childIsLast, depth + 1);
27
+ });
28
+ }
29
+ };
30
+ const entries = Object.entries(tree);
31
+ entries.forEach(([key, value], index) => {
32
+ const isLast = index === entries.length - 1;
33
+ renderNode(key, value, "", isLast, 0);
34
+ });
35
+ return lines.join(`
36
+ `);
37
+ }
38
+
39
+ export { TREE_GUIDES, renderTree };
@@ -0,0 +1 @@
1
+ // @bun
@@ -0,0 +1,19 @@
1
+ // @bun
2
+ // packages/cli/src/render/progress.ts
3
+ function renderProgress(options) {
4
+ const { current, total, width = 20, showPercent = false } = options;
5
+ if (total <= 0) {
6
+ const bar2 = "\u2591".repeat(width);
7
+ return showPercent ? `[${bar2}] 0%` : `[${bar2}]`;
8
+ }
9
+ const percent = Math.min(100, Math.max(0, current / total * 100));
10
+ const filled = Math.round(percent / 100 * width);
11
+ const empty = width - filled;
12
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
13
+ if (showPercent) {
14
+ return `[${bar}] ${Math.round(percent)}%`;
15
+ }
16
+ return `[${bar}]`;
17
+ }
18
+
19
+ export { renderProgress };
@@ -0,0 +1,23 @@
1
+ // @bun
2
+ import {
3
+ createCancelledError
4
+ } from "./cli-8bwaw3pz.js";
5
+
6
+ // packages/cli/src/prompt/confirm.ts
7
+ import { confirm, isCancel } from "@clack/prompts";
8
+ import { Result } from "better-result";
9
+ async function promptConfirm(options) {
10
+ const confirmOptions = {
11
+ message: options.message
12
+ };
13
+ if (options.initialValue !== undefined) {
14
+ confirmOptions.initialValue = options.initialValue;
15
+ }
16
+ const result = await confirm(confirmOptions);
17
+ if (isCancel(result)) {
18
+ return Result.err(createCancelledError());
19
+ }
20
+ return Result.ok(result);
21
+ }
22
+
23
+ export { promptConfirm };
@@ -0,0 +1,61 @@
1
+ // @bun
2
+ import {
3
+ createCancelledError
4
+ } from "./cli-8bwaw3pz.js";
5
+
6
+ // packages/cli/src/prompt/select.ts
7
+ import { isCancel, multiselect, select } from "@clack/prompts";
8
+ import { Result } from "better-result";
9
+ async function promptSelect(options) {
10
+ const clackOptions = options.options.map((opt) => {
11
+ const mapped = {
12
+ value: opt.value,
13
+ label: opt.label
14
+ };
15
+ if (opt.hint !== undefined) {
16
+ mapped.hint = opt.hint;
17
+ }
18
+ return mapped;
19
+ });
20
+ const selectOptions = {
21
+ message: options.message,
22
+ options: clackOptions
23
+ };
24
+ if (options.initialValue !== undefined) {
25
+ selectOptions.initialValue = options.initialValue;
26
+ }
27
+ const result = await select(selectOptions);
28
+ if (isCancel(result)) {
29
+ return Result.err(createCancelledError());
30
+ }
31
+ return Result.ok(result);
32
+ }
33
+ async function promptMultiSelect(options) {
34
+ const clackOptions = options.options.map((opt) => {
35
+ const mapped = {
36
+ value: opt.value,
37
+ label: opt.label
38
+ };
39
+ if (opt.hint !== undefined) {
40
+ mapped.hint = opt.hint;
41
+ }
42
+ return mapped;
43
+ });
44
+ const multiselectOptions = {
45
+ message: options.message,
46
+ options: clackOptions
47
+ };
48
+ if (options.initialValues !== undefined) {
49
+ multiselectOptions.initialValues = options.initialValues;
50
+ }
51
+ if (options.required !== undefined) {
52
+ multiselectOptions.required = options.required;
53
+ }
54
+ const result = await multiselect(multiselectOptions);
55
+ if (isCancel(result)) {
56
+ return Result.err(createCancelledError());
57
+ }
58
+ return Result.ok(result);
59
+ }
60
+
61
+ export { promptSelect, promptMultiSelect };
@@ -0,0 +1,144 @@
1
+ // @bun
2
+ // packages/cli/src/render/date.ts
3
+ import { Result, ValidationError } from "@outfitter/contracts";
4
+ var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
5
+ var NAMED_RANGES = ["today", "yesterday", "last week", "last month"];
6
+ function startOfDay(date) {
7
+ const result = new Date(date);
8
+ result.setHours(0, 0, 0, 0);
9
+ return result;
10
+ }
11
+ function endOfDay(date) {
12
+ const result = new Date(date);
13
+ result.setHours(23, 59, 59, 999);
14
+ return result;
15
+ }
16
+ function isNamedRange(input) {
17
+ return NAMED_RANGES.includes(input);
18
+ }
19
+ function parseNamedRange(name) {
20
+ const now = new Date(Date.now());
21
+ switch (name) {
22
+ case "today": {
23
+ return {
24
+ start: startOfDay(now),
25
+ end: endOfDay(now)
26
+ };
27
+ }
28
+ case "yesterday": {
29
+ const yesterday = new Date(now);
30
+ yesterday.setDate(yesterday.getDate() - 1);
31
+ return {
32
+ start: startOfDay(yesterday),
33
+ end: endOfDay(yesterday)
34
+ };
35
+ }
36
+ case "last week": {
37
+ const weekAgo = new Date(now);
38
+ weekAgo.setDate(weekAgo.getDate() - 7);
39
+ return {
40
+ start: startOfDay(weekAgo),
41
+ end: endOfDay(now)
42
+ };
43
+ }
44
+ case "last month": {
45
+ const monthAgo = new Date(now);
46
+ monthAgo.setDate(monthAgo.getDate() - 30);
47
+ return {
48
+ start: startOfDay(monthAgo),
49
+ end: endOfDay(now)
50
+ };
51
+ }
52
+ default: {
53
+ const _exhaustive = name;
54
+ throw new Error(`Unhandled named range: ${_exhaustive}`);
55
+ }
56
+ }
57
+ }
58
+ function parseIsoDate(dateStr) {
59
+ if (!ISO_DATE_REGEX.test(dateStr)) {
60
+ return null;
61
+ }
62
+ const parts = dateStr.split("-");
63
+ const yearStr = parts[0];
64
+ const monthStr = parts[1];
65
+ const dayStr = parts[2];
66
+ if (yearStr === undefined || monthStr === undefined || dayStr === undefined) {
67
+ return null;
68
+ }
69
+ const year = Number.parseInt(yearStr, 10);
70
+ const month = Number.parseInt(monthStr, 10) - 1;
71
+ const day = Number.parseInt(dayStr, 10);
72
+ const date = new Date(year, month, day);
73
+ if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
74
+ return null;
75
+ }
76
+ return date;
77
+ }
78
+ function parseDateRange(input) {
79
+ const trimmed = input.trim();
80
+ if (trimmed === "") {
81
+ return Result.err(new ValidationError({
82
+ message: "Date range input cannot be empty",
83
+ field: "dateRange"
84
+ }));
85
+ }
86
+ const normalized = trimmed.toLowerCase();
87
+ if (isNamedRange(normalized)) {
88
+ return Result.ok(parseNamedRange(normalized));
89
+ }
90
+ if (trimmed.includes("..")) {
91
+ const parts = trimmed.split("..");
92
+ if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
93
+ return Result.err(new ValidationError({
94
+ message: 'Invalid date range format. Expected "YYYY-MM-DD..YYYY-MM-DD"',
95
+ field: "dateRange"
96
+ }));
97
+ }
98
+ const startStr = parts[0];
99
+ const endStr = parts[1];
100
+ if (startStr === undefined || endStr === undefined) {
101
+ return Result.err(new ValidationError({
102
+ message: 'Invalid date range format. Expected "YYYY-MM-DD..YYYY-MM-DD"',
103
+ field: "dateRange"
104
+ }));
105
+ }
106
+ const startDate = parseIsoDate(startStr);
107
+ const endDate = parseIsoDate(endStr);
108
+ if (startDate === null) {
109
+ return Result.err(new ValidationError({
110
+ message: `Invalid start date: "${startStr}". Expected format: YYYY-MM-DD`,
111
+ field: "dateRange"
112
+ }));
113
+ }
114
+ if (endDate === null) {
115
+ return Result.err(new ValidationError({
116
+ message: `Invalid end date: "${endStr}". Expected format: YYYY-MM-DD`,
117
+ field: "dateRange"
118
+ }));
119
+ }
120
+ if (startDate.getTime() > endDate.getTime()) {
121
+ return Result.err(new ValidationError({
122
+ message: "Invalid date range: start date must be before or equal to end date",
123
+ field: "dateRange"
124
+ }));
125
+ }
126
+ return Result.ok({
127
+ start: startOfDay(startDate),
128
+ end: endOfDay(endDate)
129
+ });
130
+ }
131
+ const singleDate = parseIsoDate(trimmed);
132
+ if (singleDate !== null) {
133
+ return Result.ok({
134
+ start: startOfDay(singleDate),
135
+ end: endOfDay(singleDate)
136
+ });
137
+ }
138
+ return Result.err(new ValidationError({
139
+ message: `Unrecognized date range: "${trimmed}". Expected "today", "yesterday", "last week", "last month", "YYYY-MM-DD", or "YYYY-MM-DD..YYYY-MM-DD"`,
140
+ field: "dateRange"
141
+ }));
142
+ }
143
+
144
+ export { startOfDay, endOfDay, parseDateRange };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Renders markdown to terminal with ANSI styling.
3
+ *
4
+ * Supports the following markdown elements:
5
+ * - Headings (`# Heading`) - rendered bold
6
+ * - Bold (`**text**` or `__text__`) - rendered bold
7
+ * - Italic (`*text*` or `_text_`) - rendered italic
8
+ * - Inline code (`` `code` ``) - rendered cyan
9
+ * - Code blocks (` ``` `) - rendered dim
10
+ *
11
+ * When colors are not supported, markdown syntax is stripped
12
+ * but text content is preserved.
13
+ *
14
+ * @param markdown - Markdown text to render
15
+ * @returns Terminal-formatted string with ANSI codes
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const md = `# Heading
20
+ *
21
+ * Some **bold** and *italic* text.
22
+ *
23
+ * Use \`npm install\` to install.
24
+ * `;
25
+ *
26
+ * console.log(renderMarkdown(md));
27
+ * ```
28
+ */
29
+ declare function renderMarkdown(markdown: string): string;
30
+ export { renderMarkdown };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Minimal writable stream interface.
3
+ */
4
+ interface WritableStream {
5
+ write(str: string): boolean;
6
+ isTTY?: boolean;
7
+ }
8
+ /**
9
+ * Options for creating a stream writer.
10
+ */
11
+ interface StreamWriterOptions {
12
+ /** Target stream (defaults to process.stdout) */
13
+ stream?: WritableStream;
14
+ }
15
+ /**
16
+ * Interface for managing in-place terminal output.
17
+ */
18
+ interface StreamWriter {
19
+ /** Write content (replaces current content) */
20
+ write(content: string): void;
21
+ /** Update content in place */
22
+ update(content: string): void;
23
+ /** Persist current content and move to new line */
24
+ persist(): void;
25
+ /** Clear current content */
26
+ clear(): void;
27
+ }
28
+ /**
29
+ * Creates a stream writer for in-place terminal updates.
30
+ *
31
+ * The writer tracks the number of lines written and can update them in place.
32
+ * In non-TTY mode, it falls back to simple line-by-line output.
33
+ *
34
+ * @param options - Writer configuration
35
+ * @returns StreamWriter instance
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * import { createStreamWriter } from "@outfitter/cli/streaming";
40
+ *
41
+ * const writer = createStreamWriter();
42
+ *
43
+ * writer.write("Processing...");
44
+ * // Do some work
45
+ * writer.update("Processing... 50%");
46
+ * // Do more work
47
+ * writer.update("Processing... 100%");
48
+ * writer.persist();
49
+ * writer.write("Done!");
50
+ * ```
51
+ */
52
+ declare function createStreamWriter(options?: StreamWriterOptions): StreamWriter;
53
+ export { WritableStream, StreamWriterOptions, StreamWriter, createStreamWriter };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Formatting utilities for human-readable output.
3
+ *
4
+ * Provides functions to format durations and byte sizes.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ /**
9
+ * Formats milliseconds as human-readable duration.
10
+ *
11
+ * Converts milliseconds to a compact, human-friendly format using
12
+ * h (hours), m (minutes), s (seconds), or ms (milliseconds).
13
+ *
14
+ * @param ms - Duration in milliseconds
15
+ * @returns Human-friendly duration string
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * formatDuration(150) // "150ms"
20
+ * formatDuration(45000) // "45s"
21
+ * formatDuration(9015000) // "2h 30m 15s"
22
+ * ```
23
+ */
24
+ declare function formatDuration(ms: number): string;
25
+ /**
26
+ * Formats bytes as human-readable size.
27
+ *
28
+ * Converts bytes to a compact format using appropriate units
29
+ * (B, KB, MB, GB, TB). Uses 1024-based (binary) units.
30
+ *
31
+ * @param bytes - Size in bytes
32
+ * @returns Human-friendly size string
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * formatBytes(500) // "500 B"
37
+ * formatBytes(1536) // "1.5 KB"
38
+ * formatBytes(1073741824) // "1 GB"
39
+ * ```
40
+ */
41
+ declare function formatBytes(bytes: number): string;
42
+ export { formatDuration, formatBytes };