@compilr-dev/cli 0.4.0 → 0.5.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 (315) hide show
  1. package/README.md +30 -12
  2. package/dist/agent.d.ts +74 -1
  3. package/dist/agent.js +259 -76
  4. package/dist/anchors/index.d.ts +9 -0
  5. package/dist/anchors/index.js +9 -0
  6. package/dist/anchors/project-anchors.d.ts +79 -0
  7. package/dist/anchors/project-anchors.js +202 -0
  8. package/dist/commands/handler-types.d.ts +68 -0
  9. package/dist/commands/handler-types.js +8 -0
  10. package/dist/commands/handlers/agent-commands.d.ts +13 -0
  11. package/dist/commands/handlers/agent-commands.js +305 -0
  12. package/dist/commands/handlers/design-commands.d.ts +15 -0
  13. package/dist/commands/handlers/design-commands.js +334 -0
  14. package/dist/commands/handlers/index.d.ts +20 -0
  15. package/dist/commands/handlers/index.js +43 -0
  16. package/dist/commands/handlers/overlay-commands.d.ts +21 -0
  17. package/dist/commands/handlers/overlay-commands.js +287 -0
  18. package/dist/commands/handlers/project-commands.d.ts +11 -0
  19. package/dist/commands/handlers/project-commands.js +167 -0
  20. package/dist/commands/handlers/simple-commands.d.ts +19 -0
  21. package/dist/commands/handlers/simple-commands.js +144 -0
  22. package/dist/commands/index.d.ts +2 -1
  23. package/dist/commands/registry.d.ts +50 -0
  24. package/dist/commands/registry.js +75 -0
  25. package/dist/commands-v2/handlers/context.d.ts +13 -0
  26. package/dist/commands-v2/handlers/context.js +348 -0
  27. package/dist/commands-v2/handlers/core.d.ts +13 -0
  28. package/dist/commands-v2/handlers/core.js +165 -0
  29. package/dist/commands-v2/handlers/debug.d.ts +11 -0
  30. package/dist/commands-v2/handlers/debug.js +159 -0
  31. package/dist/commands-v2/handlers/index.d.ts +12 -0
  32. package/dist/commands-v2/handlers/index.js +24 -0
  33. package/dist/commands-v2/handlers/project.d.ts +22 -0
  34. package/dist/commands-v2/handlers/project.js +814 -0
  35. package/dist/commands-v2/handlers/settings.d.ts +15 -0
  36. package/dist/commands-v2/handlers/settings.js +235 -0
  37. package/dist/commands-v2/index.d.ts +13 -0
  38. package/dist/commands-v2/index.js +15 -0
  39. package/dist/commands-v2/registry.d.ts +37 -0
  40. package/dist/commands-v2/registry.js +80 -0
  41. package/dist/commands-v2/types.d.ts +75 -0
  42. package/dist/commands-v2/types.js +7 -0
  43. package/dist/commands.js +110 -7
  44. package/dist/index.js +288 -29
  45. package/dist/input-handlers/index.d.ts +7 -0
  46. package/dist/input-handlers/index.js +7 -0
  47. package/dist/input-handlers/memory-handler.d.ts +26 -0
  48. package/dist/input-handlers/memory-handler.js +68 -0
  49. package/dist/repl-helpers.d.ts +63 -0
  50. package/dist/repl-helpers.js +318 -0
  51. package/dist/repl-v2.d.ts +155 -0
  52. package/dist/repl-v2.js +774 -0
  53. package/dist/repl.d.ts +32 -4
  54. package/dist/repl.js +250 -977
  55. package/dist/settings/index.d.ts +23 -0
  56. package/dist/settings/index.js +48 -0
  57. package/dist/settings/paths.d.ts +110 -0
  58. package/dist/settings/paths.js +264 -0
  59. package/dist/templates/compilr-md.js +7 -4
  60. package/dist/templates/index.js +3 -4
  61. package/dist/themes/colors.js +3 -1
  62. package/dist/themes/registry.d.ts +5 -36
  63. package/dist/themes/registry.js +11 -95
  64. package/dist/themes/types.d.ts +3 -38
  65. package/dist/themes/types.js +2 -2
  66. package/dist/tools/anchor-tools.d.ts +31 -0
  67. package/dist/tools/anchor-tools.js +255 -0
  68. package/dist/tools/backlog-wrappers.d.ts +54 -0
  69. package/dist/tools/backlog-wrappers.js +338 -0
  70. package/dist/tools/backlog.js +1 -1
  71. package/dist/tools/db-tools.d.ts +65 -0
  72. package/dist/tools/db-tools.js +19 -0
  73. package/dist/tools/document-db.d.ts +43 -0
  74. package/dist/tools/document-db.js +220 -0
  75. package/dist/tools/project-db.d.ts +102 -0
  76. package/dist/tools/project-db.js +370 -0
  77. package/dist/tools/workitem-db.d.ts +103 -0
  78. package/dist/tools/workitem-db.js +549 -0
  79. package/dist/tools.js +13 -3
  80. package/dist/ui/agents-overlay-v2.d.ts +43 -0
  81. package/dist/ui/agents-overlay-v2.js +809 -0
  82. package/dist/ui/agents-overlay.d.ts +5 -5
  83. package/dist/ui/agents-overlay.js +782 -420
  84. package/dist/ui/anchors-overlay.d.ts +12 -0
  85. package/dist/ui/anchors-overlay.js +775 -0
  86. package/dist/ui/arch-type-overlay.d.ts +1 -6
  87. package/dist/ui/arch-type-overlay.js +175 -203
  88. package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
  89. package/dist/ui/ask-user-overlay-v2.js +555 -0
  90. package/dist/ui/ask-user-overlay.d.ts +2 -2
  91. package/dist/ui/ask-user-overlay.js +443 -535
  92. package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
  93. package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
  94. package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
  95. package/dist/ui/ask-user-simple-overlay.js +182 -209
  96. package/dist/ui/backlog-overlay.d.ts +16 -1
  97. package/dist/ui/backlog-overlay.js +525 -659
  98. package/dist/ui/base/index.d.ts +26 -0
  99. package/dist/ui/base/index.js +33 -0
  100. package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
  101. package/dist/ui/base/inline-overlay-utils.js +320 -0
  102. package/dist/ui/base/inline-overlay.d.ts +159 -0
  103. package/dist/ui/base/inline-overlay.js +257 -0
  104. package/dist/ui/base/key-utils.d.ts +15 -0
  105. package/dist/ui/base/key-utils.js +30 -0
  106. package/dist/ui/base/overlay-base-v2.d.ts +193 -0
  107. package/dist/ui/base/overlay-base-v2.js +246 -0
  108. package/dist/ui/base/overlay-base.d.ts +156 -0
  109. package/dist/ui/base/overlay-base.js +238 -0
  110. package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
  111. package/dist/ui/base/overlay-lifecycle.js +159 -0
  112. package/dist/ui/base/overlay-types.d.ts +185 -0
  113. package/dist/ui/base/overlay-types.js +7 -0
  114. package/dist/ui/base/render-utils.d.ts +8 -0
  115. package/dist/ui/base/render-utils.js +11 -0
  116. package/dist/ui/base/screen-stack.d.ts +148 -0
  117. package/dist/ui/base/screen-stack.js +184 -0
  118. package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
  119. package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
  120. package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
  121. package/dist/ui/base/tabbed-list-overlay.js +369 -0
  122. package/dist/ui/commands-overlay-v2.d.ts +33 -0
  123. package/dist/ui/commands-overlay-v2.js +441 -0
  124. package/dist/ui/commands-overlay.d.ts +7 -2
  125. package/dist/ui/commands-overlay.js +384 -355
  126. package/dist/ui/config-overlay.d.ts +5 -4
  127. package/dist/ui/config-overlay.js +243 -513
  128. package/dist/ui/conversation.d.ts +75 -4
  129. package/dist/ui/conversation.js +374 -161
  130. package/dist/ui/docs-overlay.d.ts +17 -0
  131. package/dist/ui/docs-overlay.js +303 -0
  132. package/dist/ui/ephemeral.d.ts +1 -1
  133. package/dist/ui/ephemeral.js +1 -1
  134. package/dist/ui/features/index.d.ts +34 -0
  135. package/dist/ui/features/index.js +34 -0
  136. package/dist/ui/features/input-feature.d.ts +85 -0
  137. package/dist/ui/features/input-feature.js +238 -0
  138. package/dist/ui/features/list-feature.d.ts +155 -0
  139. package/dist/ui/features/list-feature.js +244 -0
  140. package/dist/ui/features/pagination-feature.d.ts +154 -0
  141. package/dist/ui/features/pagination-feature.js +238 -0
  142. package/dist/ui/features/search-feature.d.ts +148 -0
  143. package/dist/ui/features/search-feature.js +185 -0
  144. package/dist/ui/features/tab-feature.d.ts +194 -0
  145. package/dist/ui/features/tab-feature.js +307 -0
  146. package/dist/ui/footer-v2.d.ts +222 -0
  147. package/dist/ui/footer-v2.js +1349 -0
  148. package/dist/ui/footer.d.ts +107 -0
  149. package/dist/ui/footer.js +359 -67
  150. package/dist/ui/guardrail-overlay.d.ts +29 -0
  151. package/dist/ui/guardrail-overlay.js +145 -0
  152. package/dist/ui/help-overlay-v2.d.ts +34 -0
  153. package/dist/ui/help-overlay-v2.js +309 -0
  154. package/dist/ui/help-overlay.d.ts +16 -0
  155. package/dist/ui/help-overlay.js +316 -0
  156. package/dist/ui/index.d.ts +1 -1
  157. package/dist/ui/index.js +1 -3
  158. package/dist/ui/init-overlay-v2.d.ts +34 -0
  159. package/dist/ui/init-overlay-v2.js +600 -0
  160. package/dist/ui/init-overlay.d.ts +12 -2
  161. package/dist/ui/init-overlay.js +349 -270
  162. package/dist/ui/input-prompt-v2.d.ts +1 -0
  163. package/dist/ui/input-prompt-v2.js +14 -6
  164. package/dist/ui/input-prompt.d.ts +116 -33
  165. package/dist/ui/input-prompt.js +536 -337
  166. package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
  167. package/dist/ui/iteration-limit-overlay-v2.js +114 -0
  168. package/dist/ui/iteration-limit-overlay.d.ts +2 -2
  169. package/dist/ui/iteration-limit-overlay.js +92 -128
  170. package/dist/ui/keys-overlay-v2.d.ts +41 -0
  171. package/dist/ui/keys-overlay-v2.js +248 -0
  172. package/dist/ui/keys-overlay.d.ts +1 -0
  173. package/dist/ui/keys-overlay.js +203 -141
  174. package/dist/ui/line-utils.d.ts +88 -0
  175. package/dist/ui/line-utils.js +150 -0
  176. package/dist/ui/live-region.d.ts +161 -0
  177. package/dist/ui/live-region.js +387 -0
  178. package/dist/ui/mascot/expressions.d.ts +32 -0
  179. package/dist/ui/mascot/expressions.js +213 -0
  180. package/dist/ui/mascot/index.d.ts +8 -0
  181. package/dist/ui/mascot/index.js +8 -0
  182. package/dist/ui/mascot/renderer.d.ts +19 -0
  183. package/dist/ui/mascot/renderer.js +97 -0
  184. package/dist/ui/mascot-overlay-v2.d.ts +41 -0
  185. package/dist/ui/mascot-overlay-v2.js +138 -0
  186. package/dist/ui/mascot-overlay.d.ts +21 -0
  187. package/dist/ui/mascot-overlay.js +146 -0
  188. package/dist/ui/model-overlay-v2.d.ts +49 -0
  189. package/dist/ui/model-overlay-v2.js +118 -0
  190. package/dist/ui/model-overlay.d.ts +27 -0
  191. package/dist/ui/model-overlay.js +221 -0
  192. package/dist/ui/model-warning-overlay.js +3 -5
  193. package/dist/ui/new-overlay.d.ts +34 -0
  194. package/dist/ui/new-overlay.js +604 -0
  195. package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
  196. package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
  197. package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
  198. package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
  199. package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
  200. package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
  201. package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
  202. package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
  203. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
  204. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
  205. package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
  206. package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
  207. package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
  208. package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
  209. package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
  210. package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
  211. package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
  212. package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
  213. package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
  214. package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
  215. package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
  216. package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
  217. package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
  218. package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
  219. package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
  220. package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
  221. package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
  222. package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
  223. package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
  224. package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
  225. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
  226. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
  227. package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
  228. package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
  229. package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
  230. package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
  231. package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
  232. package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
  233. package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
  234. package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
  235. package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
  236. package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
  237. package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
  238. package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
  239. package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
  240. package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
  241. package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
  242. package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
  243. package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
  244. package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
  245. package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
  246. package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
  247. package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
  248. package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
  249. package/dist/ui/overlay/index.d.ts +33 -0
  250. package/dist/ui/overlay/index.js +35 -0
  251. package/dist/ui/overlay/key-utils.d.ts +6 -0
  252. package/dist/ui/overlay/key-utils.js +6 -0
  253. package/dist/ui/overlay/overlay-types.d.ts +128 -0
  254. package/dist/ui/overlay/overlay-types.js +22 -0
  255. package/dist/ui/overlay/types.d.ts +135 -0
  256. package/dist/ui/overlay/types.js +22 -0
  257. package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
  258. package/dist/ui/overlays/help-overlay-v2.js +198 -0
  259. package/dist/ui/overlays/index.d.ts +11 -0
  260. package/dist/ui/overlays/index.js +11 -0
  261. package/dist/ui/overlays.d.ts +0 -4
  262. package/dist/ui/overlays.js +0 -444
  263. package/dist/ui/permission-overlay-v2.d.ts +36 -0
  264. package/dist/ui/permission-overlay-v2.js +380 -0
  265. package/dist/ui/permission-overlay.d.ts +1 -1
  266. package/dist/ui/permission-overlay.js +186 -298
  267. package/dist/ui/projects-overlay.d.ts +19 -0
  268. package/dist/ui/projects-overlay.js +484 -0
  269. package/dist/ui/providers/types.d.ts +178 -0
  270. package/dist/ui/providers/types.js +9 -0
  271. package/dist/ui/render-modes.d.ts +36 -0
  272. package/dist/ui/render-modes.js +44 -0
  273. package/dist/ui/startup-menu.d.ts +36 -0
  274. package/dist/ui/startup-menu.js +236 -0
  275. package/dist/ui/subagent-renderer.d.ts +117 -0
  276. package/dist/ui/subagent-renderer.js +334 -0
  277. package/dist/ui/terminal-codes.d.ts +94 -0
  278. package/dist/ui/terminal-codes.js +124 -0
  279. package/dist/ui/terminal-renderer.d.ts +221 -0
  280. package/dist/ui/terminal-renderer.js +751 -0
  281. package/dist/ui/terminal-ui.d.ts +463 -0
  282. package/dist/ui/terminal-ui.js +2296 -0
  283. package/dist/ui/terminal.d.ts +20 -0
  284. package/dist/ui/terminal.js +72 -0
  285. package/dist/ui/theme-overlay-v2.d.ts +42 -0
  286. package/dist/ui/theme-overlay-v2.js +135 -0
  287. package/dist/ui/theme-overlay.d.ts +24 -0
  288. package/dist/ui/theme-overlay.js +127 -0
  289. package/dist/ui/todo-zone.js +53 -25
  290. package/dist/ui/tool-formatters.d.ts +16 -0
  291. package/dist/ui/tool-formatters.js +516 -0
  292. package/dist/ui/tools-overlay-v2.d.ts +47 -0
  293. package/dist/ui/tools-overlay-v2.js +218 -0
  294. package/dist/ui/tools-overlay.d.ts +10 -2
  295. package/dist/ui/tools-overlay.js +172 -220
  296. package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
  297. package/dist/ui/tutorial-overlay-v2.js +1035 -0
  298. package/dist/ui/tutorial-overlay.d.ts +1 -0
  299. package/dist/ui/tutorial-overlay.js +400 -302
  300. package/dist/ui/workflow-overlay.d.ts +22 -0
  301. package/dist/ui/workflow-overlay.js +636 -0
  302. package/dist/utils/debug-log.d.ts +28 -0
  303. package/dist/utils/debug-log.js +57 -0
  304. package/dist/utils/model-tiers.js +1 -1
  305. package/dist/utils/path-safety.d.ts +56 -0
  306. package/dist/utils/path-safety.js +239 -0
  307. package/dist/workflow/guided-mode-injector.d.ts +42 -0
  308. package/dist/workflow/guided-mode-injector.js +191 -0
  309. package/dist/workflow/index.d.ts +8 -0
  310. package/dist/workflow/index.js +8 -0
  311. package/dist/workflow/step-criteria.d.ts +62 -0
  312. package/dist/workflow/step-criteria.js +150 -0
  313. package/dist/workflow/step-tracker.d.ts +92 -0
  314. package/dist/workflow/step-tracker.js +141 -0
  315. package/package.json +12 -5
