@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,555 @@
1
+ /**
2
+ * Ask User Overlay (Refactored)
3
+ *
4
+ * Modal overlay for presenting multi-question forms to the user.
5
+ * Uses InlineOverlay base class for consistent lifecycle management.
6
+ *
7
+ * Features:
8
+ * - Multiple questions with navigation (←/→ 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
+ */
14
+ import chalk from 'chalk';
15
+ import { getStyles } from '../themes/index.js';
16
+ import { InlineOverlay } from './base/inline-overlay.js';
17
+ // =============================================================================
18
+ // Overlay Implementation
19
+ // =============================================================================
20
+ class AskUserOverlayImpl extends InlineOverlay {
21
+ questions;
22
+ context;
23
+ constructor(options) {
24
+ super();
25
+ this.questions = options.questions;
26
+ this.context = options.context;
27
+ }
28
+ getInitialState() {
29
+ // Check if first question should auto-start in typing mode
30
+ const firstQ = this.questions[0];
31
+ const firstQAutoType = (firstQ.options?.length ?? 0) === 0 &&
32
+ firstQ.allowCustom !== false &&
33
+ firstQ.multiSelect !== true;
34
+ return {
35
+ currentQuestion: 0,
36
+ selectedIndex: 0,
37
+ inputBuffer: '',
38
+ isTypingCustom: firstQAutoType,
39
+ answers: {},
40
+ multiSelections: new Set(),
41
+ warningMessage: '',
42
+ isOnSubmitTab: false,
43
+ };
44
+ }
45
+ render() {
46
+ const s = getStyles();
47
+ const lines = [];
48
+ const cols = this.getTerminalWidth();
49
+ const border = s.muted('─'.repeat(Math.max(1, cols - 1)));
50
+ // Header
51
+ lines.push(border);
52
+ lines.push(' ' + s.primaryBold('Questions'));
53
+ lines.push('');
54
+ // Tab bar
55
+ lines.push(...this.renderTabBar());
56
+ lines.push('');
57
+ // Current question or Submit tab
58
+ if (this.state.isOnSubmitTab) {
59
+ lines.push(...this.renderSubmitTab());
60
+ }
61
+ else {
62
+ lines.push(...this.renderQuestion());
63
+ }
64
+ // Footer with instructions
65
+ lines.push(...this.renderFooter());
66
+ // Warning message if present
67
+ if (this.state.warningMessage) {
68
+ lines.push('');
69
+ lines.push(s.warning(` ⚠ ${this.state.warningMessage}`));
70
+ }
71
+ // Bottom border
72
+ lines.push(border);
73
+ return lines;
74
+ }
75
+ handleKey(data) {
76
+ // Ctrl+C always cancels
77
+ if (this.isCtrlCKey(data)) {
78
+ return { type: 'close', result: this.getResult() };
79
+ }
80
+ // Handle Submit tab first
81
+ if (this.state.isOnSubmitTab) {
82
+ return this.handleSubmitTabKey(data);
83
+ }
84
+ // Handle typing mode
85
+ if (this.state.isTypingCustom) {
86
+ return this.handleTypingKey(data);
87
+ }
88
+ // Handle navigation mode
89
+ return this.handleNavigationKey(data);
90
+ }
91
+ getCleanupSummary(result) {
92
+ const s = getStyles();
93
+ const answeredCount = Object.keys(result.answers).length;
94
+ const skippedCount = result.skipped.length;
95
+ if (answeredCount === 0) {
96
+ return s.muted('Questions: ') + s.warning('Cancelled');
97
+ }
98
+ if (skippedCount > 0) {
99
+ return s.muted('Questions: ') + s.success(`${String(answeredCount)} answered`) + s.muted(`, ${String(skippedCount)} skipped`);
100
+ }
101
+ return s.muted('Questions: ') + s.success(`${String(answeredCount)} answered`);
102
+ }
103
+ // ===========================================================================
104
+ // Private: Rendering Helpers
105
+ // ===========================================================================
106
+ renderTabBar() {
107
+ const s = getStyles();
108
+ const lines = [];
109
+ let tabLine = ' ';
110
+ for (let i = 0; i < this.questions.length; i++) {
111
+ const q = this.questions[i];
112
+ const hasAnswer = q.id in this.state.answers;
113
+ const isCurrent = i === this.state.currentQuestion && !this.state.isOnSubmitTab;
114
+ const label = q.header || q.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
115
+ if (isCurrent) {
116
+ tabLine += s.selected(` ${label} `) + ' ';
117
+ }
118
+ else if (hasAnswer) {
119
+ tabLine += s.success(`✓ ${label}`) + ' ';
120
+ }
121
+ else {
122
+ tabLine += s.muted(` ${label} `) + ' ';
123
+ }
124
+ }
125
+ // Add Submit tab
126
+ const allAnswered = this.questions.every((q) => q.id in this.state.answers);
127
+ if (this.state.isOnSubmitTab) {
128
+ tabLine += s.selected(' Submit ');
129
+ }
130
+ else if (allAnswered) {
131
+ tabLine += s.success(' Submit ');
132
+ }
133
+ else {
134
+ tabLine += s.muted(' Submit ');
135
+ }
136
+ lines.push(tabLine);
137
+ return lines;
138
+ }
139
+ renderQuestion() {
140
+ const s = getStyles();
141
+ const lines = [];
142
+ const question = this.questions[this.state.currentQuestion];
143
+ const questionIndex = this.state.currentQuestion;
144
+ const totalQuestions = this.questions.length;
145
+ // Context if provided (only on first question)
146
+ if (this.context && questionIndex === 0) {
147
+ lines.push(s.muted(' ' + this.context));
148
+ lines.push('');
149
+ }
150
+ // Question number and text
151
+ lines.push(chalk.bold(` [${String(questionIndex + 1)}/${String(totalQuestions)}] ${question.question}`));
152
+ lines.push('');
153
+ // Options
154
+ const options = question.options ?? [];
155
+ const allowCustom = question.allowCustom !== false;
156
+ const isMultiSelect = question.multiSelect === true;
157
+ const customIndex = options.length;
158
+ // Get existing answer info
159
+ const { answeredOptions, customAnswerValue } = this.getAnswerInfo(question);
160
+ for (let i = 0; i < options.length; i++) {
161
+ const isCursor = this.state.selectedIndex === i;
162
+ const isMultiSelected = isMultiSelect && this.state.multiSelections.has(i);
163
+ const isAnswered = answeredOptions.has(i);
164
+ const prefix = isCursor ? ' ❯ ' : ' ';
165
+ let marker = '';
166
+ if (isMultiSelect) {
167
+ marker = isMultiSelected ? '[✓] ' : '[ ] ';
168
+ }
169
+ else if (isAnswered) {
170
+ marker = '● ';
171
+ }
172
+ const label = `${String(i + 1)}. ${options[i]}`;
173
+ if (isCursor) {
174
+ lines.push(s.primary(prefix + marker + label));
175
+ }
176
+ else if (isMultiSelected || isAnswered) {
177
+ lines.push(s.success(prefix + marker + label));
178
+ }
179
+ else {
180
+ lines.push(s.muted(prefix + marker + label));
181
+ }
182
+ }
183
+ // Custom option
184
+ if (allowCustom) {
185
+ const isCursor = this.state.selectedIndex === customIndex;
186
+ const prefix = isCursor ? ' ❯ ' : ' ';
187
+ const hasCustomAnswer = customAnswerValue !== '' || this.state.inputBuffer !== '';
188
+ const displayValue = this.state.inputBuffer || customAnswerValue;
189
+ if (this.state.isTypingCustom) {
190
+ lines.push(s.primary(prefix + 'Custom: ' + this.state.inputBuffer + '▋'));
191
+ }
192
+ else if (hasCustomAnswer) {
193
+ const marker = isMultiSelect ? '[✓] ' : '● ';
194
+ const label = `${String(options.length + 1)}. Custom: "${displayValue}"`;
195
+ lines.push(isCursor ? s.primary(prefix + marker + label) : s.success(prefix + marker + label));
196
+ }
197
+ else {
198
+ const label = `${String(options.length + 1)}. Type something custom...`;
199
+ lines.push(isCursor ? s.primary(prefix + label) : s.muted(prefix + label));
200
+ }
201
+ }
202
+ return lines;
203
+ }
204
+ renderSubmitTab() {
205
+ const s = getStyles();
206
+ const lines = [];
207
+ lines.push('');
208
+ lines.push(chalk.bold(' Ready to submit?'));
209
+ lines.push('');
210
+ const answeredCount = Object.keys(this.state.answers).length;
211
+ const totalCount = this.questions.length;
212
+ const allAnswered = answeredCount === totalCount;
213
+ if (allAnswered) {
214
+ lines.push(s.success(` ✓ All ${String(totalCount)} questions answered`));
215
+ lines.push('');
216
+ lines.push(' ' + s.muted('Press Enter to submit your answers.'));
217
+ }
218
+ else {
219
+ const unansweredCount = totalCount - answeredCount;
220
+ lines.push(s.warning(` ⚠ ${String(unansweredCount)} of ${String(totalCount)} questions not answered`));
221
+ lines.push('');
222
+ lines.push(' ' + s.muted('Unanswered:'));
223
+ for (const q of this.questions) {
224
+ if (!(q.id in this.state.answers)) {
225
+ const label = q.header || q.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
226
+ lines.push(' ' + s.muted(`• ${label}`));
227
+ }
228
+ }
229
+ lines.push('');
230
+ lines.push(' ' + s.muted('Press ← or Tab to go back and answer.'));
231
+ }
232
+ return lines;
233
+ }
234
+ renderFooter() {
235
+ const s = getStyles();
236
+ const lines = [];
237
+ lines.push('');
238
+ if (this.state.isOnSubmitTab) {
239
+ lines.push(s.muted(' Enter Submit all · ←/Tab Go back · Esc Cancel'));
240
+ }
241
+ else if (this.state.isTypingCustom) {
242
+ lines.push(s.muted(' Enter Submit · Esc Cancel custom'));
243
+ }
244
+ else {
245
+ const currentQ = this.questions[this.state.currentQuestion];
246
+ if (currentQ.multiSelect === true) {
247
+ lines.push(s.muted(' ↑↓ Navigate · Space Toggle · Enter Confirm · Tab Next · Esc Cancel'));
248
+ }
249
+ else {
250
+ lines.push(s.muted(' ↑↓ Navigate · Enter Select · Tab Next · Esc Cancel'));
251
+ }
252
+ }
253
+ return lines;
254
+ }
255
+ // ===========================================================================
256
+ // Private: Key Handlers
257
+ // ===========================================================================
258
+ handleSubmitTabKey(data) {
259
+ if (this.isEscapeKey(data)) {
260
+ return { type: 'close', result: this.getResult() };
261
+ }
262
+ if (this.isLeftArrowKey(data) || this.isTabKey(data)) {
263
+ this.prevQuestion();
264
+ return { type: 'continue' };
265
+ }
266
+ if (this.isEnterKey(data)) {
267
+ if (this.tryComplete()) {
268
+ return { type: 'close', result: this.getResult() };
269
+ }
270
+ // tryComplete sets warning and navigates to first unanswered
271
+ }
272
+ return { type: 'continue' };
273
+ }
274
+ handleTypingKey(data) {
275
+ const currentQ = this.questions[this.state.currentQuestion];
276
+ const options = currentQ.options ?? [];
277
+ if (this.isEscapeKey(data)) {
278
+ this.state.isTypingCustom = false;
279
+ this.state.inputBuffer = '';
280
+ return { type: 'continue' };
281
+ }
282
+ if (this.isUpArrowKey(data) && options.length > 0) {
283
+ this.state.isTypingCustom = false;
284
+ this.state.inputBuffer = '';
285
+ this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
286
+ return { type: 'continue' };
287
+ }
288
+ if (this.isDownArrowKey(data)) {
289
+ // Custom is last option, down arrow does nothing
290
+ return { type: 'continue' };
291
+ }
292
+ if (this.isEnterKey(data)) {
293
+ if (this.submitAnswer()) {
294
+ this.state.warningMessage = '';
295
+ this.nextQuestion();
296
+ }
297
+ return { type: 'continue' };
298
+ }
299
+ if (this.isBackspaceKey(data)) {
300
+ this.state.inputBuffer = this.state.inputBuffer.slice(0, -1);
301
+ return { type: 'continue' };
302
+ }
303
+ // Printable character
304
+ const char = this.getPrintableChar(data);
305
+ if (char) {
306
+ this.state.inputBuffer += char;
307
+ }
308
+ return { type: 'continue' };
309
+ }
310
+ handleNavigationKey(data) {
311
+ const optionCount = this.getOptionCount();
312
+ const currentQ = this.questions[this.state.currentQuestion];
313
+ const isMultiSelect = currentQ.multiSelect === true;
314
+ const options = currentQ.options ?? [];
315
+ const allowCustom = currentQ.allowCustom !== false;
316
+ const customIndex = options.length;
317
+ if (this.isEscapeKey(data)) {
318
+ return { type: 'close', result: this.getResult() };
319
+ }
320
+ if (this.isUpArrowKey(data)) {
321
+ this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
322
+ if (!isMultiSelect && allowCustom && this.state.selectedIndex === customIndex) {
323
+ this.state.isTypingCustom = true;
324
+ }
325
+ return { type: 'continue' };
326
+ }
327
+ if (this.isDownArrowKey(data)) {
328
+ this.state.selectedIndex = Math.min(optionCount - 1, this.state.selectedIndex + 1);
329
+ if (!isMultiSelect && allowCustom && this.state.selectedIndex === customIndex) {
330
+ this.state.isTypingCustom = true;
331
+ }
332
+ return { type: 'continue' };
333
+ }
334
+ if (this.isLeftArrowKey(data)) {
335
+ this.prevQuestion();
336
+ return { type: 'continue' };
337
+ }
338
+ if (this.isRightArrowKey(data) || this.isTabKey(data)) {
339
+ this.nextQuestion();
340
+ return { type: 'continue' };
341
+ }
342
+ if (this.isSpaceKey(data) && isMultiSelect) {
343
+ if (this.state.multiSelections.has(this.state.selectedIndex)) {
344
+ this.state.multiSelections.delete(this.state.selectedIndex);
345
+ }
346
+ else {
347
+ this.state.multiSelections.add(this.state.selectedIndex);
348
+ }
349
+ return { type: 'continue' };
350
+ }
351
+ if (this.isEnterKey(data)) {
352
+ if (this.submitAnswer()) {
353
+ this.state.warningMessage = '';
354
+ this.nextQuestion();
355
+ }
356
+ return { type: 'continue' };
357
+ }
358
+ // Number keys 1-9
359
+ if (data.length === 1 && data[0] >= 0x31 && data[0] <= 0x39) {
360
+ const numIndex = data[0] - 0x31;
361
+ if (numIndex < optionCount) {
362
+ this.state.selectedIndex = numIndex;
363
+ if (!isMultiSelect) {
364
+ if (this.submitAnswer()) {
365
+ this.state.warningMessage = '';
366
+ this.nextQuestion();
367
+ }
368
+ }
369
+ }
370
+ }
371
+ return { type: 'continue' };
372
+ }
373
+ // ===========================================================================
374
+ // Private: Helper Methods
375
+ // ===========================================================================
376
+ getResult() {
377
+ const skipped = this.questions.filter((q) => !(q.id in this.state.answers)).map((q) => q.id);
378
+ return {
379
+ answers: this.state.answers,
380
+ skipped,
381
+ };
382
+ }
383
+ getOptionCount() {
384
+ const q = this.questions[this.state.currentQuestion];
385
+ const optionCount = q.options?.length ?? 0;
386
+ const allowCustom = q.allowCustom !== false;
387
+ return allowCustom ? optionCount + 1 : optionCount;
388
+ }
389
+ getAnswerInfo(question) {
390
+ const options = question.options ?? [];
391
+ const isMultiSelect = question.multiSelect === true;
392
+ const answeredOptions = new Set();
393
+ let customAnswerValue = '';
394
+ if (question.id in this.state.answers) {
395
+ const existingAnswer = this.state.answers[question.id];
396
+ if (isMultiSelect && Array.isArray(existingAnswer)) {
397
+ for (const ans of existingAnswer) {
398
+ const idx = options.indexOf(ans);
399
+ if (idx >= 0) {
400
+ answeredOptions.add(idx);
401
+ }
402
+ else {
403
+ customAnswerValue = ans;
404
+ }
405
+ }
406
+ }
407
+ else if (typeof existingAnswer === 'string') {
408
+ const idx = options.indexOf(existingAnswer);
409
+ if (idx >= 0) {
410
+ answeredOptions.add(idx);
411
+ }
412
+ else {
413
+ customAnswerValue = existingAnswer;
414
+ }
415
+ }
416
+ }
417
+ return { answeredOptions, customAnswerValue };
418
+ }
419
+ restoreFromAnswer() {
420
+ const q = this.questions[this.state.currentQuestion];
421
+ const options = q.options ?? [];
422
+ const isMultiSelect = q.multiSelect === true;
423
+ const allowCustom = q.allowCustom !== false;
424
+ // Reset state first
425
+ this.state.selectedIndex = 0;
426
+ this.state.inputBuffer = '';
427
+ this.state.isTypingCustom = false;
428
+ this.state.multiSelections.clear();
429
+ if (!(q.id in this.state.answers)) {
430
+ // No existing answer - check if we should auto-type
431
+ if (options.length === 0 && allowCustom && !isMultiSelect) {
432
+ this.state.isTypingCustom = true;
433
+ }
434
+ return;
435
+ }
436
+ const existingAnswer = this.state.answers[q.id];
437
+ if (isMultiSelect && Array.isArray(existingAnswer)) {
438
+ for (const answer of existingAnswer) {
439
+ const idx = options.indexOf(answer);
440
+ if (idx >= 0) {
441
+ this.state.multiSelections.add(idx);
442
+ }
443
+ else {
444
+ this.state.multiSelections.add(options.length);
445
+ this.state.inputBuffer = answer;
446
+ }
447
+ }
448
+ if (this.state.multiSelections.size > 0) {
449
+ this.state.selectedIndex = Math.min(...this.state.multiSelections);
450
+ }
451
+ }
452
+ else if (typeof existingAnswer === 'string') {
453
+ const idx = options.indexOf(existingAnswer);
454
+ if (idx >= 0) {
455
+ this.state.selectedIndex = idx;
456
+ }
457
+ else {
458
+ this.state.selectedIndex = options.length;
459
+ this.state.inputBuffer = existingAnswer;
460
+ }
461
+ }
462
+ }
463
+ nextQuestion() {
464
+ if (this.state.isOnSubmitTab) {
465
+ return;
466
+ }
467
+ if (this.state.currentQuestion < this.questions.length - 1) {
468
+ this.state.currentQuestion++;
469
+ this.restoreFromAnswer();
470
+ }
471
+ else {
472
+ // On last question - move to Submit tab
473
+ this.state.isOnSubmitTab = true;
474
+ this.state.isTypingCustom = false;
475
+ this.state.inputBuffer = '';
476
+ }
477
+ }
478
+ prevQuestion() {
479
+ if (this.state.isOnSubmitTab) {
480
+ this.state.isOnSubmitTab = false;
481
+ this.restoreFromAnswer();
482
+ return;
483
+ }
484
+ if (this.state.currentQuestion > 0) {
485
+ this.state.currentQuestion--;
486
+ this.restoreFromAnswer();
487
+ }
488
+ }
489
+ tryComplete() {
490
+ const unanswered = this.questions.filter((q) => !(q.id in this.state.answers));
491
+ if (unanswered.length === 0) {
492
+ this.state.warningMessage = '';
493
+ return true;
494
+ }
495
+ // Navigate to first unanswered question and show warning
496
+ const firstUnansweredIndex = this.questions.findIndex((q) => !(q.id in this.state.answers));
497
+ this.state.warningMessage = `${String(unanswered.length)} question${unanswered.length > 1 ? 's' : ''} unanswered. Please answer all questions.`;
498
+ this.state.currentQuestion = firstUnansweredIndex;
499
+ this.state.isOnSubmitTab = false;
500
+ this.restoreFromAnswer();
501
+ return false;
502
+ }
503
+ submitAnswer() {
504
+ const q = this.questions[this.state.currentQuestion];
505
+ const options = q.options ?? [];
506
+ const allowCustom = q.allowCustom !== false;
507
+ const isMultiSelect = q.multiSelect === true;
508
+ const customIndex = options.length;
509
+ if (isMultiSelect) {
510
+ const selectedAnswers = [];
511
+ for (const idx of this.state.multiSelections) {
512
+ if (idx < options.length) {
513
+ selectedAnswers.push(options[idx]);
514
+ }
515
+ }
516
+ if (this.state.multiSelections.has(customIndex) && this.state.inputBuffer.trim()) {
517
+ selectedAnswers.push(this.state.inputBuffer.trim());
518
+ }
519
+ if (selectedAnswers.length > 0) {
520
+ this.state.answers[q.id] = selectedAnswers;
521
+ return true;
522
+ }
523
+ return false;
524
+ }
525
+ else {
526
+ if (this.state.isTypingCustom) {
527
+ const custom = this.state.inputBuffer.trim();
528
+ if (custom) {
529
+ this.state.answers[q.id] = custom;
530
+ return true;
531
+ }
532
+ return false;
533
+ }
534
+ else if (this.state.selectedIndex < options.length) {
535
+ this.state.answers[q.id] = options[this.state.selectedIndex];
536
+ return true;
537
+ }
538
+ else if (allowCustom && this.state.selectedIndex === customIndex) {
539
+ this.state.isTypingCustom = true;
540
+ return false;
541
+ }
542
+ }
543
+ return false;
544
+ }
545
+ }
546
+ // =============================================================================
547
+ // Main Export
548
+ // =============================================================================
549
+ /**
550
+ * Show the ask user overlay
551
+ */
552
+ export async function showAskUserOverlay(options) {
553
+ const overlay = new AskUserOverlayImpl(options);
554
+ return overlay.show();
555
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Ask User Overlay
2
+ * Ask User Overlay (Refactored)
3
3
  *
4
4
  * Modal overlay for presenting multi-question forms to the user.
5
- * Used by the ask_user tool during /design and /refine workflows.
5
+ * Uses InlineOverlay base class for consistent lifecycle management.
6
6
  *
7
7
  * Features:
8
8
  * - Multiple questions with navigation (←/→ or Tab)