@@ -0,0 +1,584 @@
1
+ /**
2
+ * Ask User Overlay V2
3
+ *
4
+ * Modal overlay for presenting multi-question forms to the user.
5
+ * Uses BaseOverlayV2 for TerminalUI integration.
6
+ *
7
+ * Features:
8
+ * - Multiple questions with navigation (Left/Right or Tab)
9
+ * - Options to select from with arrow keys
10
+ * - Custom text input option
11
+ * - Multi-select support
12
+ * - Progress indicator showing answered questions
13
+ * - Submit tab at the end
14
+ */
15
+ import chalk from 'chalk';
16
+ import { BaseOverlayV2, renderBorder } from '../../base/index.js';
17
+ // =============================================================================
18
+ // Overlay Implementation
19
+ // =============================================================================
20
+ export class AskUserOverlayV2 extends BaseOverlayV2 {
21
+ type = 'inline';
22
+ id = 'ask-user-overlay-v2';
23
+ questions;
24
+ context;
25
+ constructor(options) {
26
+ // Check if first question should auto-start in typing mode
27
+ const firstQ = options.questions[0];
28
+ const firstQAutoType = firstQ !== undefined &&
29
+ (firstQ.options?.length ?? 0) === 0 &&
30
+ firstQ.allowCustom !== false &&
31
+ firstQ.multiSelect !== true;
32
+ super({
33
+ currentQuestion: 0,
34
+ selectedIndex: 0,
35
+ inputBuffer: '',
36
+ isTypingCustom: firstQAutoType,
37
+ answers: {},
38
+ multiSelections: new Set(),
39
+ warningMessage: '',
40
+ isOnSubmitTab: false,
41
+ });
42
+ this.questions = options.questions;
43
+ this.context = options.context;
44
+ this.minHeight = 20; // Prevent jitter with multi-question forms
45
+ }
46
+ renderContent(context) {
47
+ const s = context.styles;
48
+ const lines = [];
49
+ const cols = context.width;
50
+ const border = renderBorder(cols, s);
51
+ // Header
52
+ lines.push(border);
53
+ lines.push(' ' + s.primaryBold('Questions'));
54
+ lines.push('');
55
+ // Tab bar
56
+ lines.push(...this.renderTabBar(s));
57
+ lines.push('');
58
+ // Current question or Submit tab
59
+ if (this.state.isOnSubmitTab) {
60
+ lines.push(...this.renderSubmitTab(s));
61
+ }
62
+ else {
63
+ lines.push(...this.renderQuestion(s));
64
+ }
65
+ // Footer with instructions
66
+ lines.push(...this.renderInstructions(s));
67
+ // Warning message if present
68
+ if (this.state.warningMessage) {
69
+ lines.push('');
70
+ lines.push(s.warning(` ! ${this.state.warningMessage}`));
71
+ }
72
+ // Bottom border
73
+ lines.push(border);
74
+ return lines;
75
+ }
76
+ handleKey(key) {
77
+ // Ctrl+C always cancels
78
+ if (key.ctrl && key.name === 'c') {
79
+ return this.close(this.getResult());
80
+ }
81
+ // Handle Submit tab first
82
+ if (this.state.isOnSubmitTab) {
83
+ return this.handleSubmitTabKey(key);
84
+ }
85
+ // Handle typing mode
86
+ if (this.state.isTypingCustom) {
87
+ return this.handleTypingKey(key);
88
+ }
89
+ // Handle navigation mode
90
+ return this.handleNavigationKey(key);
91
+ }
92
+ /**
93
+ * Summary shown after overlay closes.
94
+ */
95
+ getCloseSummary(result) {
96
+ const s = this.getStyles();
97
+ const answeredCount = Object.keys(result.answers).length;
98
+ const skippedCount = result.skipped.length;
99
+ if (answeredCount === 0) {
100
+ return s.muted('Questions: ') + s.warning('Cancelled');
101
+ }
102
+ if (skippedCount > 0) {
103
+ return s.muted('Questions: ') + s.success(`${String(answeredCount)} answered`) + s.muted(`, ${String(skippedCount)} skipped`);
104
+ }
105
+ return s.muted('Questions: ') + s.success(`${String(answeredCount)} answered`);
106
+ }
107
+ // ===========================================================================
108
+ // Private: Rendering Helpers
109
+ // ===========================================================================
110
+ renderTabBar(s) {
111
+ const lines = [];
112
+ let tabLine = ' ';
113
+ for (let i = 0; i < this.questions.length; i++) {
114
+ const q = this.questions[i];
115
+ const hasAnswer = q.id in this.state.answers;
116
+ const isCurrent = i === this.state.currentQuestion && !this.state.isOnSubmitTab;
117
+ const label = q.header || q.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
118
+ if (isCurrent) {
119
+ tabLine += s.selected(` ${label} `) + ' ';
120
+ }
121
+ else if (hasAnswer) {
122
+ tabLine += s.success(`* ${label}`) + ' ';
123
+ }
124
+ else {
125
+ tabLine += s.muted(` ${label} `) + ' ';
126
+ }
127
+ }
128
+ // Add Submit tab
129
+ const allAnswered = this.questions.every((q) => q.id in this.state.answers);
130
+ if (this.state.isOnSubmitTab) {
131
+ tabLine += s.selected(' Submit ');
132
+ }
133
+ else if (allAnswered) {
134
+ tabLine += s.success(' Submit ');
135
+ }
136
+ else {
137
+ tabLine += s.muted(' Submit ');
138
+ }
139
+ lines.push(tabLine);
140
+ return lines;
141
+ }
142
+ renderQuestion(s) {
143
+ const lines = [];
144
+ const question = this.questions[this.state.currentQuestion];
145
+ const questionIndex = this.state.currentQuestion;
146
+ const totalQuestions = this.questions.length;
147
+ // Context if provided (only on first question)
148
+ if (this.context && questionIndex === 0) {
149
+ lines.push(s.muted(' ' + this.context));
150
+ lines.push('');
151
+ }
152
+ // Question number and text
153
+ lines.push(chalk.bold(` [${String(questionIndex + 1)}/${String(totalQuestions)}] ${question.question}`));
154
+ lines.push('');
155
+ // Options
156
+ const options = question.options ?? [];
157
+ const allowCustom = question.allowCustom !== false;
158
+ const isMultiSelect = question.multiSelect === true;
159
+ const customIndex = options.length;
160
+ // Get existing answer info
161
+ const { answeredOptions, customAnswerValue } = this.getAnswerInfo(question);
162
+ for (let i = 0; i < options.length; i++) {
163
+ const isCursor = this.state.selectedIndex === i;
164
+ const isMultiSelected = isMultiSelect && this.state.multiSelections.has(i);
165
+ const isAnswered = answeredOptions.has(i);
166
+ const prefix = isCursor ? ' > ' : ' ';
167
+ let marker = '';
168
+ if (isMultiSelect) {
169
+ marker = isMultiSelected ? '[*] ' : '[ ] ';
170
+ }
171
+ else if (isAnswered) {
172
+ marker = '* ';
173
+ }
174
+ const label = `${String(i + 1)}. ${options[i]}`;
175
+ if (isCursor) {
176
+ lines.push(s.primary(prefix + marker + label));
177
+ }
178
+ else if (isMultiSelected || isAnswered) {
179
+ lines.push(s.success(prefix + marker + label));
180
+ }
181
+ else {
182
+ lines.push(s.muted(prefix + marker + label));
183
+ }
184
+ }
185
+ // Custom option
186
+ if (allowCustom) {
187
+ const isCursor = this.state.selectedIndex === customIndex;
188
+ const prefix = isCursor ? ' > ' : ' ';
189
+ const hasCustomAnswer = customAnswerValue !== '' || this.state.inputBuffer !== '';
190
+ const displayValue = this.state.inputBuffer || customAnswerValue;
191
+ if (this.state.isTypingCustom) {
192
+ lines.push(s.primary(prefix + 'Custom: ' + this.state.inputBuffer + '_'));
193
+ }
194
+ else if (hasCustomAnswer) {
195
+ const marker = isMultiSelect ? '[*] ' : '* ';
196
+ const label = `${String(options.length + 1)}. Custom: "${displayValue}"`;
197
+ lines.push(isCursor ? s.primary(prefix + marker + label) : s.success(prefix + marker + label));
198
+ }
199
+ else {
200
+ const label = `${String(options.length + 1)}. Type something custom...`;
201
+ lines.push(isCursor ? s.primary(prefix + label) : s.muted(prefix + label));
202
+ }
203
+ }
204
+ return lines;
205
+ }
206
+ renderSubmitTab(s) {
207
+ const lines = [];
208
+ lines.push('');
209
+ lines.push(chalk.bold(' Ready to submit?'));
210
+ lines.push('');
211
+ const answeredCount = Object.keys(this.state.answers).length;
212
+ const totalCount = this.questions.length;
213
+ const allAnswered = answeredCount === totalCount;
214
+ if (allAnswered) {
215
+ lines.push(s.success(` * All ${String(totalCount)} questions answered`));
216
+ lines.push('');
217
+ lines.push(' ' + s.muted('Press Enter to submit your answers.'));
218
+ }
219
+ else {
220
+ const unansweredCount = totalCount - answeredCount;
221
+ lines.push(s.warning(` ! ${String(unansweredCount)} of ${String(totalCount)} questions not answered`));
222
+ lines.push('');
223
+ lines.push(' ' + s.muted('Unanswered:'));
224
+ for (const q of this.questions) {
225
+ if (!(q.id in this.state.answers)) {
226
+ const label = q.header || q.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
227
+ lines.push(' ' + s.muted(`- ${label}`));
228
+ }
229
+ }
230
+ lines.push('');
231
+ lines.push(' ' + s.muted('Press Left or Tab to go back and answer.'));
232
+ }
233
+ return lines;
234
+ }
235
+ renderInstructions(s) {
236
+ const lines = [];
237
+ lines.push('');
238
+ if (this.state.isOnSubmitTab) {
239
+ lines.push(s.muted(' Enter Submit all | Left/Tab Go back | Esc Cancel'));
240
+ }
241
+ else if (this.state.isTypingCustom) {
242
+ const currentQ = this.questions[this.state.currentQuestion];
243
+ if (currentQ.multiSelect === true) {
244
+ lines.push(s.muted(' Enter Done typing | Up Navigate | Esc Clear custom'));
245
+ }
246
+ else {
247
+ lines.push(s.muted(' Enter Submit | Esc Cancel custom'));
248
+ }
249
+ }
250
+ else {
251
+ const currentQ = this.questions[this.state.currentQuestion];
252
+ if (currentQ.multiSelect === true) {
253
+ lines.push(s.muted(' Up/Down Navigate | Space Toggle | Enter Confirm | Tab Next | Esc Cancel'));
254
+ }
255
+ else {
256
+ lines.push(s.muted(' Up/Down Navigate | Enter Select | Tab Next | Esc Cancel'));
257
+ }
258
+ }
259
+ return lines;
260
+ }
261
+ // ===========================================================================
262
+ // Private: Key Handlers
263
+ // ===========================================================================
264
+ handleSubmitTabKey(key) {
265
+ if (key.name === 'escape') {
266
+ return this.close(this.getResult());
267
+ }
268
+ if (key.name === 'left' || key.name === 'tab') {
269
+ this.prevQuestion();
270
+ return this.rerender();
271
+ }
272
+ if (key.name === 'return') {
273
+ if (this.tryComplete()) {
274
+ return this.close(this.getResult());
275
+ }
276
+ // tryComplete sets warning and navigates to first unanswered
277
+ return this.rerender();
278
+ }
279
+ return this.noAction();
280
+ }
281
+ handleTypingKey(key) {
282
+ const currentQ = this.questions[this.state.currentQuestion];
283
+ const options = currentQ.options ?? [];
284
+ if (key.name === 'escape') {
285
+ this.state.isTypingCustom = false;
286
+ this.state.inputBuffer = '';
287
+ return this.rerender();
288
+ }
289
+ if (key.name === 'up' && options.length > 0) {
290
+ this.state.isTypingCustom = false;
291
+ this.state.inputBuffer = '';
292
+ this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
293
+ return this.rerender();
294
+ }
295
+ if (key.name === 'down') {
296
+ // Custom is last option, down arrow does nothing
297
+ return this.noAction();
298
+ }
299
+ if (key.name === 'return') {
300
+ const isMultiSelect = currentQ.multiSelect === true;
301
+ if (isMultiSelect) {
302
+ // In multi-select: Enter just confirms the custom text and exits typing mode
303
+ // The custom option stays selected, user can continue selecting other options
304
+ if (this.state.inputBuffer.trim()) {
305
+ // Custom text entered - keep it and exit typing mode
306
+ this.state.isTypingCustom = false;
307
+ }
308
+ else {
309
+ // No text - deselect custom option and exit typing mode
310
+ this.state.multiSelections.delete(options.length);
311
+ this.state.isTypingCustom = false;
312
+ }
313
+ return this.rerender();
314
+ }
315
+ else {
316
+ // Single-select: submit answer and move to next question
317
+ if (this.submitAnswer()) {
318
+ this.state.warningMessage = '';
319
+ this.nextQuestion();
320
+ }
321
+ return this.rerender();
322
+ }
323
+ }
324
+ if (key.name === 'backspace') {
325
+ this.state.inputBuffer = this.state.inputBuffer.slice(0, -1);
326
+ return this.rerender();
327
+ }
328
+ // Printable character
329
+ if (key.char && key.char.length === 1 && !key.ctrl && !key.meta) {
330
+ this.state.inputBuffer += key.char;
331
+ return this.rerender();
332
+ }
333
+ return this.noAction();
334
+ }
335
+ handleNavigationKey(key) {
336
+ const optionCount = this.getOptionCount();
337
+ const currentQ = this.questions[this.state.currentQuestion];
338
+ const isMultiSelect = currentQ.multiSelect === true;
339
+ const options = currentQ.options ?? [];
340
+ const allowCustom = currentQ.allowCustom !== false;
341
+ const customIndex = options.length;
342
+ if (key.name === 'escape') {
343
+ return this.close(this.getResult());
344
+ }
345
+ if (key.name === 'up') {
346
+ this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
347
+ if (!isMultiSelect && allowCustom && this.state.selectedIndex === customIndex) {
348
+ this.state.isTypingCustom = true;
349
+ }
350
+ return this.rerender();
351
+ }
352
+ if (key.name === 'down') {
353
+ this.state.selectedIndex = Math.min(optionCount - 1, this.state.selectedIndex + 1);
354
+ if (!isMultiSelect && allowCustom && this.state.selectedIndex === customIndex) {
355
+ this.state.isTypingCustom = true;
356
+ }
357
+ return this.rerender();
358
+ }
359
+ if (key.name === 'left') {
360
+ this.prevQuestion();
361
+ return this.rerender();
362
+ }
363
+ if (key.name === 'right' || key.name === 'tab') {
364
+ this.nextQuestion();
365
+ return this.rerender();
366
+ }
367
+ if (key.name === 'space' && isMultiSelect) {
368
+ const isOnCustom = allowCustom && this.state.selectedIndex === customIndex;
369
+ if (this.state.multiSelections.has(this.state.selectedIndex)) {
370
+ // Deselect
371
+ this.state.multiSelections.delete(this.state.selectedIndex);
372
+ if (isOnCustom) {
373
+ this.state.inputBuffer = '';
374
+ }
375
+ }
376
+ else {
377
+ // Select
378
+ this.state.multiSelections.add(this.state.selectedIndex);
379
+ if (isOnCustom) {
380
+ // Start typing mode for custom input
381
+ this.state.isTypingCustom = true;
382
+ }
383
+ }
384
+ return this.rerender();
385
+ }
386
+ if (key.name === 'return') {
387
+ if (this.submitAnswer()) {
388
+ this.state.warningMessage = '';
389
+ this.nextQuestion();
390
+ }
391
+ return this.rerender();
392
+ }
393
+ // Number keys 1-9 reserved for tab navigation (jump to question N)
394
+ if (key.char && key.char >= '1' && key.char <= '9') {
395
+ const tabIndex = parseInt(key.char, 10) - 1;
396
+ if (tabIndex < this.questions.length) {
397
+ this.state.currentQuestion = tabIndex;
398
+ this.state.isOnSubmitTab = false;
399
+ this.restoreFromAnswer();
400
+ return this.rerender();
401
+ }
402
+ else if (tabIndex === this.questions.length) {
403
+ // Jump to Submit tab
404
+ this.state.isOnSubmitTab = true;
405
+ this.state.isTypingCustom = false;
406
+ this.state.inputBuffer = '';
407
+ return this.rerender();
408
+ }
409
+ }
410
+ return this.noAction();
411
+ }
412
+ // ===========================================================================
413
+ // Private: Helper Methods
414
+ // ===========================================================================
415
+ getResult() {
416
+ const skipped = this.questions.filter((q) => !(q.id in this.state.answers)).map((q) => q.id);
417
+ return {
418
+ answers: this.state.answers,
419
+ skipped,
420
+ };
421
+ }
422
+ getOptionCount() {
423
+ const q = this.questions[this.state.currentQuestion];
424
+ const optionCount = q.options?.length ?? 0;
425
+ const allowCustom = q.allowCustom !== false;
426
+ return allowCustom ? optionCount + 1 : optionCount;
427
+ }
428
+ getAnswerInfo(question) {
429
+ const options = question.options ?? [];
430
+ const isMultiSelect = question.multiSelect === true;
431
+ const answeredOptions = new Set();
432
+ let customAnswerValue = '';
433
+ if (question.id in this.state.answers) {
434
+ const existingAnswer = this.state.answers[question.id];
435
+ if (isMultiSelect && Array.isArray(existingAnswer)) {
436
+ for (const ans of existingAnswer) {
437
+ const idx = options.indexOf(ans);
438
+ if (idx >= 0) {
439
+ answeredOptions.add(idx);
440
+ }
441
+ else {
442
+ customAnswerValue = ans;
443
+ }
444
+ }
445
+ }
446
+ else if (typeof existingAnswer === 'string') {
447
+ const idx = options.indexOf(existingAnswer);
448
+ if (idx >= 0) {
449
+ answeredOptions.add(idx);
450
+ }
451
+ else {
452
+ customAnswerValue = existingAnswer;
453
+ }
454
+ }
455
+ }
456
+ return { answeredOptions, customAnswerValue };
457
+ }
458
+ restoreFromAnswer() {
459
+ const q = this.questions[this.state.currentQuestion];
460
+ const options = q.options ?? [];
461
+ const isMultiSelect = q.multiSelect === true;
462
+ const allowCustom = q.allowCustom !== false;
463
+ // Reset state first
464
+ this.state.selectedIndex = 0;
465
+ this.state.inputBuffer = '';
466
+ this.state.isTypingCustom = false;
467
+ this.state.multiSelections.clear();
468
+ if (!(q.id in this.state.answers)) {
469
+ // No existing answer - check if we should auto-type
470
+ if (options.length === 0 && allowCustom && !isMultiSelect) {
471
+ this.state.isTypingCustom = true;
472
+ }
473
+ return;
474
+ }
475
+ const existingAnswer = this.state.answers[q.id];
476
+ if (isMultiSelect && Array.isArray(existingAnswer)) {
477
+ for (const answer of existingAnswer) {
478
+ const idx = options.indexOf(answer);
479
+ if (idx >= 0) {
480
+ this.state.multiSelections.add(idx);
481
+ }
482
+ else {
483
+ this.state.multiSelections.add(options.length);
484
+ this.state.inputBuffer = answer;
485
+ }
486
+ }
487
+ if (this.state.multiSelections.size > 0) {
488
+ this.state.selectedIndex = Math.min(...this.state.multiSelections);
489
+ }
490
+ }
491
+ else if (typeof existingAnswer === 'string') {
492
+ const idx = options.indexOf(existingAnswer);
493
+ if (idx >= 0) {
494
+ this.state.selectedIndex = idx;
495
+ }
496
+ else {
497
+ this.state.selectedIndex = options.length;
498
+ this.state.inputBuffer = existingAnswer;
499
+ }
500
+ }
501
+ }
502
+ nextQuestion() {
503
+ if (this.state.isOnSubmitTab) {
504
+ return;
505
+ }
506
+ if (this.state.currentQuestion < this.questions.length - 1) {
507
+ this.state.currentQuestion++;
508
+ this.restoreFromAnswer();
509
+ }
510
+ else {
511
+ // On last question - move to Submit tab
512
+ this.state.isOnSubmitTab = true;
513
+ this.state.isTypingCustom = false;
514
+ this.state.inputBuffer = '';
515
+ }
516
+ }
517
+ prevQuestion() {
518
+ if (this.state.isOnSubmitTab) {
519
+ this.state.isOnSubmitTab = false;
520
+ this.restoreFromAnswer();
521
+ return;
522
+ }
523
+ if (this.state.currentQuestion > 0) {
524
+ this.state.currentQuestion--;
525
+ this.restoreFromAnswer();
526
+ }
527
+ }
528
+ tryComplete() {
529
+ const unanswered = this.questions.filter((q) => !(q.id in this.state.answers));
530
+ if (unanswered.length === 0) {
531
+ this.state.warningMessage = '';
532
+ return true;
533
+ }
534
+ // Navigate to first unanswered question and show warning
535
+ const firstUnansweredIndex = this.questions.findIndex((q) => !(q.id in this.state.answers));
536
+ this.state.warningMessage = `${String(unanswered.length)} question${unanswered.length > 1 ? 's' : ''} unanswered. Please answer all questions.`;
537
+ this.state.currentQuestion = firstUnansweredIndex;
538
+ this.state.isOnSubmitTab = false;
539
+ this.restoreFromAnswer();
540
+ return false;
541
+ }
542
+ submitAnswer() {
543
+ const q = this.questions[this.state.currentQuestion];
544
+ const options = q.options ?? [];
545
+ const allowCustom = q.allowCustom !== false;
546
+ const isMultiSelect = q.multiSelect === true;
547
+ const customIndex = options.length;
548
+ if (isMultiSelect) {
549
+ const selectedAnswers = [];
550
+ for (const idx of this.state.multiSelections) {
551
+ if (idx < options.length) {
552
+ selectedAnswers.push(options[idx]);
553
+ }
554
+ }
555
+ if (this.state.multiSelections.has(customIndex) && this.state.inputBuffer.trim()) {
556
+ selectedAnswers.push(this.state.inputBuffer.trim());
557
+ }
558
+ if (selectedAnswers.length > 0) {
559
+ this.state.answers[q.id] = selectedAnswers;
560
+ return true;
561
+ }
562
+ return false;
563
+ }
564
+ else {
565
+ if (this.state.isTypingCustom) {
566
+ const custom = this.state.inputBuffer.trim();
567
+ if (custom) {
568
+ this.state.answers[q.id] = custom;
569
+ return true;
570
+ }
571
+ return false;
572
+ }
573
+ else if (this.state.selectedIndex < options.length) {
574
+ this.state.answers[q.id] = options[this.state.selectedIndex];
575
+ return true;
576
+ }
577
+ else if (allowCustom && this.state.selectedIndex === customIndex) {
578
+ this.state.isTypingCustom = true;
579
+ return false;
580
+ }
581
+ }
582
+ return false;
583
+ }
584
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Ask User Simple Overlay V2
3
+ *
4
+ * Simplified modal overlay for single-question input.
5
+ * Uses BaseOverlayV2 for TerminalUI integration.
6
+ *
7
+ * Features:
8
+ * - Single question (no tabs, no navigation)
9
+ * - Options to select from with arrow keys
10
+ * - Custom text input option
11
+ * - Simple, clean UI
12
+ */
13
+ import { BaseOverlayV2 } from '../../base/index.js';
14
+ import type { RenderContext, OverlayAction, KeyEvent } from '../types.js';
15
+ interface SimpleState {
16
+ /** Selected option index */
17
+ selectedIndex: number;
18
+ /** Custom input buffer for free-text */
19
+ inputBuffer: string;
20
+ /** Whether currently typing custom input */
21
+ isTypingCustom: boolean;
22
+ }
23
+ export interface AskUserSimpleOptionsV2 {
24
+ question: string;
25
+ options?: string[];
26
+ allowCustom?: boolean;
27
+ }
28
+ export interface AskUserSimpleResultV2 {
29
+ answer: string;
30
+ skipped: boolean;
31
+ }
32
+ export declare class AskUserSimpleOverlayV2 extends BaseOverlayV2<SimpleState, AskUserSimpleResultV2> {
33
+ readonly type: "inline";
34
+ readonly id = "ask-user-simple-overlay-v2";
35
+ private readonly question;
36
+ private readonly choices;
37
+ private readonly allowCustom;
38
+ constructor(options: AskUserSimpleOptionsV2);
39
+ protected renderContent(context: RenderContext): string[];
40
+ handleKey(key: KeyEvent): OverlayAction<AskUserSimpleResultV2>;
41
+ /**
42
+ * Summary shown after overlay closes.
43
+ */
44
+ getCloseSummary(result: AskUserSimpleResultV2): string | null;
45
+ }
46
+ export {};