@compilr-dev/cli 0.4.0 → 0.5.1

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 (634) hide show
  1. package/README.md +254 -68
  2. package/dist/.tsbuildinfo.app +1 -0
  3. package/dist/.tsbuildinfo.data +1 -0
  4. package/dist/.tsbuildinfo.domain +1 -0
  5. package/dist/.tsbuildinfo.foundation +1 -0
  6. package/dist/agent.d.ts +134 -4
  7. package/dist/agent.js +345 -166
  8. package/dist/anchors/index.d.ts +9 -0
  9. package/dist/anchors/index.js +9 -0
  10. package/dist/anchors/project-anchors.d.ts +79 -0
  11. package/dist/anchors/project-anchors.js +202 -0
  12. package/dist/auth/api-client.d.ts +124 -0
  13. package/dist/auth/api-client.js +261 -0
  14. package/dist/auth/index.d.ts +172 -0
  15. package/dist/auth/index.js +545 -0
  16. package/dist/auth/storage.d.ts +52 -0
  17. package/dist/auth/storage.js +118 -0
  18. package/dist/changelog/index.d.ts +16 -0
  19. package/dist/changelog/index.js +24 -0
  20. package/dist/changelog/releases.d.ts +17 -0
  21. package/dist/changelog/releases.js +63 -0
  22. package/dist/commands/index.d.ts +2 -1
  23. package/dist/commands-v2/handlers/auth.d.ts +10 -0
  24. package/dist/commands-v2/handlers/auth.js +118 -0
  25. package/dist/commands-v2/handlers/background.d.ts +14 -0
  26. package/dist/commands-v2/handlers/background.js +276 -0
  27. package/dist/commands-v2/handlers/context.d.ts +13 -0
  28. package/dist/commands-v2/handlers/context.js +533 -0
  29. package/dist/commands-v2/handlers/core.d.ts +14 -0
  30. package/dist/commands-v2/handlers/core.js +290 -0
  31. package/dist/commands-v2/handlers/debug.d.ts +11 -0
  32. package/dist/commands-v2/handlers/debug.js +177 -0
  33. package/dist/commands-v2/handlers/delegations.d.ts +8 -0
  34. package/dist/commands-v2/handlers/delegations.js +29 -0
  35. package/dist/commands-v2/handlers/files.d.ts +8 -0
  36. package/dist/commands-v2/handlers/files.js +162 -0
  37. package/dist/commands-v2/handlers/filter.d.ts +9 -0
  38. package/dist/commands-v2/handlers/filter.js +130 -0
  39. package/dist/commands-v2/handlers/games.d.ts +7 -0
  40. package/dist/commands-v2/handlers/games.js +57 -0
  41. package/dist/commands-v2/handlers/index.d.ts +25 -0
  42. package/dist/commands-v2/handlers/index.js +63 -0
  43. package/dist/commands-v2/handlers/mcp.d.ts +8 -0
  44. package/dist/commands-v2/handlers/mcp.js +39 -0
  45. package/dist/commands-v2/handlers/notifications.d.ts +9 -0
  46. package/dist/commands-v2/handlers/notifications.js +34 -0
  47. package/dist/commands-v2/handlers/project.d.ts +22 -0
  48. package/dist/commands-v2/handlers/project.js +1035 -0
  49. package/dist/commands-v2/handlers/reset.d.ts +11 -0
  50. package/dist/commands-v2/handlers/reset.js +118 -0
  51. package/dist/commands-v2/handlers/session.d.ts +161 -0
  52. package/dist/commands-v2/handlers/session.js +805 -0
  53. package/dist/commands-v2/handlers/settings.d.ts +17 -0
  54. package/dist/commands-v2/handlers/settings.js +417 -0
  55. package/dist/commands-v2/handlers/tasks.d.ts +5 -0
  56. package/dist/commands-v2/handlers/tasks.js +36 -0
  57. package/dist/commands-v2/handlers/team.d.ts +9 -0
  58. package/dist/commands-v2/handlers/team.js +549 -0
  59. package/dist/commands-v2/handlers/terminals.d.ts +9 -0
  60. package/dist/commands-v2/handlers/terminals.js +34 -0
  61. package/dist/commands-v2/index.d.ts +14 -0
  62. package/dist/commands-v2/index.js +18 -0
  63. package/dist/commands-v2/registry.d.ts +52 -0
  64. package/dist/commands-v2/registry.js +114 -0
  65. package/dist/commands-v2/types.d.ts +153 -0
  66. package/dist/commands-v2/types.js +7 -0
  67. package/dist/commands.js +123 -7
  68. package/dist/compilr-diff-companion.vsix +0 -0
  69. package/dist/db/index.js +98 -4
  70. package/dist/db/repositories/document-repository.d.ts +2 -0
  71. package/dist/db/repositories/document-repository.js +5 -1
  72. package/dist/db/repositories/index.d.ts +2 -0
  73. package/dist/db/repositories/index.js +1 -0
  74. package/dist/db/repositories/plan-repository.d.ts +101 -0
  75. package/dist/db/repositories/plan-repository.js +275 -0
  76. package/dist/db/repositories/project-repository.d.ts +6 -0
  77. package/dist/db/repositories/project-repository.js +41 -0
  78. package/dist/db/repositories/work-item-repository.d.ts +15 -0
  79. package/dist/db/repositories/work-item-repository.js +69 -4
  80. package/dist/db/schema.d.ts +40 -3
  81. package/dist/db/schema.js +66 -3
  82. package/dist/episodes/index.d.ts +20 -0
  83. package/dist/episodes/index.js +27 -0
  84. package/dist/episodes/recorder.d.ts +51 -0
  85. package/dist/episodes/recorder.js +195 -0
  86. package/dist/episodes/significant-work.d.ts +21 -0
  87. package/dist/episodes/significant-work.js +56 -0
  88. package/dist/episodes/store.d.ts +38 -0
  89. package/dist/episodes/store.js +199 -0
  90. package/dist/episodes/types.d.ts +35 -0
  91. package/dist/episodes/types.js +6 -0
  92. package/dist/episodes/work-at-risk.d.ts +12 -0
  93. package/dist/episodes/work-at-risk.js +38 -0
  94. package/dist/episodes/work-summary-anchor.d.ts +23 -0
  95. package/dist/episodes/work-summary-anchor.js +73 -0
  96. package/dist/games/coins.d.ts +66 -0
  97. package/dist/games/coins.js +165 -0
  98. package/dist/games/game-base.d.ts +84 -0
  99. package/dist/games/game-base.js +204 -0
  100. package/dist/games/index.d.ts +16 -0
  101. package/dist/games/index.js +49 -0
  102. package/dist/games/scores.d.ts +69 -0
  103. package/dist/games/scores.js +191 -0
  104. package/dist/games/tetris/board.d.ts +59 -0
  105. package/dist/games/tetris/board.js +170 -0
  106. package/dist/games/tetris/index.d.ts +109 -0
  107. package/dist/games/tetris/index.js +610 -0
  108. package/dist/games/tetris/pieces.d.ts +44 -0
  109. package/dist/games/tetris/pieces.js +271 -0
  110. package/dist/games/tetris/renderer.d.ts +26 -0
  111. package/dist/games/tetris/renderer.js +77 -0
  112. package/dist/guide/guide-content.d.ts +23 -0
  113. package/dist/guide/guide-content.js +196 -0
  114. package/dist/guide/index.d.ts +8 -0
  115. package/dist/guide/index.js +7 -0
  116. package/dist/guide/shared-content.d.ts +37 -0
  117. package/dist/guide/shared-content.js +1272 -0
  118. package/dist/guide/tutorial-helpers.d.ts +57 -0
  119. package/dist/guide/tutorial-helpers.js +147 -0
  120. package/dist/handlers/ask-user-handlers.d.ts +32 -0
  121. package/dist/handlers/ask-user-handlers.js +104 -0
  122. package/dist/handlers/delegation-handlers.d.ts +34 -0
  123. package/dist/handlers/delegation-handlers.js +291 -0
  124. package/dist/handlers/permission-handler.d.ts +30 -0
  125. package/dist/handlers/permission-handler.js +205 -0
  126. package/dist/index.d.ts +11 -1
  127. package/dist/index.js +615 -179
  128. package/dist/input-handlers/index.d.ts +7 -0
  129. package/dist/input-handlers/index.js +7 -0
  130. package/dist/input-handlers/memory-handler.d.ts +26 -0
  131. package/dist/input-handlers/memory-handler.js +69 -0
  132. package/dist/models/index.d.ts +10 -0
  133. package/dist/models/index.js +12 -0
  134. package/dist/models/model-registry.d.ts +38 -0
  135. package/dist/models/model-registry.js +69 -0
  136. package/dist/models/model-tiers.d.ts +28 -0
  137. package/dist/models/model-tiers.js +71 -0
  138. package/dist/models/model-validation.d.ts +25 -0
  139. package/dist/models/model-validation.js +291 -0
  140. package/dist/models/ollama-models.d.ts +73 -0
  141. package/dist/models/ollama-models.js +178 -0
  142. package/dist/models/provider-types.d.ts +6 -0
  143. package/dist/models/provider-types.js +1 -0
  144. package/dist/models/providers.d.ts +35 -0
  145. package/dist/models/providers.js +58 -0
  146. package/dist/models/types.d.ts +4 -0
  147. package/dist/models/types.js +4 -0
  148. package/dist/multi-agent/activity.d.ts +21 -0
  149. package/dist/multi-agent/activity.js +34 -0
  150. package/dist/multi-agent/agent-selection.d.ts +55 -0
  151. package/dist/multi-agent/agent-selection.js +90 -0
  152. package/dist/multi-agent/artifacts.d.ts +197 -0
  153. package/dist/multi-agent/artifacts.js +379 -0
  154. package/dist/multi-agent/checkpointer.d.ts +138 -0
  155. package/dist/multi-agent/checkpointer.js +471 -0
  156. package/dist/multi-agent/collision-utils.d.ts +16 -0
  157. package/dist/multi-agent/collision-utils.js +28 -0
  158. package/dist/multi-agent/context-resolver.d.ts +97 -0
  159. package/dist/multi-agent/context-resolver.js +316 -0
  160. package/dist/multi-agent/custom-agents.d.ts +83 -0
  161. package/dist/multi-agent/custom-agents.js +227 -0
  162. package/dist/multi-agent/delegation-tracker.d.ts +157 -0
  163. package/dist/multi-agent/delegation-tracker.js +243 -0
  164. package/dist/multi-agent/file-lock-hook.d.ts +29 -0
  165. package/dist/multi-agent/file-lock-hook.js +97 -0
  166. package/dist/multi-agent/file-locks.d.ts +58 -0
  167. package/dist/multi-agent/file-locks.js +194 -0
  168. package/dist/multi-agent/index.d.ts +24 -0
  169. package/dist/multi-agent/index.js +30 -0
  170. package/dist/multi-agent/mention-parser.d.ts +64 -0
  171. package/dist/multi-agent/mention-parser.js +146 -0
  172. package/dist/multi-agent/notification-manager.d.ts +84 -0
  173. package/dist/multi-agent/notification-manager.js +224 -0
  174. package/dist/multi-agent/pending-requests.d.ts +122 -0
  175. package/dist/multi-agent/pending-requests.js +155 -0
  176. package/dist/multi-agent/session-registry.d.ts +139 -0
  177. package/dist/multi-agent/session-registry.js +514 -0
  178. package/dist/multi-agent/shared-context.d.ts +293 -0
  179. package/dist/multi-agent/shared-context.js +671 -0
  180. package/dist/multi-agent/skill-requirements.d.ts +66 -0
  181. package/dist/multi-agent/skill-requirements.js +178 -0
  182. package/dist/multi-agent/task-assignment.d.ts +69 -0
  183. package/dist/multi-agent/task-assignment.js +123 -0
  184. package/dist/multi-agent/task-suggestion.d.ts +31 -0
  185. package/dist/multi-agent/task-suggestion.js +72 -0
  186. package/dist/multi-agent/team-agent.d.ts +201 -0
  187. package/dist/multi-agent/team-agent.js +488 -0
  188. package/dist/multi-agent/team.d.ts +286 -0
  189. package/dist/multi-agent/team.js +610 -0
  190. package/dist/multi-agent/tool-config.d.ts +110 -0
  191. package/dist/multi-agent/tool-config.js +661 -0
  192. package/dist/multi-agent/types.d.ts +211 -0
  193. package/dist/multi-agent/types.js +617 -0
  194. package/dist/prompts/plan-mode-prompt.d.ts +11 -0
  195. package/dist/prompts/plan-mode-prompt.js +95 -0
  196. package/dist/repl-helpers.d.ts +63 -0
  197. package/dist/repl-helpers.js +319 -0
  198. package/dist/repl-v2.d.ts +554 -0
  199. package/dist/repl-v2.js +3286 -0
  200. package/dist/session/index.d.ts +6 -0
  201. package/dist/session/index.js +6 -0
  202. package/dist/session/project-session-manager.d.ts +158 -0
  203. package/dist/session/project-session-manager.js +650 -0
  204. package/dist/settings/index.d.ts +156 -13
  205. package/dist/settings/index.js +377 -24
  206. package/dist/settings/mcp-config.d.ts +76 -0
  207. package/dist/settings/mcp-config.js +143 -0
  208. package/dist/settings/paths.d.ts +114 -0
  209. package/dist/settings/paths.js +270 -0
  210. package/dist/shared-handlers.d.ts +62 -0
  211. package/dist/shared-handlers.js +48 -0
  212. package/dist/system-prompt/builder.d.ts +5 -0
  213. package/dist/system-prompt/builder.js +4 -0
  214. package/dist/system-prompt/index.d.ts +18 -0
  215. package/dist/system-prompt/index.js +18 -0
  216. package/dist/system-prompt/modules.d.ts +5 -0
  217. package/dist/system-prompt/modules.js +4 -0
  218. package/dist/tabbed-menu.js +2 -1
  219. package/dist/templates/compilr-md-import.d.ts +16 -0
  220. package/dist/templates/compilr-md-import.js +241 -0
  221. package/dist/templates/compilr-md.js +10 -58
  222. package/dist/templates/config-json.d.ts +1 -25
  223. package/dist/templates/index.d.ts +2 -0
  224. package/dist/templates/index.js +35 -75
  225. package/dist/themes/colors.js +3 -1
  226. package/dist/themes/registry.d.ts +5 -36
  227. package/dist/themes/registry.js +11 -95
  228. package/dist/themes/types.d.ts +3 -38
  229. package/dist/themes/types.js +2 -2
  230. package/dist/tool-names.d.ts +108 -0
  231. package/dist/tool-names.js +227 -0
  232. package/dist/tools/anchor-tools.d.ts +31 -0
  233. package/dist/tools/anchor-tools.js +255 -0
  234. package/dist/tools/artifact-tools.d.ts +42 -0
  235. package/dist/tools/artifact-tools.js +328 -0
  236. package/dist/tools/ask-user-simple.d.ts +1 -1
  237. package/dist/tools/ask-user-simple.js +2 -1
  238. package/dist/tools/ask-user.d.ts +1 -1
  239. package/dist/tools/ask-user.js +2 -1
  240. package/dist/tools/backlog-wrappers.d.ts +56 -0
  241. package/dist/tools/backlog-wrappers.js +353 -0
  242. package/dist/tools/backlog.d.ts +2 -2
  243. package/dist/tools/backlog.js +2 -2
  244. package/dist/tools/db-tools.d.ts +12 -0
  245. package/dist/tools/db-tools.js +14 -0
  246. package/dist/tools/delegate-background.d.ts +27 -0
  247. package/dist/tools/delegate-background.js +115 -0
  248. package/dist/tools/delegate.d.ts +22 -0
  249. package/dist/tools/delegate.js +97 -0
  250. package/dist/tools/delegation-status.d.ts +16 -0
  251. package/dist/tools/delegation-status.js +128 -0
  252. package/dist/tools/document-db.d.ts +43 -0
  253. package/dist/tools/document-db.js +220 -0
  254. package/dist/tools/guide-tool.d.ts +12 -0
  255. package/dist/tools/guide-tool.js +59 -0
  256. package/dist/tools/handoff.d.ts +25 -0
  257. package/dist/tools/handoff.js +99 -0
  258. package/dist/tools/meta-tools.d.ts +26 -0
  259. package/dist/tools/meta-tools.js +47 -0
  260. package/dist/tools/plan-tools.d.ts +54 -0
  261. package/dist/tools/plan-tools.js +338 -0
  262. package/dist/tools/platform-adapter.d.ts +29 -0
  263. package/dist/tools/platform-adapter.js +394 -0
  264. package/dist/tools/project-db.d.ts +34 -0
  265. package/dist/tools/project-db.js +39 -0
  266. package/dist/tools/recall-work-tool.d.ts +18 -0
  267. package/dist/tools/recall-work-tool.js +82 -0
  268. package/dist/tools/workitem-db.d.ts +135 -0
  269. package/dist/tools/workitem-db.js +730 -0
  270. package/dist/tools.d.ts +67 -2
  271. package/dist/tools.js +238 -38
  272. package/dist/ui/ask-user-overlay.d.ts +2 -2
  273. package/dist/ui/ask-user-overlay.js +443 -535
  274. package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
  275. package/dist/ui/ask-user-simple-overlay.js +182 -209
  276. package/dist/ui/autocomplete-controller.d.ts +42 -0
  277. package/dist/ui/autocomplete-controller.js +384 -0
  278. package/dist/ui/base/index.d.ts +26 -0
  279. package/dist/ui/base/index.js +33 -0
  280. package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
  281. package/dist/ui/base/inline-overlay-utils.js +320 -0
  282. package/dist/ui/base/inline-overlay.d.ts +159 -0
  283. package/dist/ui/base/inline-overlay.js +257 -0
  284. package/dist/ui/base/key-utils.d.ts +15 -0
  285. package/dist/ui/base/key-utils.js +30 -0
  286. package/dist/ui/base/overlay-base-v2.d.ts +203 -0
  287. package/dist/ui/base/overlay-base-v2.js +260 -0
  288. package/dist/ui/base/overlay-base.d.ts +156 -0
  289. package/dist/ui/base/overlay-base.js +238 -0
  290. package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
  291. package/dist/ui/base/overlay-lifecycle.js +159 -0
  292. package/dist/ui/base/overlay-types.d.ts +185 -0
  293. package/dist/ui/base/overlay-types.js +7 -0
  294. package/dist/ui/base/render-utils.d.ts +27 -0
  295. package/dist/ui/base/render-utils.js +36 -0
  296. package/dist/ui/base/screen-stack.d.ts +148 -0
  297. package/dist/ui/base/screen-stack.js +184 -0
  298. package/dist/ui/base/tabbed-list-overlay-v2.d.ts +118 -0
  299. package/dist/ui/base/tabbed-list-overlay-v2.js +335 -0
  300. package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
  301. package/dist/ui/base/tabbed-list-overlay.js +369 -0
  302. package/dist/ui/constants/labels.d.ts +14 -0
  303. package/dist/ui/constants/labels.js +51 -0
  304. package/dist/ui/conversation-store.d.ts +55 -0
  305. package/dist/ui/conversation-store.js +107 -0
  306. package/dist/ui/conversation.d.ts +75 -4
  307. package/dist/ui/conversation.js +376 -165
  308. package/dist/ui/diff.d.ts +7 -1
  309. package/dist/ui/diff.js +85 -48
  310. package/dist/ui/ephemeral.d.ts +1 -1
  311. package/dist/ui/ephemeral.js +4 -10
  312. package/dist/ui/features/index.d.ts +34 -0
  313. package/dist/ui/features/index.js +34 -0
  314. package/dist/ui/features/input-feature.d.ts +85 -0
  315. package/dist/ui/features/input-feature.js +238 -0
  316. package/dist/ui/features/list-feature.d.ts +155 -0
  317. package/dist/ui/features/list-feature.js +244 -0
  318. package/dist/ui/features/pagination-feature.d.ts +154 -0
  319. package/dist/ui/features/pagination-feature.js +238 -0
  320. package/dist/ui/features/search-feature.d.ts +148 -0
  321. package/dist/ui/features/search-feature.js +185 -0
  322. package/dist/ui/features/tab-feature.d.ts +194 -0
  323. package/dist/ui/features/tab-feature.js +307 -0
  324. package/dist/ui/file-autocomplete.d.ts +24 -0
  325. package/dist/ui/file-autocomplete.js +56 -0
  326. package/dist/ui/footer-renderer.d.ts +69 -0
  327. package/dist/ui/footer-renderer.js +431 -0
  328. package/dist/ui/footer.d.ts +181 -7
  329. package/dist/ui/footer.js +523 -74
  330. package/dist/ui/guardrail-overlay.d.ts +29 -0
  331. package/dist/ui/guardrail-overlay.js +145 -0
  332. package/dist/ui/index.d.ts +1 -1
  333. package/dist/ui/index.js +1 -3
  334. package/dist/ui/input-controller.d.ts +51 -0
  335. package/dist/ui/input-controller.js +176 -0
  336. package/dist/ui/input-prompt.d.ts +135 -33
  337. package/dist/ui/input-prompt.js +728 -337
  338. package/dist/ui/iteration-limit-overlay.d.ts +2 -2
  339. package/dist/ui/iteration-limit-overlay.js +92 -128
  340. package/dist/ui/keyboard-handler.d.ts +57 -0
  341. package/dist/ui/keyboard-handler.js +557 -0
  342. package/dist/ui/keys-overlay.d.ts +1 -0
  343. package/dist/ui/keys-overlay.js +203 -141
  344. package/dist/ui/line-utils.d.ts +88 -0
  345. package/dist/ui/line-utils.js +150 -0
  346. package/dist/ui/live-region-facade.d.ts +42 -0
  347. package/dist/ui/live-region-facade.js +205 -0
  348. package/dist/ui/live-region.d.ts +157 -0
  349. package/dist/ui/live-region.js +379 -0
  350. package/dist/ui/mascot/expressions.d.ts +32 -0
  351. package/dist/ui/mascot/expressions.js +213 -0
  352. package/dist/ui/mascot/index.d.ts +8 -0
  353. package/dist/ui/mascot/index.js +8 -0
  354. package/dist/ui/mascot/renderer.d.ts +19 -0
  355. package/dist/ui/mascot/renderer.js +132 -0
  356. package/dist/ui/overlay/data/tutorial-content.d.ts +9 -0
  357. package/dist/ui/overlay/data/tutorial-content.js +9 -0
  358. package/dist/ui/overlay/data/tutorial-registry.d.ts +12 -0
  359. package/dist/ui/overlay/data/tutorial-registry.js +116 -0
  360. package/dist/ui/overlay/data/tutorial-types.d.ts +35 -0
  361. package/dist/ui/overlay/data/tutorial-types.js +6 -0
  362. package/dist/ui/overlay/data/tutorials/basics/first-conversation.d.ts +7 -0
  363. package/dist/ui/overlay/data/tutorials/basics/first-conversation.js +220 -0
  364. package/dist/ui/overlay/data/tutorials/basics/first-project.d.ts +7 -0
  365. package/dist/ui/overlay/data/tutorials/basics/first-project.js +284 -0
  366. package/dist/ui/overlay/data/tutorials/basics/navigation.d.ts +8 -0
  367. package/dist/ui/overlay/data/tutorials/basics/navigation.js +22 -0
  368. package/dist/ui/overlay/data/tutorials/basics/welcome.d.ts +7 -0
  369. package/dist/ui/overlay/data/tutorials/basics/welcome.js +174 -0
  370. package/dist/ui/overlay/data/tutorials/config/context-management.d.ts +7 -0
  371. package/dist/ui/overlay/data/tutorials/config/context-management.js +158 -0
  372. package/dist/ui/overlay/data/tutorials/config/mcp-servers.d.ts +8 -0
  373. package/dist/ui/overlay/data/tutorials/config/mcp-servers.js +155 -0
  374. package/dist/ui/overlay/data/tutorials/config/model-selection.d.ts +7 -0
  375. package/dist/ui/overlay/data/tutorials/config/model-selection.js +162 -0
  376. package/dist/ui/overlay/data/tutorials/config/permissions-safety.d.ts +7 -0
  377. package/dist/ui/overlay/data/tutorials/config/permissions-safety.js +163 -0
  378. package/dist/ui/overlay/data/tutorials/config/settings-config.d.ts +7 -0
  379. package/dist/ui/overlay/data/tutorials/config/settings-config.js +166 -0
  380. package/dist/ui/overlay/data/tutorials/planning/arch.d.ts +7 -0
  381. package/dist/ui/overlay/data/tutorials/planning/arch.js +168 -0
  382. package/dist/ui/overlay/data/tutorials/planning/backlog.d.ts +7 -0
  383. package/dist/ui/overlay/data/tutorials/planning/backlog.js +103 -0
  384. package/dist/ui/overlay/data/tutorials/planning/build.d.ts +7 -0
  385. package/dist/ui/overlay/data/tutorials/planning/build.js +173 -0
  386. package/dist/ui/overlay/data/tutorials/planning/design.d.ts +7 -0
  387. package/dist/ui/overlay/data/tutorials/planning/design.js +205 -0
  388. package/dist/ui/overlay/data/tutorials/planning/docs.d.ts +7 -0
  389. package/dist/ui/overlay/data/tutorials/planning/docs.js +143 -0
  390. package/dist/ui/overlay/data/tutorials/planning/prd.d.ts +7 -0
  391. package/dist/ui/overlay/data/tutorials/planning/prd.js +173 -0
  392. package/dist/ui/overlay/data/tutorials/planning/scaffold.d.ts +7 -0
  393. package/dist/ui/overlay/data/tutorials/planning/scaffold.js +164 -0
  394. package/dist/ui/overlay/data/tutorials/planning/sketch.d.ts +7 -0
  395. package/dist/ui/overlay/data/tutorials/planning/sketch.js +58 -0
  396. package/dist/ui/overlay/data/tutorials/projects/anchors.d.ts +7 -0
  397. package/dist/ui/overlay/data/tutorials/projects/anchors.js +248 -0
  398. package/dist/ui/overlay/data/tutorials/projects/import-project.d.ts +7 -0
  399. package/dist/ui/overlay/data/tutorials/projects/import-project.js +172 -0
  400. package/dist/ui/overlay/data/tutorials/projects/managing-projects.d.ts +8 -0
  401. package/dist/ui/overlay/data/tutorials/projects/managing-projects.js +212 -0
  402. package/dist/ui/overlay/data/tutorials/projects/new-project.d.ts +7 -0
  403. package/dist/ui/overlay/data/tutorials/projects/new-project.js +251 -0
  404. package/dist/ui/overlay/data/tutorials/projects/session-management.d.ts +7 -0
  405. package/dist/ui/overlay/data/tutorials/projects/session-management.js +169 -0
  406. package/dist/ui/overlay/data/tutorials/teams/background-execution.d.ts +7 -0
  407. package/dist/ui/overlay/data/tutorials/teams/background-execution.js +171 -0
  408. package/dist/ui/overlay/data/tutorials/teams/multi-terminal.d.ts +8 -0
  409. package/dist/ui/overlay/data/tutorials/teams/multi-terminal.js +147 -0
  410. package/dist/ui/overlay/data/tutorials/teams/task-assignment.d.ts +7 -0
  411. package/dist/ui/overlay/data/tutorials/teams/task-assignment.js +204 -0
  412. package/dist/ui/overlay/data/tutorials/teams/team-overview.d.ts +7 -0
  413. package/dist/ui/overlay/data/tutorials/teams/team-overview.js +165 -0
  414. package/dist/ui/overlay/data/tutorials/teams/working-with-agents.d.ts +7 -0
  415. package/dist/ui/overlay/data/tutorials/teams/working-with-agents.js +172 -0
  416. package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
  417. package/dist/ui/overlay/impl/agents-overlay-v2.js +814 -0
  418. package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
  419. package/dist/ui/overlay/impl/anchors-overlay-v2.js +749 -0
  420. package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
  421. package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
  422. package/dist/ui/overlay/impl/artifact-detail-overlay-v2.d.ts +43 -0
  423. package/dist/ui/overlay/impl/artifact-detail-overlay-v2.js +232 -0
  424. package/dist/ui/overlay/impl/artifact-overlay-v2.d.ts +40 -0
  425. package/dist/ui/overlay/impl/artifact-overlay-v2.js +115 -0
  426. package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
  427. package/dist/ui/overlay/impl/ask-user-overlay-v2.js +581 -0
  428. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
  429. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
  430. package/dist/ui/overlay/impl/background-overlay-v2.d.ts +40 -0
  431. package/dist/ui/overlay/impl/background-overlay-v2.js +147 -0
  432. package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +52 -0
  433. package/dist/ui/overlay/impl/backlog-overlay-v2.js +681 -0
  434. package/dist/ui/overlay/impl/changelog-overlay-v2.d.ts +44 -0
  435. package/dist/ui/overlay/impl/changelog-overlay-v2.js +165 -0
  436. package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
  437. package/dist/ui/overlay/impl/commands-overlay-v2.js +439 -0
  438. package/dist/ui/overlay/impl/config-overlay-v2.d.ts +111 -0
  439. package/dist/ui/overlay/impl/config-overlay-v2.js +718 -0
  440. package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.d.ts +83 -0
  441. package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.js +711 -0
  442. package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +57 -0
  443. package/dist/ui/overlay/impl/dashboard-overlay-v2.js +382 -0
  444. package/dist/ui/overlay/impl/delegations-overlay-v2.d.ts +28 -0
  445. package/dist/ui/overlay/impl/delegations-overlay-v2.js +279 -0
  446. package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
  447. package/dist/ui/overlay/impl/docs-overlay-v2.js +117 -0
  448. package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +84 -0
  449. package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1112 -0
  450. package/dist/ui/overlay/impl/filter-overlay-v2.d.ts +41 -0
  451. package/dist/ui/overlay/impl/filter-overlay-v2.js +110 -0
  452. package/dist/ui/overlay/impl/games-overlay-v2.d.ts +31 -0
  453. package/dist/ui/overlay/impl/games-overlay-v2.js +135 -0
  454. package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
  455. package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
  456. package/dist/ui/overlay/impl/help-overlay-v2.d.ts +57 -0
  457. package/dist/ui/overlay/impl/help-overlay-v2.js +287 -0
  458. package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
  459. package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
  460. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
  461. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
  462. package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
  463. package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
  464. package/dist/ui/overlay/impl/login-overlay-v2.d.ts +49 -0
  465. package/dist/ui/overlay/impl/login-overlay-v2.js +277 -0
  466. package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
  467. package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
  468. package/dist/ui/overlay/impl/mcp-overlay-v2.d.ts +63 -0
  469. package/dist/ui/overlay/impl/mcp-overlay-v2.js +907 -0
  470. package/dist/ui/overlay/impl/model-overlay-v2.d.ts +93 -0
  471. package/dist/ui/overlay/impl/model-overlay-v2.js +1143 -0
  472. package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
  473. package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
  474. package/dist/ui/overlay/impl/new-overlay-v2.d.ts +108 -0
  475. package/dist/ui/overlay/impl/new-overlay-v2.js +1243 -0
  476. package/dist/ui/overlay/impl/notifications-overlay-v2.d.ts +20 -0
  477. package/dist/ui/overlay/impl/notifications-overlay-v2.js +116 -0
  478. package/dist/ui/overlay/impl/onboarding-wizard-overlay-v2.d.ts +76 -0
  479. package/dist/ui/overlay/impl/onboarding-wizard-overlay-v2.js +728 -0
  480. package/dist/ui/overlay/impl/pending-overlay-v2.d.ts +51 -0
  481. package/dist/ui/overlay/impl/pending-overlay-v2.js +445 -0
  482. package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
  483. package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
  484. package/dist/ui/overlay/impl/permissions-overlay-v2.d.ts +85 -0
  485. package/dist/ui/overlay/impl/permissions-overlay-v2.js +820 -0
  486. package/dist/ui/overlay/impl/plan-approval-overlay-v2.d.ts +35 -0
  487. package/dist/ui/overlay/impl/plan-approval-overlay-v2.js +181 -0
  488. package/dist/ui/overlay/impl/project-edit-overlay-v2.d.ts +36 -0
  489. package/dist/ui/overlay/impl/project-edit-overlay-v2.js +195 -0
  490. package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +37 -0
  491. package/dist/ui/overlay/impl/projects-overlay-v2.js +733 -0
  492. package/dist/ui/overlay/impl/reset-overlay-v2.d.ts +39 -0
  493. package/dist/ui/overlay/impl/reset-overlay-v2.js +107 -0
  494. package/dist/ui/overlay/impl/resume-overlay-v2.d.ts +60 -0
  495. package/dist/ui/overlay/impl/resume-overlay-v2.js +414 -0
  496. package/dist/ui/overlay/impl/session-mode-overlay-v2.d.ts +43 -0
  497. package/dist/ui/overlay/impl/session-mode-overlay-v2.js +124 -0
  498. package/dist/ui/overlay/impl/tasks-overlay-v2.d.ts +28 -0
  499. package/dist/ui/overlay/impl/tasks-overlay-v2.js +283 -0
  500. package/dist/ui/overlay/impl/team-overlay-v2.d.ts +86 -0
  501. package/dist/ui/overlay/impl/team-overlay-v2.js +692 -0
  502. package/dist/ui/overlay/impl/terminals-overlay-v2.d.ts +26 -0
  503. package/dist/ui/overlay/impl/terminals-overlay-v2.js +217 -0
  504. package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
  505. package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
  506. package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
  507. package/dist/ui/overlay/impl/tools-overlay-v2.js +214 -0
  508. package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +45 -0
  509. package/dist/ui/overlay/impl/tutorial-overlay-v2.js +212 -0
  510. package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
  511. package/dist/ui/overlay/impl/workflow-overlay-v2.js +641 -0
  512. package/dist/ui/overlay/index.d.ts +52 -0
  513. package/dist/ui/overlay/index.js +54 -0
  514. package/dist/ui/overlay/key-utils.d.ts +6 -0
  515. package/dist/ui/overlay/key-utils.js +6 -0
  516. package/dist/ui/overlay/types.d.ts +140 -0
  517. package/dist/ui/overlay/types.js +22 -0
  518. package/dist/ui/overlay-manager.d.ts +43 -0
  519. package/dist/ui/overlay-manager.js +238 -0
  520. package/dist/ui/overlays.d.ts +0 -4
  521. package/dist/ui/overlays.js +4 -460
  522. package/dist/ui/permission-overlay.d.ts +1 -1
  523. package/dist/ui/permission-overlay.js +189 -300
  524. package/dist/ui/providers/types.d.ts +178 -0
  525. package/dist/ui/providers/types.js +9 -0
  526. package/dist/ui/render-modes.d.ts +36 -0
  527. package/dist/ui/render-modes.js +44 -0
  528. package/dist/ui/startup-menu.d.ts +36 -0
  529. package/dist/ui/startup-menu.js +236 -0
  530. package/dist/ui/status-bar-controller.d.ts +33 -0
  531. package/dist/ui/status-bar-controller.js +99 -0
  532. package/dist/ui/subagent-renderer.d.ts +117 -0
  533. package/dist/ui/subagent-renderer.js +318 -0
  534. package/dist/ui/terminal-autocomplete-utils.d.ts +23 -0
  535. package/dist/ui/terminal-autocomplete-utils.js +83 -0
  536. package/dist/ui/terminal-codes.d.ts +94 -0
  537. package/dist/ui/terminal-codes.js +124 -0
  538. package/dist/ui/terminal-line-builders.d.ts +17 -0
  539. package/dist/ui/terminal-line-builders.js +42 -0
  540. package/dist/ui/terminal-render-item.d.ts +16 -0
  541. package/dist/ui/terminal-render-item.js +267 -0
  542. package/dist/ui/terminal-renderer.d.ts +220 -0
  543. package/dist/ui/terminal-renderer.js +750 -0
  544. package/dist/ui/terminal-types.d.ts +179 -0
  545. package/dist/ui/terminal-types.js +34 -0
  546. package/dist/ui/terminal-ui.d.ts +331 -0
  547. package/dist/ui/terminal-ui.js +819 -0
  548. package/dist/ui/terminal.d.ts +20 -0
  549. package/dist/ui/terminal.js +72 -0
  550. package/dist/ui/todo-zone.d.ts +19 -1
  551. package/dist/ui/todo-zone.js +124 -38
  552. package/dist/ui/tool-formatters.d.ts +16 -0
  553. package/dist/ui/tool-formatters.js +1027 -0
  554. package/dist/ui/turn-metrics.d.ts +56 -0
  555. package/dist/ui/turn-metrics.js +75 -0
  556. package/dist/ui/types.d.ts +28 -0
  557. package/dist/ui/types.js +1 -0
  558. package/dist/ui/vscode-diff-ipc.d.ts +102 -0
  559. package/dist/ui/vscode-diff-ipc.js +385 -0
  560. package/dist/utils/credentials.d.ts +24 -5
  561. package/dist/utils/credentials.js +123 -9
  562. package/dist/utils/debug-log.d.ts +28 -0
  563. package/dist/utils/debug-log.js +57 -0
  564. package/dist/utils/format-tokens.d.ts +13 -0
  565. package/dist/utils/format-tokens.js +18 -0
  566. package/dist/utils/git-config.d.ts +26 -0
  567. package/dist/utils/git-config.js +54 -0
  568. package/dist/utils/message-utils.d.ts +61 -0
  569. package/dist/utils/message-utils.js +72 -0
  570. package/dist/utils/model-tiers.d.ts +8 -1
  571. package/dist/utils/model-tiers.js +39 -17
  572. package/dist/utils/open-browser.d.ts +5 -0
  573. package/dist/utils/open-browser.js +32 -0
  574. package/dist/utils/path-safety.d.ts +56 -0
  575. package/dist/utils/path-safety.js +240 -0
  576. package/dist/utils/project-detection.d.ts +58 -0
  577. package/dist/utils/project-detection.js +424 -0
  578. package/dist/utils/project-memory.js +2 -1
  579. package/dist/utils/project-status.d.ts +2 -2
  580. package/dist/utils/startup-perf.d.ts +18 -0
  581. package/dist/utils/startup-perf.js +60 -0
  582. package/dist/utils/token-tracker.d.ts +62 -0
  583. package/dist/utils/token-tracker.js +150 -0
  584. package/dist/utils/token-types.d.ts +23 -0
  585. package/dist/utils/token-types.js +18 -0
  586. package/dist/utils/types/config-types.d.ts +32 -0
  587. package/dist/utils/types/config-types.js +8 -0
  588. package/dist/utils/update-checker.d.ts +28 -0
  589. package/dist/utils/update-checker.js +106 -0
  590. package/dist/utils/version.d.ts +7 -0
  591. package/dist/utils/version.js +10 -0
  592. package/dist/utils/vscode-detect.d.ts +39 -0
  593. package/dist/utils/vscode-detect.js +137 -0
  594. package/dist/workflow/guided-mode-injector.d.ts +42 -0
  595. package/dist/workflow/guided-mode-injector.js +191 -0
  596. package/dist/workflow/index.d.ts +8 -0
  597. package/dist/workflow/index.js +8 -0
  598. package/dist/workflow/step-criteria.d.ts +62 -0
  599. package/dist/workflow/step-criteria.js +150 -0
  600. package/dist/workflow/step-tracker.d.ts +92 -0
  601. package/dist/workflow/step-tracker.js +141 -0
  602. package/package.json +32 -12
  603. package/dist/index.old.d.ts +0 -7
  604. package/dist/index.old.js +0 -1014
  605. package/dist/repl.d.ts +0 -121
  606. package/dist/repl.js +0 -1878
  607. package/dist/templates/claude-md.d.ts +0 -7
  608. package/dist/templates/claude-md.js +0 -189
  609. package/dist/test-autocomplete.d.ts +0 -7
  610. package/dist/test-autocomplete.js +0 -85
  611. package/dist/test-tabbed-menu.d.ts +0 -7
  612. package/dist/test-tabbed-menu.js +0 -25
  613. package/dist/tool-selector.d.ts +0 -71
  614. package/dist/tool-selector.js +0 -184
  615. package/dist/ui/agents-overlay.d.ts +0 -12
  616. package/dist/ui/agents-overlay.js +0 -501
  617. package/dist/ui/arch-type-overlay.d.ts +0 -20
  618. package/dist/ui/arch-type-overlay.js +0 -229
  619. package/dist/ui/backlog-overlay.d.ts +0 -17
  620. package/dist/ui/backlog-overlay.js +0 -786
  621. package/dist/ui/commands-overlay.d.ts +0 -11
  622. package/dist/ui/commands-overlay.js +0 -410
  623. package/dist/ui/config-overlay.d.ts +0 -34
  624. package/dist/ui/config-overlay.js +0 -977
  625. package/dist/ui/init-overlay.d.ts +0 -24
  626. package/dist/ui/init-overlay.js +0 -525
  627. package/dist/ui/input-prompt-v2.d.ts +0 -179
  628. package/dist/ui/input-prompt-v2.js +0 -991
  629. package/dist/ui/model-warning-overlay.d.ts +0 -30
  630. package/dist/ui/model-warning-overlay.js +0 -171
  631. package/dist/ui/tools-overlay.d.ts +0 -26
  632. package/dist/ui/tools-overlay.js +0 -278
  633. package/dist/ui/tutorial-overlay.d.ts +0 -10
  634. package/dist/ui/tutorial-overlay.js +0 -936
@@ -1,83 +1,174 @@
1
1
  /**
2
- * Input Prompt
2
+ * Input Prompt v2 (Event-Driven)
3
3
  *
4
- * @deprecated This module is superseded by input-prompt-v2.ts which uses
5
- * event-driven architecture for non-blocking input with queue mode support.
6
- * This file is kept for reference and may be removed in a future cleanup.
4
+ * Refactored input handling with event-driven architecture.
5
+ * - Emits events instead of blocking with getInput()
6
+ * - Supports queue mode for capturing input during agent execution
7
+ * - Always captures keystrokes (no blocking)
7
8
  *
8
- * Self-contained input handling with multiline support, autocomplete,
9
- * history navigation, and proper visual line navigation.
9
+ * Events:
10
+ * - 'submit' - User pressed Enter with input
11
+ * - 'command' - User submitted a slash command
12
+ * - 'cancel' - User pressed Ctrl+C
13
+ * - 'escape' - User pressed Esc (for aborting agent)
14
+ * - 'change' - Input text changed
10
15
  */
11
- import pc from 'picocolors';
16
+ import { EventEmitter } from 'events';
17
+ import { Chalk } from 'chalk';
18
+ import { getStyles } from '../themes/index.js';
12
19
  import * as terminal from './terminal.js';
20
+ import { getFileMatches, extractAtMention, replaceAtMention, extractDollarMention, replaceDollarMention, } from './file-autocomplete.js';
21
+ import { debugRender } from '../utils/debug-log.js';
22
+ import { stripAnsi as unifiedStripAnsi, getPhysicalLineCountWithOffset, } from './line-utils.js';
13
23
  // =============================================================================
14
24
  // Constants
15
25
  // =============================================================================
26
+ // Force color output (level 3 = 24-bit true color)
27
+ const chalk = new Chalk({ level: 3 });
16
28
  const MAX_VISIBLE_COMMANDS = 10;
17
29
  // =============================================================================
18
- // Default Commands
30
+ // Default Commands (from central registry)
19
31
  // =============================================================================
20
- export const DEFAULT_COMMANDS = [
21
- { command: '/help', description: 'Show available commands' },
22
- { command: '/exit', description: 'Quit the demo' },
23
- { command: '/clear', description: 'Clear conversation history' },
24
- { command: '/compact', description: 'Summarize old messages' },
25
- { command: '/tools', description: 'List available tools' },
26
- { command: '/tokens', description: 'Show session token usage' },
27
- { command: '/context', description: 'Show context window usage' },
28
- ];
32
+ export const DEFAULT_COMMANDS = [];
29
33
  // =============================================================================
30
34
  // Helper Functions
31
35
  // =============================================================================
32
36
  /**
33
37
  * Strip ANSI codes from string
38
+ * Uses unified line-utils for consistent behavior across components.
34
39
  */
35
40
  export function stripAnsi(str) {
36
- // eslint-disable-next-line no-control-regex
37
- return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
41
+ return unifiedStripAnsi(str);
42
+ }
43
+ /**
44
+ * Format input text with syntax highlighting for special prefixes.
45
+ * - $agent references: bold + secondary color
46
+ * - @path references: secondary color
47
+ * - /commands: primary color
48
+ */
49
+ function formatInputText(text) {
50
+ const s = getStyles();
51
+ let result = text;
52
+ // Format $agent references - bold + secondary
53
+ // Match $word at word boundaries (not inside other text)
54
+ result = result.replace(/(?<![$\w])\$([a-zA-Z_][a-zA-Z0-9_]*)\b/g, (_match, agentId) => chalk.bold(s.secondary(`$${agentId}`)));
55
+ // Format @path references - secondary (not bold, paths can be long)
56
+ // Match @word or @path/to/file patterns
57
+ result = result.replace(/(?<![@\w])@([\w./-]+)/g, (_match, path) => s.secondary(`@${path}`));
58
+ // Format /commands at start of input - primary color
59
+ // Only match at start of line (after optional whitespace)
60
+ result = result.replace(/^(\s*)(\/[a-zA-Z][a-zA-Z0-9_-]*)/g, (_match, space, cmd) => space + s.primary(cmd));
61
+ return result;
38
62
  }
39
63
  /**
40
64
  * Calculate physical lines for wrapped text
65
+ * Uses unified line-utils for consistent behavior across components.
41
66
  */
42
67
  function calcPhysicalLines(text, startCol, termWidth) {
43
- if (text.length === 0)
44
- return 1;
45
- const totalLen = startCol + text.length;
46
- return Math.ceil(totalLen / termWidth) || 1;
68
+ return getPhysicalLineCountWithOffset(text, startCol, termWidth);
47
69
  }
48
70
  /**
49
- * Filter commands matching input
71
+ * Calculate fuzzy match score for a query against a target string.
72
+ * Higher score = better match.
73
+ * Returns -1 if no match.
74
+ */
75
+ function fuzzyMatchScore(query, target) {
76
+ const queryLower = query.toLowerCase();
77
+ const targetLower = target.toLowerCase();
78
+ // Exact prefix match - highest priority (score 1000+)
79
+ if (targetLower.startsWith(queryLower)) {
80
+ return 1000 + (100 - target.length); // Shorter commands rank higher
81
+ }
82
+ // Contiguous substring match - high priority (score 500+)
83
+ if (targetLower.includes(queryLower)) {
84
+ const index = targetLower.indexOf(queryLower);
85
+ return 500 + (100 - index); // Earlier matches rank higher
86
+ }
87
+ // Fuzzy match - characters appear in order (score 100+)
88
+ let queryIdx = 0;
89
+ let consecutiveBonus = 0;
90
+ let lastMatchIdx = -1;
91
+ for (let i = 0; i < targetLower.length && queryIdx < queryLower.length; i++) {
92
+ if (targetLower[i] === queryLower[queryIdx]) {
93
+ // Bonus for consecutive matches
94
+ if (lastMatchIdx === i - 1) {
95
+ consecutiveBonus += 10;
96
+ }
97
+ lastMatchIdx = i;
98
+ queryIdx++;
99
+ }
100
+ }
101
+ // All query characters found in order
102
+ if (queryIdx === queryLower.length) {
103
+ return 100 + consecutiveBonus + (100 - target.length);
104
+ }
105
+ // No match
106
+ return -1;
107
+ }
108
+ /**
109
+ * Filter and rank commands matching input using fuzzy matching
50
110
  */
51
111
  function filterCommands(input, commands) {
52
- const lower = input.toLowerCase();
53
- return commands.filter((cmd) => cmd.command.toLowerCase().startsWith(lower));
112
+ // Score all commands
113
+ const scored = commands
114
+ .map((cmd) => ({
115
+ cmd,
116
+ score: fuzzyMatchScore(input, cmd.command),
117
+ }))
118
+ .filter((item) => item.score >= 0);
119
+ // Sort by score (highest first)
120
+ scored.sort((a, b) => b.score - a.score);
121
+ return scored.map((item) => item.cmd);
54
122
  }
55
123
  // =============================================================================
56
124
  // Input Prompt Class
57
125
  // =============================================================================
58
- export class InputPrompt {
126
+ export class InputPrompt extends EventEmitter {
59
127
  // Configuration
60
128
  prompt;
61
129
  promptLen;
62
130
  showSeparators;
63
131
  commands;
64
- getTodos;
132
+ getAgentSuggestions;
133
+ getCommands;
65
134
  // Input state
66
135
  state = {
67
136
  lines: [''],
68
137
  currentLine: 0,
69
138
  cursorPos: 0,
70
139
  };
71
- // Autocomplete
140
+ // Command autocomplete (for /commands)
72
141
  autocomplete = {
73
142
  active: false,
74
143
  matches: [],
75
144
  selectedIndex: 0,
145
+ scrollOffset: 0,
146
+ };
147
+ // File autocomplete (for @paths)
148
+ fileAutocomplete = {
149
+ active: false,
150
+ matches: [],
151
+ selectedIndex: 0,
152
+ scrollOffset: 0,
153
+ partial: '',
154
+ };
155
+ // Agent autocomplete (for $agents)
156
+ agentAutocomplete = {
157
+ active: false,
158
+ matches: [],
159
+ selectedIndex: 0,
160
+ scrollOffset: 0,
161
+ partial: '',
162
+ startPos: 0,
163
+ lineIndex: 0,
76
164
  };
77
165
  // History
78
166
  history = [];
79
167
  historyIndex = -1;
80
168
  savedInput = '';
169
+ // Queue mode
170
+ queueMode = false;
171
+ queuedInputs = [];
81
172
  // Rendering tracking
82
173
  renderedLines = 0;
83
174
  dropdownLines = 0;
@@ -85,29 +176,56 @@ export class InputPrompt {
85
176
  hasSeparators = false;
86
177
  // Control
87
178
  isRunning = false;
88
- resolveInput = null;
179
+ // Double Esc detection
180
+ lastEscTime = 0;
181
+ DOUBLE_ESC_THRESHOLD_MS = 500; // 500ms window for double Esc
182
+ // Suggestion (ghost text for next action)
183
+ suggestion = null;
89
184
  constructor(options = {}) {
90
- this.prompt = options.prompt ?? pc.cyan('❯ ');
185
+ super();
186
+ this.prompt = options.prompt ?? getStyles().primary('❯ ');
91
187
  this.promptLen = stripAnsi(this.prompt).length;
92
188
  this.showSeparators = options.showSeparators ?? true;
93
189
  this.commands = options.commands ?? DEFAULT_COMMANDS;
94
- this.getTodos = options.getTodos;
190
+ this.getAgentSuggestions = options.getAgentSuggestions;
191
+ this.getCommands = options.getCommands;
95
192
  }
96
193
  // ===========================================================================
97
194
  // Public API
98
195
  // ===========================================================================
99
196
  /**
100
- * Get input from user (async)
101
- * Returns when user submits or cancels
197
+ * Update the prompt string (for dynamic theme changes)
102
198
  */
103
- async getInput() {
104
- return new Promise((resolve) => {
105
- this.resolveInput = resolve;
106
- this.start();
107
- });
199
+ setPrompt(prompt) {
200
+ this.prompt = prompt;
201
+ this.promptLen = stripAnsi(prompt).length;
202
+ }
203
+ /**
204
+ * Set a suggestion for the next action (ghost text)
205
+ */
206
+ setSuggestion(action) {
207
+ this.suggestion = action;
208
+ }
209
+ /**
210
+ * Get the current suggestion
211
+ */
212
+ getSuggestion() {
213
+ return this.suggestion;
214
+ }
215
+ /**
216
+ * Clear the current suggestion
217
+ */
218
+ clearSuggestion() {
219
+ this.suggestion = null;
220
+ }
221
+ /**
222
+ * Set the callback to get available team agents for $ autocomplete
223
+ */
224
+ setAgentSuggestionsCallback(callback) {
225
+ this.getAgentSuggestions = callback;
108
226
  }
109
227
  /**
110
- * Start input mode
228
+ * Start input capture (non-blocking, event-driven)
111
229
  */
112
230
  start() {
113
231
  if (this.isRunning)
@@ -115,17 +233,67 @@ export class InputPrompt {
115
233
  this.isRunning = true;
116
234
  terminal.enableRawMode();
117
235
  this.resetState();
118
- this.render();
119
236
  process.stdin.on('data', this.handleData);
120
237
  }
121
238
  /**
122
- * Stop input mode
239
+ * Stop input capture
123
240
  */
124
241
  stop() {
242
+ if (!this.isRunning)
243
+ return;
125
244
  this.isRunning = false;
126
245
  terminal.disableRawMode();
127
246
  process.stdin.removeListener('data', this.handleData);
128
247
  }
248
+ /**
249
+ * Check if running
250
+ */
251
+ isActive() {
252
+ return this.isRunning;
253
+ }
254
+ /**
255
+ * Enable/disable queue mode
256
+ * In queue mode, Enter adds to queue instead of emitting 'submit'
257
+ */
258
+ setQueueMode(enabled) {
259
+ this.queueMode = enabled;
260
+ }
261
+ /**
262
+ * Check if in queue mode
263
+ */
264
+ isQueueMode() {
265
+ return this.queueMode;
266
+ }
267
+ /**
268
+ * Get all queued inputs (FIFO order)
269
+ */
270
+ getQueuedInputs() {
271
+ return [...this.queuedInputs];
272
+ }
273
+ /**
274
+ * Pop the first queued input
275
+ */
276
+ popQueuedInput() {
277
+ return this.queuedInputs.shift() ?? null;
278
+ }
279
+ /**
280
+ * Check if there are queued inputs
281
+ */
282
+ hasQueuedInput() {
283
+ return this.queuedInputs.length > 0;
284
+ }
285
+ /**
286
+ * Get number of queued inputs
287
+ */
288
+ getQueueLength() {
289
+ return this.queuedInputs.length;
290
+ }
291
+ /**
292
+ * Clear the queue
293
+ */
294
+ clearQueue() {
295
+ this.queuedInputs = [];
296
+ }
129
297
  /**
130
298
  * Get current buffer value
131
299
  */
@@ -139,37 +307,235 @@ export class InputPrompt {
139
307
  this.state.lines = value.split('\n');
140
308
  this.state.currentLine = this.state.lines.length - 1;
141
309
  this.state.cursorPos = this.state.lines[this.state.currentLine].length;
142
- if (this.isRunning) {
143
- this.updateAutocomplete();
144
- this.render();
310
+ this.updateAutocomplete();
311
+ }
312
+ /**
313
+ * Clear input buffer
314
+ */
315
+ clearInput() {
316
+ this.state = {
317
+ lines: [''],
318
+ currentLine: 0,
319
+ cursorPos: 0,
320
+ };
321
+ this.autocomplete = {
322
+ active: false,
323
+ matches: [],
324
+ selectedIndex: 0,
325
+ scrollOffset: 0,
326
+ };
327
+ this.fileAutocomplete = {
328
+ active: false,
329
+ matches: [],
330
+ selectedIndex: 0,
331
+ scrollOffset: 0,
332
+ partial: '',
333
+ };
334
+ this.agentAutocomplete = {
335
+ active: false,
336
+ matches: [],
337
+ selectedIndex: 0,
338
+ scrollOffset: 0,
339
+ partial: '',
340
+ startPos: 0,
341
+ lineIndex: 0,
342
+ };
343
+ }
344
+ /**
345
+ * Add command to history
346
+ */
347
+ addToHistory(input) {
348
+ if (input.trim() && (this.history.length === 0 || this.history[this.history.length - 1] !== input)) {
349
+ this.history.push(input);
145
350
  }
146
351
  }
147
352
  /**
148
- * Clear the rendered area
353
+ * Render the input prompt - returns array of lines
354
+ * Does NOT write to terminal (Footer handles that)
149
355
  */
150
- clear() {
151
- this.clearDropdown();
152
- if (this.linesAboveCursor > 0) {
153
- terminal.moveCursorToLineStart();
154
- terminal.moveCursorUp(this.linesAboveCursor);
356
+ render() {
357
+ const lines = [];
358
+ // Top separator
359
+ if (this.showSeparators) {
360
+ lines.push(this.getSeparatorLine());
155
361
  }
156
- terminal.clearToEndOfScreen();
157
- this.renderedLines = 0;
158
- this.linesAboveCursor = 0;
362
+ // Input lines
363
+ const s = getStyles();
364
+ for (let i = 0; i < this.state.lines.length; i++) {
365
+ const linePrompt = i === 0 ? this.prompt : s.muted(' \\ ');
366
+ const lineContent = this.state.lines[i];
367
+ // Show suggestion as ghost text on first line when input is empty
368
+ if (i === 0 && lineContent === '' && this.suggestion && !this.autocomplete.active && !this.fileAutocomplete.active) {
369
+ const hint = s.muted(' (tab to accept)');
370
+ lines.push(linePrompt + s.muted(this.suggestion) + hint);
371
+ }
372
+ else {
373
+ // Format special prefixes: $agent, @path, /command
374
+ const formattedContent = formatInputText(lineContent);
375
+ lines.push(linePrompt + formattedContent);
376
+ }
377
+ }
378
+ // Bottom separator
379
+ if (this.showSeparators) {
380
+ lines.push(this.getSeparatorLine());
381
+ }
382
+ return lines;
159
383
  }
160
384
  /**
161
- * Get number of rendered lines
385
+ * Get the cursor position info for rendering
162
386
  */
163
- getRenderedLines() {
164
- return this.renderedLines + this.dropdownLines + (this.showSeparators ? 2 : 0);
387
+ getCursorInfo() {
388
+ const termWidth = terminal.getTerminalWidth();
389
+ const currentLinePromptLen = this.state.currentLine === 0 ? this.promptLen : 5;
390
+ const cursorAbsPos = currentLinePromptLen + this.state.cursorPos;
391
+ // Calculate which row within input (accounting for separators)
392
+ let row = this.showSeparators ? 1 : 0; // Start after top separator
393
+ for (let i = 0; i < this.state.currentLine; i++) {
394
+ const lp = i === 0 ? this.promptLen : 5;
395
+ row += calcPhysicalLines(this.state.lines[i], lp, termWidth);
396
+ }
397
+ row += Math.floor(cursorAbsPos / termWidth);
398
+ const col = cursorAbsPos % termWidth;
399
+ debugRender('InputPrompt:getCursorInfo', `termWidth=${String(termWidth)} cursorPos=${String(this.state.cursorPos)} currentLine=${String(this.state.currentLine)} promptLen=${String(this.promptLen)}`);
400
+ debugRender('InputPrompt:getCursorInfo', `cursorAbsPos=${String(cursorAbsPos)} row=${String(row)} col=${String(col)}`);
401
+ return { row, col };
165
402
  }
166
403
  /**
167
- * Add command to history
404
+ * Get autocomplete dropdown lines (if active)
168
405
  */
169
- addToHistory(input) {
170
- if (input.trim() && (this.history.length === 0 || this.history[this.history.length - 1] !== input)) {
171
- this.history.push(input);
406
+ getAutocompleteLines() {
407
+ const s = getStyles();
408
+ // Agent autocomplete takes highest priority
409
+ if (this.agentAutocomplete.active && this.agentAutocomplete.matches.length > 0) {
410
+ const lines = [];
411
+ const offset = this.agentAutocomplete.scrollOffset;
412
+ const visible = this.agentAutocomplete.matches.slice(offset, offset + MAX_VISIBLE_COMMANDS);
413
+ const total = this.agentAutocomplete.matches.length;
414
+ // Show scroll indicator if there are more items above
415
+ if (offset > 0) {
416
+ lines.push(s.muted(` ↑ ${String(offset)} more above`));
417
+ }
418
+ for (let i = 0; i < visible.length; i++) {
419
+ const agent = visible[i];
420
+ const actualIndex = offset + i;
421
+ const isSelected = actualIndex === this.agentAutocomplete.selectedIndex;
422
+ const prefix = isSelected ? s.primary('❯ ') : ' ';
423
+ const mascot = agent.mascot + ' ';
424
+ const name = isSelected ? s.primary(chalk.bold('$' + agent.id)) : '$' + agent.id;
425
+ const desc = s.muted(` - ${agent.displayName}`);
426
+ lines.push(`${prefix}${mascot}${name}${desc}`);
427
+ }
428
+ // Show scroll indicator if there are more items below
429
+ const remaining = total - offset - visible.length;
430
+ if (remaining > 0) {
431
+ lines.push(s.muted(` ↓ ${String(remaining)} more below`));
432
+ }
433
+ return lines;
434
+ }
435
+ // File autocomplete takes second priority
436
+ if (this.fileAutocomplete.active && this.fileAutocomplete.matches.length > 0) {
437
+ const lines = [];
438
+ const offset = this.fileAutocomplete.scrollOffset;
439
+ const visible = this.fileAutocomplete.matches.slice(offset, offset + MAX_VISIBLE_COMMANDS);
440
+ const total = this.fileAutocomplete.matches.length;
441
+ // Show scroll indicator if there are more items above
442
+ if (offset > 0) {
443
+ lines.push(s.muted(` ↑ ${String(offset)} more above`));
444
+ }
445
+ for (let i = 0; i < visible.length; i++) {
446
+ const file = visible[i];
447
+ const actualIndex = offset + i;
448
+ const isSelected = actualIndex === this.fileAutocomplete.selectedIndex;
449
+ const prefix = isSelected ? s.primary('❯ ') : ' ';
450
+ const icon = file.isDirectory ? s.warning('📁 ') : s.info('📄 ');
451
+ const name = isSelected ? s.primary(chalk.bold(file.path)) : file.path;
452
+ lines.push(`${prefix}${icon}${name}`);
453
+ }
454
+ // Show scroll indicator if there are more items below
455
+ const remaining = total - offset - visible.length;
456
+ if (remaining > 0) {
457
+ lines.push(s.muted(` ↓ ${String(remaining)} more below`));
458
+ }
459
+ return lines;
460
+ }
461
+ // Command autocomplete
462
+ if (!this.autocomplete.active || this.autocomplete.matches.length === 0) {
463
+ return [];
172
464
  }
465
+ const lines = [];
466
+ const offset = this.autocomplete.scrollOffset;
467
+ const visible = this.autocomplete.matches.slice(offset, offset + MAX_VISIBLE_COMMANDS);
468
+ const total = this.autocomplete.matches.length;
469
+ // Show scroll indicator if there are more items above
470
+ if (offset > 0) {
471
+ lines.push(s.muted(` ↑ ${String(offset)} more above`));
472
+ }
473
+ for (let i = 0; i < visible.length; i++) {
474
+ const cmd = visible[i];
475
+ const actualIndex = offset + i;
476
+ const isSelected = actualIndex === this.autocomplete.selectedIndex;
477
+ const prefix = isSelected ? s.primary('❯ ') : ' ';
478
+ const name = isSelected ? s.primary(chalk.bold(cmd.command)) : cmd.command;
479
+ const desc = s.muted(` - ${cmd.description}`);
480
+ lines.push(`${prefix}${name}${desc}`);
481
+ }
482
+ // Show scroll indicator if there are more items below
483
+ const remaining = total - offset - visible.length;
484
+ if (remaining > 0) {
485
+ lines.push(s.muted(` ↓ ${String(remaining)} more below`));
486
+ }
487
+ return lines;
488
+ }
489
+ /**
490
+ * Check if autocomplete is showing
491
+ */
492
+ isAutocompleteActive() {
493
+ return ((this.autocomplete.active && this.autocomplete.matches.length > 0) ||
494
+ (this.fileAutocomplete.active && this.fileAutocomplete.matches.length > 0) ||
495
+ (this.agentAutocomplete.active && this.agentAutocomplete.matches.length > 0));
496
+ }
497
+ /**
498
+ * Get height of rendered content
499
+ */
500
+ getHeight() {
501
+ let height = this.state.lines.length;
502
+ if (this.showSeparators)
503
+ height += 2;
504
+ return height;
505
+ }
506
+ // ===========================================================================
507
+ // Legacy API (for backwards compatibility during migration)
508
+ // ===========================================================================
509
+ /**
510
+ * @deprecated Use event-driven start() instead
511
+ * Kept for backwards compatibility during migration
512
+ */
513
+ async getInput() {
514
+ return new Promise((resolve) => {
515
+ const onSubmit = (input) => {
516
+ cleanup();
517
+ resolve({ action: 'submit', value: input });
518
+ };
519
+ const onCommand = (command, args) => {
520
+ cleanup();
521
+ resolve({ action: 'command', command, args });
522
+ };
523
+ const onCancel = () => {
524
+ cleanup();
525
+ resolve({ action: 'cancel' });
526
+ };
527
+ const cleanup = () => {
528
+ this.removeListener('submit', onSubmit);
529
+ this.removeListener('command', onCommand);
530
+ this.removeListener('cancel', onCancel);
531
+ };
532
+ this.on('submit', onSubmit);
533
+ this.on('command', onCommand);
534
+ this.on('cancel', onCancel);
535
+ if (!this.isRunning) {
536
+ this.start();
537
+ }
538
+ });
173
539
  }
174
540
  // ===========================================================================
175
541
  // Private: State Management
@@ -184,6 +550,23 @@ export class InputPrompt {
184
550
  active: false,
185
551
  matches: [],
186
552
  selectedIndex: 0,
553
+ scrollOffset: 0,
554
+ };
555
+ this.fileAutocomplete = {
556
+ active: false,
557
+ matches: [],
558
+ selectedIndex: 0,
559
+ scrollOffset: 0,
560
+ partial: '',
561
+ };
562
+ this.agentAutocomplete = {
563
+ active: false,
564
+ matches: [],
565
+ selectedIndex: 0,
566
+ scrollOffset: 0,
567
+ partial: '',
568
+ startPos: 0,
569
+ lineIndex: 0,
187
570
  };
188
571
  this.historyIndex = -1;
189
572
  this.savedInput = '';
@@ -194,229 +577,89 @@ export class InputPrompt {
194
577
  }
195
578
  updateAutocomplete() {
196
579
  const fullInput = this.getValue();
580
+ const currentLine = this.state.lines[this.state.currentLine];
581
+ const cursorPos = this.state.cursorPos;
582
+ // Check for @ file path autocomplete first
583
+ const atMention = extractAtMention(currentLine, cursorPos);
584
+ if (atMention !== null) {
585
+ // File autocomplete mode
586
+ this.autocomplete.active = false;
587
+ this.autocomplete.matches = [];
588
+ this.autocomplete.selectedIndex = 0;
589
+ this.autocomplete.scrollOffset = 0;
590
+ this.agentAutocomplete.active = false;
591
+ this.agentAutocomplete.matches = [];
592
+ this.fileAutocomplete.active = true;
593
+ this.fileAutocomplete.partial = atMention;
594
+ this.fileAutocomplete.matches = getFileMatches(atMention);
595
+ if (this.fileAutocomplete.selectedIndex >= this.fileAutocomplete.matches.length) {
596
+ this.fileAutocomplete.selectedIndex = Math.max(0, this.fileAutocomplete.matches.length - 1);
597
+ this.fileAutocomplete.scrollOffset = 0;
598
+ }
599
+ return;
600
+ }
601
+ // Reset file autocomplete
602
+ this.fileAutocomplete.active = false;
603
+ this.fileAutocomplete.matches = [];
604
+ this.fileAutocomplete.selectedIndex = 0;
605
+ this.fileAutocomplete.scrollOffset = 0;
606
+ this.fileAutocomplete.partial = '';
607
+ // Check for $ agent autocomplete (anywhere in the current line)
608
+ if (this.getAgentSuggestions) {
609
+ const currentLine = this.state.lines[this.state.currentLine];
610
+ const dollarMention = extractDollarMention(currentLine, this.state.cursorPos);
611
+ if (dollarMention !== null) {
612
+ const partial = dollarMention.partial.toLowerCase();
613
+ const allAgents = this.getAgentSuggestions();
614
+ // Filter agents by partial match
615
+ const matches = allAgents.filter(agent => agent.id.toLowerCase().startsWith(partial) ||
616
+ agent.displayName.toLowerCase().includes(partial));
617
+ if (matches.length > 0) {
618
+ this.agentAutocomplete.active = true;
619
+ this.agentAutocomplete.partial = partial;
620
+ this.agentAutocomplete.matches = matches;
621
+ this.agentAutocomplete.startPos = dollarMention.startPos;
622
+ this.agentAutocomplete.lineIndex = this.state.currentLine;
623
+ if (this.agentAutocomplete.selectedIndex >= matches.length) {
624
+ this.agentAutocomplete.selectedIndex = Math.max(0, matches.length - 1);
625
+ this.agentAutocomplete.scrollOffset = 0;
626
+ }
627
+ // Reset other autocompletes
628
+ this.autocomplete.active = false;
629
+ this.autocomplete.matches = [];
630
+ return;
631
+ }
632
+ }
633
+ }
634
+ // Reset agent autocomplete
635
+ this.agentAutocomplete.active = false;
636
+ this.agentAutocomplete.matches = [];
637
+ this.agentAutocomplete.selectedIndex = 0;
638
+ this.agentAutocomplete.scrollOffset = 0;
639
+ this.agentAutocomplete.partial = '';
640
+ this.agentAutocomplete.startPos = 0;
641
+ this.agentAutocomplete.lineIndex = 0;
642
+ // Check for / command autocomplete
197
643
  if (fullInput.startsWith('/') && this.state.lines.length === 1) {
198
644
  this.autocomplete.active = true;
199
- this.autocomplete.matches = filterCommands(fullInput, this.commands);
645
+ // Refresh commands dynamically to include newly created custom commands
646
+ const freshCommands = this.getCommands?.() ?? this.commands;
647
+ this.autocomplete.matches = filterCommands(fullInput, freshCommands);
200
648
  if (this.autocomplete.selectedIndex >= this.autocomplete.matches.length) {
201
649
  this.autocomplete.selectedIndex = Math.max(0, this.autocomplete.matches.length - 1);
650
+ this.autocomplete.scrollOffset = 0;
202
651
  }
203
652
  }
204
653
  else {
205
654
  this.autocomplete.active = false;
206
655
  this.autocomplete.matches = [];
207
656
  this.autocomplete.selectedIndex = 0;
657
+ this.autocomplete.scrollOffset = 0;
208
658
  }
209
659
  }
210
- // ===========================================================================
211
- // Private: Layout Calculation
212
- // ===========================================================================
213
- /**
214
- * Get physical layout of current buffer
215
- */
216
- getPhysicalLayout() {
217
- const termWidth = terminal.getTerminalWidth();
218
- const lines = [];
219
- let cursorRow = 0;
220
- let cursorCol = 0;
221
- let totalRows = 0;
222
- for (let i = 0; i < this.state.lines.length; i++) {
223
- const linePromptLen = i === 0 ? this.promptLen : 5; // " \ "
224
- const lineText = this.state.lines[i];
225
- const physicalLines = calcPhysicalLines(lineText, linePromptLen, termWidth);
226
- for (let p = 0; p < physicalLines; p++) {
227
- const start = p * termWidth - (p === 0 ? 0 : linePromptLen);
228
- const end = start + termWidth;
229
- lines.push(lineText.slice(Math.max(0, start), end));
230
- }
231
- if (i === this.state.currentLine) {
232
- const cursorAbsPos = linePromptLen + this.state.cursorPos;
233
- cursorRow = totalRows + Math.floor(cursorAbsPos / termWidth);
234
- cursorCol = cursorAbsPos % termWidth;
235
- }
236
- totalRows += physicalLines;
237
- }
238
- return { lines, cursorRow, cursorCol, totalRows };
239
- }
240
- // ===========================================================================
241
- // Private: Rendering
242
- // ===========================================================================
243
660
  getSeparatorLine() {
244
- return pc.dim('─'.repeat(terminal.getTerminalWidth()));
245
- }
246
- renderTodos() {
247
- if (!this.getTodos)
248
- return 0;
249
- const todos = this.getTodos();
250
- if (todos.length === 0)
251
- return 0;
252
- let linesRendered = 0;
253
- // Group todos by status for display
254
- const inProgress = todos.filter(t => t.status === 'in_progress');
255
- const pending = todos.filter(t => t.status === 'pending');
256
- const completed = todos.filter(t => t.status === 'completed');
257
- // Show in-progress task prominently
258
- for (const todo of inProgress) {
259
- const label = todo.activeForm || todo.content;
260
- terminal.write(pc.cyan('⟳ ') + pc.bold(label) + '\n');
261
- linesRendered++;
262
- }
263
- // Show pending tasks
264
- for (const todo of pending) {
265
- terminal.write(pc.dim('○ ') + pc.dim(todo.content) + '\n');
266
- linesRendered++;
267
- }
268
- // Show completed tasks (dimmed)
269
- for (const todo of completed) {
270
- terminal.write(pc.green('✓ ') + pc.dim(pc.strikethrough(todo.content)) + '\n');
271
- linesRendered++;
272
- }
273
- if (linesRendered > 0) {
274
- terminal.write('\n');
275
- linesRendered++;
276
- }
277
- return linesRendered;
278
- }
279
- render() {
280
- const termWidth = terminal.getTerminalWidth();
281
- // Clear previous render
282
- this.clearDropdown();
283
- if (this.linesAboveCursor > 0) {
284
- terminal.moveCursorToLineStart();
285
- terminal.moveCursorUp(this.linesAboveCursor);
286
- }
287
- else {
288
- terminal.moveCursorToLineStart();
289
- }
290
- terminal.clearToEndOfScreen();
291
- // Render todos (above input area)
292
- let physicalLinesRendered = 0;
293
- physicalLinesRendered += this.renderTodos();
294
- // Render top separator
295
- if (this.showSeparators) {
296
- terminal.write(this.getSeparatorLine() + '\n');
297
- physicalLinesRendered += 1;
298
- }
299
- // Render input lines
300
- let physicalLinesBeforeCursor = 0;
301
- for (let i = 0; i < this.state.lines.length; i++) {
302
- const linePrompt = i === 0 ? this.prompt : pc.dim(' \\ ');
303
- const linePromptLen = i === 0 ? this.promptLen : 5;
304
- if (i < this.state.currentLine) {
305
- physicalLinesBeforeCursor += calcPhysicalLines(this.state.lines[i], linePromptLen, termWidth);
306
- }
307
- terminal.write(linePrompt + this.state.lines[i]);
308
- if (i < this.state.lines.length - 1) {
309
- terminal.write('\n');
310
- }
311
- }
312
- // Render bottom separator
313
- if (this.showSeparators) {
314
- terminal.write('\n' + this.getSeparatorLine());
315
- }
316
- // Track state
317
- this.renderedLines = this.state.lines.length;
318
- this.hasSeparators = this.showSeparators;
319
- // Position cursor
320
- const currentLinePromptLen = this.state.currentLine === 0 ? this.promptLen : 5;
321
- const cursorAbsPos = currentLinePromptLen + this.state.cursorPos;
322
- const currentLinePhysical = calcPhysicalLines(this.state.lines[this.state.currentLine], currentLinePromptLen, termWidth);
323
- const cursorPhysicalRow = Math.floor(cursorAbsPos / termWidth);
324
- // Move up from bottom to cursor position
325
- let linesToMoveUp = this.showSeparators ? 1 : 0;
326
- for (let i = this.state.currentLine + 1; i < this.state.lines.length; i++) {
327
- const lp = i === 0 ? this.promptLen : 5;
328
- linesToMoveUp += calcPhysicalLines(this.state.lines[i], lp, termWidth);
329
- }
330
- linesToMoveUp += currentLinePhysical - 1 - cursorPhysicalRow;
331
- if (linesToMoveUp > 0) {
332
- terminal.moveCursorUp(linesToMoveUp);
333
- }
334
- const cursorCol = (cursorAbsPos % termWidth) + 1;
335
- terminal.moveCursorToColumn(cursorCol);
336
- // Track cursor position for next render
337
- this.linesAboveCursor = physicalLinesRendered + physicalLinesBeforeCursor + cursorPhysicalRow;
338
- // Render dropdown
339
- this.renderDropdown();
340
- }
341
- renderDropdown() {
342
- if (!this.autocomplete.active || this.autocomplete.matches.length === 0) {
343
- this.dropdownLines = 0;
344
- return;
345
- }
346
- const termWidth = terminal.getTerminalWidth();
347
- const currentLinePromptLen = this.state.currentLine === 0 ? this.promptLen : 5;
348
- // Move down past remaining input lines and bottom separator
349
- let linesToMoveDown = this.state.lines.length - 1 - this.state.currentLine;
350
- if (this.hasSeparators)
351
- linesToMoveDown += 1;
352
- // Account for wrapped lines
353
- for (let i = this.state.currentLine; i < this.state.lines.length; i++) {
354
- const lp = i === 0 ? this.promptLen : 5;
355
- const physical = calcPhysicalLines(this.state.lines[i], lp, termWidth);
356
- if (i === this.state.currentLine) {
357
- const cursorAbsPos = currentLinePromptLen + this.state.cursorPos;
358
- const cursorRow = Math.floor(cursorAbsPos / termWidth);
359
- linesToMoveDown += physical - 1 - cursorRow;
360
- }
361
- else {
362
- linesToMoveDown += physical - 1;
363
- }
364
- }
365
- if (linesToMoveDown > 0) {
366
- terminal.moveCursorDown(linesToMoveDown);
367
- }
368
- terminal.write('\n');
369
- const visible = this.autocomplete.matches.slice(0, MAX_VISIBLE_COMMANDS);
370
- for (let i = 0; i < visible.length; i++) {
371
- const cmd = visible[i];
372
- const isSelected = i === this.autocomplete.selectedIndex;
373
- const prefix = isSelected ? pc.cyan('❯ ') : ' ';
374
- const name = isSelected ? pc.cyan(pc.bold(cmd.command)) : cmd.command;
375
- const desc = pc.dim(` - ${cmd.description}`);
376
- terminal.write(`${prefix}${name}${desc}\n`);
377
- }
378
- // Move back up to cursor position
379
- this.dropdownLines = visible.length;
380
- const linesToMoveUp = this.dropdownLines + linesToMoveDown + 1;
381
- terminal.moveCursorUp(linesToMoveUp);
382
- const cursorAbsPos = currentLinePromptLen + this.state.cursorPos;
383
- const cursorCol = (cursorAbsPos % termWidth) + 1;
384
- terminal.moveCursorToColumn(cursorCol);
385
- }
386
- clearDropdown() {
387
- if (this.dropdownLines === 0)
388
- return;
389
- const termWidth = terminal.getTerminalWidth();
390
- const currentLinePromptLen = this.state.currentLine === 0 ? this.promptLen : 5;
391
- // Move down past remaining lines and separator
392
- let linesToMoveDown = this.state.lines.length - 1 - this.state.currentLine;
393
- if (this.hasSeparators)
394
- linesToMoveDown += 1;
395
- for (let i = this.state.currentLine; i < this.state.lines.length; i++) {
396
- const lp = i === 0 ? this.promptLen : 5;
397
- const physical = calcPhysicalLines(this.state.lines[i], lp, termWidth);
398
- if (i === this.state.currentLine) {
399
- const cursorAbsPos = currentLinePromptLen + this.state.cursorPos;
400
- const cursorRow = Math.floor(cursorAbsPos / termWidth);
401
- linesToMoveDown += physical - 1 - cursorRow;
402
- }
403
- else {
404
- linesToMoveDown += physical - 1;
405
- }
406
- }
407
- if (linesToMoveDown > 0) {
408
- terminal.moveCursorDown(linesToMoveDown);
409
- }
410
- terminal.write('\n');
411
- // Clear dropdown lines
412
- for (let i = 0; i < this.dropdownLines; i++) {
413
- terminal.clearLine();
414
- terminal.write('\n');
415
- }
416
- // Move back up
417
- const linesToMoveUp = this.dropdownLines + linesToMoveDown + 1;
418
- terminal.moveCursorUp(linesToMoveUp);
419
- this.dropdownLines = 0;
661
+ const s = getStyles();
662
+ return s.muted('─'.repeat(terminal.getTerminalWidth()));
420
663
  }
421
664
  // ===========================================================================
422
665
  // Private: Input Handling
@@ -433,7 +676,10 @@ export class InputPrompt {
433
676
  const isEnter = key === '\r' || key === '\n';
434
677
  const isBackspace = key === '\x7f' || key === '\b';
435
678
  const isTab = key === '\t';
679
+ const isShiftTab = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x5a;
436
680
  const isCtrlC = key === '\x03';
681
+ const isCtrlO = key === '\x0f'; // Ctrl+O for toggle subagent expand
682
+ const isCtrlB = key === '\x02'; // Ctrl+B for background tasks
437
683
  const isUpArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x41;
438
684
  const isDownArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x42;
439
685
  const isLeftArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x44;
@@ -460,16 +706,50 @@ export class InputPrompt {
460
706
  const isEnd = key === '\x05' || (data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x46);
461
707
  // Ctrl+C - cancel
462
708
  if (isCtrlC) {
463
- this.finishInput({ action: 'cancel' });
709
+ this.emit('cancel');
710
+ return;
711
+ }
712
+ // Ctrl+O - toggle subagent expand (only when agent is running)
713
+ if (isCtrlO) {
714
+ this.emit('toggleSubagentExpand');
715
+ return;
716
+ }
717
+ // Ctrl+B - show background tasks overlay
718
+ if (isCtrlB) {
719
+ this.emit('showTasks');
464
720
  return;
465
721
  }
466
- // Escape - close autocomplete
722
+ // Escape - close autocomplete, double Esc clears input, or emit escape for agent abort
467
723
  if (isEscape) {
468
- if (this.autocomplete.active) {
469
- this.clearDropdown();
724
+ const now = Date.now();
725
+ const isDoubleEsc = now - this.lastEscTime < this.DOUBLE_ESC_THRESHOLD_MS;
726
+ this.lastEscTime = now;
727
+ if (this.agentAutocomplete.active) {
728
+ // First priority: close agent autocomplete
729
+ this.agentAutocomplete.active = false;
730
+ this.agentAutocomplete.matches = [];
731
+ this.emit('change', this.getValue());
732
+ }
733
+ else if (this.fileAutocomplete.active) {
734
+ // Second priority: close file autocomplete
735
+ this.fileAutocomplete.active = false;
736
+ this.fileAutocomplete.matches = [];
737
+ this.emit('change', this.getValue());
738
+ }
739
+ else if (this.autocomplete.active) {
740
+ // Third priority: close command autocomplete
470
741
  this.autocomplete.active = false;
471
742
  this.autocomplete.matches = [];
472
- this.render();
743
+ this.emit('change', this.getValue());
744
+ }
745
+ else if (isDoubleEsc && this.getValue().length > 0) {
746
+ // Fourth priority: double Esc clears input (if there's content)
747
+ this.clearInput();
748
+ this.emit('change', this.getValue());
749
+ }
750
+ else {
751
+ // Fifth priority: emit escape for agent abort
752
+ this.emit('escape');
473
753
  }
474
754
  return;
475
755
  }
@@ -478,6 +758,11 @@ export class InputPrompt {
478
758
  this.handleEnter();
479
759
  return;
480
760
  }
761
+ // Shift+Tab - cycle modes
762
+ if (isShiftTab) {
763
+ this.emit('modeChange');
764
+ return;
765
+ }
481
766
  // Tab - accept autocomplete or insert spaces
482
767
  if (isTab) {
483
768
  this.handleTab();
@@ -512,12 +797,12 @@ export class InputPrompt {
512
797
  // Home/End
513
798
  if (isHome) {
514
799
  this.state.cursorPos = 0;
515
- this.render();
800
+ this.emit('change', this.getValue());
516
801
  return;
517
802
  }
518
803
  if (isEnd) {
519
804
  this.state.cursorPos = this.state.lines[this.state.currentLine].length;
520
- this.render();
805
+ this.emit('change', this.getValue());
521
806
  return;
522
807
  }
523
808
  // Backspace
@@ -532,13 +817,17 @@ export class InputPrompt {
532
817
  .filter((c) => c.charCodeAt(0) >= 32)
533
818
  .join('');
534
819
  if (printable.length > 0) {
820
+ // Clear suggestion when user starts typing
821
+ if (this.suggestion) {
822
+ this.suggestion = null;
823
+ }
535
824
  const line = this.state.lines[this.state.currentLine];
536
825
  this.state.lines[this.state.currentLine] =
537
826
  line.slice(0, this.state.cursorPos) + printable + line.slice(this.state.cursorPos);
538
827
  this.state.cursorPos += printable.length;
539
828
  this.historyIndex = -1;
540
829
  this.updateAutocomplete();
541
- this.render();
830
+ this.emit('change', this.getValue());
542
831
  }
543
832
  }
544
833
  }
@@ -551,35 +840,114 @@ export class InputPrompt {
551
840
  this.state.currentLine++;
552
841
  this.state.cursorPos = 0;
553
842
  this.autocomplete.active = false;
554
- this.clearDropdown();
555
- this.render();
843
+ this.emit('change', this.getValue());
844
+ return;
845
+ }
846
+ // Agent autocomplete selection - replace with $agentId and add space
847
+ if (this.agentAutocomplete.active && this.agentAutocomplete.matches.length > 0) {
848
+ const selectedAgent = this.agentAutocomplete.matches[this.agentAutocomplete.selectedIndex];
849
+ const lineIdx = this.agentAutocomplete.lineIndex;
850
+ const line = this.state.lines[lineIdx];
851
+ const result = replaceDollarMention(line, this.state.cursorPos, selectedAgent.id);
852
+ this.state.lines[lineIdx] = result.input;
853
+ this.state.currentLine = lineIdx;
854
+ this.state.cursorPos = result.cursorPos;
855
+ this.agentAutocomplete.active = false;
856
+ this.agentAutocomplete.matches = [];
857
+ this.emit('change', this.getValue());
858
+ // Return - let user type message after selecting agent
556
859
  return;
557
860
  }
558
- // Autocomplete selection
861
+ // File autocomplete selection - accept the path and continue
862
+ if (this.fileAutocomplete.active && this.fileAutocomplete.matches.length > 0) {
863
+ const selectedFile = this.fileAutocomplete.matches[this.fileAutocomplete.selectedIndex];
864
+ const result = replaceAtMention(currentLine, this.state.cursorPos, selectedFile.path);
865
+ this.state.lines[this.state.currentLine] = result.input;
866
+ this.state.cursorPos = result.cursorPos;
867
+ this.fileAutocomplete.active = false;
868
+ this.fileAutocomplete.matches = [];
869
+ // Don't return - fall through to submit
870
+ }
871
+ // Command autocomplete selection - accept the command and continue to execute it
559
872
  if (this.autocomplete.active && this.autocomplete.matches.length > 0) {
560
873
  this.state.lines[0] = this.autocomplete.matches[this.autocomplete.selectedIndex].command;
561
874
  this.state.cursorPos = this.state.lines[0].length;
875
+ this.autocomplete.active = false;
876
+ // Don't return - fall through to execute the command
562
877
  }
563
878
  const input = this.getValue();
879
+ // Empty input - ignore
880
+ if (!input.trim()) {
881
+ return;
882
+ }
883
+ // Add to history
884
+ this.addToHistory(input);
885
+ // Queue mode - add to queue instead of emitting
886
+ if (this.queueMode) {
887
+ this.queuedInputs.push(input);
888
+ this.clearInput();
889
+ this.emit('change', this.getValue());
890
+ return;
891
+ }
564
892
  // Check if it's a command
565
893
  if (input.startsWith('/')) {
566
894
  const parts = input.slice(1).split(/\s+/);
567
895
  const command = parts[0];
568
896
  const args = parts.slice(1).join(' ');
569
- this.finishInput({ action: 'command', command, args });
897
+ this.clearInput();
898
+ this.emit('command', command, args);
570
899
  }
571
900
  else {
572
- this.finishInput({ action: 'submit', value: input });
901
+ // Strip @ prefix from file paths (autocomplete uses @ but agent doesn't need it)
902
+ // Matches @word or @path/to/file patterns
903
+ const cleanedInput = input.replace(/(?<![@\w])@([\w./-]+)/g, '$1');
904
+ this.clearInput();
905
+ this.emit('submit', cleanedInput);
573
906
  }
574
907
  }
575
908
  handleTab() {
909
+ // Accept suggestion if present and input is empty
910
+ if (this.suggestion && this.getValue() === '') {
911
+ this.state.lines[0] = this.suggestion;
912
+ this.state.cursorPos = this.suggestion.length;
913
+ this.suggestion = null;
914
+ this.emit('change', this.getValue());
915
+ return;
916
+ }
917
+ // Agent autocomplete completion - replace with $agentId and add space
918
+ if (this.agentAutocomplete.active && this.agentAutocomplete.matches.length > 0) {
919
+ const selectedAgent = this.agentAutocomplete.matches[this.agentAutocomplete.selectedIndex];
920
+ const lineIdx = this.agentAutocomplete.lineIndex;
921
+ const line = this.state.lines[lineIdx];
922
+ const result = replaceDollarMention(line, this.state.cursorPos, selectedAgent.id);
923
+ this.state.lines[lineIdx] = result.input;
924
+ this.state.currentLine = lineIdx;
925
+ this.state.cursorPos = result.cursorPos;
926
+ this.agentAutocomplete.active = false;
927
+ this.agentAutocomplete.matches = [];
928
+ this.emit('change', this.getValue());
929
+ return;
930
+ }
931
+ // File autocomplete completion
932
+ if (this.fileAutocomplete.active && this.fileAutocomplete.matches.length > 0) {
933
+ const selectedFile = this.fileAutocomplete.matches[this.fileAutocomplete.selectedIndex];
934
+ const currentLine = this.state.lines[this.state.currentLine];
935
+ const result = replaceAtMention(currentLine, this.state.cursorPos, selectedFile.path);
936
+ this.state.lines[this.state.currentLine] = result.input;
937
+ this.state.cursorPos = result.cursorPos;
938
+ this.fileAutocomplete.active = false;
939
+ this.fileAutocomplete.matches = [];
940
+ this.updateAutocomplete(); // Check if still in @ context (e.g., directory selected)
941
+ this.emit('change', this.getValue());
942
+ return;
943
+ }
944
+ // Command autocomplete completion
576
945
  if (this.autocomplete.active && this.autocomplete.matches.length > 0) {
577
946
  this.state.lines[this.state.currentLine] =
578
947
  this.autocomplete.matches[this.autocomplete.selectedIndex].command;
579
948
  this.state.cursorPos = this.state.lines[this.state.currentLine].length;
580
949
  this.autocomplete.active = false;
581
950
  this.autocomplete.matches = [];
582
- this.clearDropdown();
583
951
  }
584
952
  else {
585
953
  // Insert 2 spaces
@@ -590,13 +958,37 @@ export class InputPrompt {
590
958
  this.historyIndex = -1;
591
959
  this.updateAutocomplete();
592
960
  }
593
- this.render();
961
+ this.emit('change', this.getValue());
594
962
  }
595
963
  handleArrowUp() {
596
- // Autocomplete navigation
964
+ // Agent autocomplete navigation
965
+ if (this.agentAutocomplete.active && this.agentAutocomplete.selectedIndex > 0) {
966
+ this.agentAutocomplete.selectedIndex--;
967
+ // Adjust scroll offset if selection goes above visible area
968
+ if (this.agentAutocomplete.selectedIndex < this.agentAutocomplete.scrollOffset) {
969
+ this.agentAutocomplete.scrollOffset = this.agentAutocomplete.selectedIndex;
970
+ }
971
+ this.emit('change', this.getValue());
972
+ return;
973
+ }
974
+ // File autocomplete navigation
975
+ if (this.fileAutocomplete.active && this.fileAutocomplete.selectedIndex > 0) {
976
+ this.fileAutocomplete.selectedIndex--;
977
+ // Adjust scroll offset if selection goes above visible area
978
+ if (this.fileAutocomplete.selectedIndex < this.fileAutocomplete.scrollOffset) {
979
+ this.fileAutocomplete.scrollOffset = this.fileAutocomplete.selectedIndex;
980
+ }
981
+ this.emit('change', this.getValue());
982
+ return;
983
+ }
984
+ // Command autocomplete navigation
597
985
  if (this.autocomplete.active && this.autocomplete.selectedIndex > 0) {
598
986
  this.autocomplete.selectedIndex--;
599
- this.render();
987
+ // Adjust scroll offset if selection goes above visible area
988
+ if (this.autocomplete.selectedIndex < this.autocomplete.scrollOffset) {
989
+ this.autocomplete.scrollOffset = this.autocomplete.selectedIndex;
990
+ }
991
+ this.emit('change', this.getValue());
600
992
  return;
601
993
  }
602
994
  const termWidth = terminal.getTerminalWidth();
@@ -609,7 +1001,7 @@ export class InputPrompt {
609
1001
  const newAbsPos = (currentPhysicalRow - 1) * termWidth + cursorColInRow;
610
1002
  this.state.cursorPos = Math.max(0, newAbsPos - currentLinePromptLen);
611
1003
  this.state.cursorPos = Math.min(this.state.cursorPos, this.state.lines[this.state.currentLine].length);
612
- this.render();
1004
+ this.emit('change', this.getValue());
613
1005
  return;
614
1006
  }
615
1007
  // Navigate to previous logical line
@@ -622,7 +1014,7 @@ export class InputPrompt {
622
1014
  const targetAbsPos = lastRowStart + cursorColInRow;
623
1015
  this.state.cursorPos = Math.max(0, targetAbsPos - prevLinePromptLen);
624
1016
  this.state.cursorPos = Math.min(this.state.cursorPos, prevLineLen);
625
- this.render();
1017
+ this.emit('change', this.getValue());
626
1018
  return;
627
1019
  }
628
1020
  // History navigation (at top of input)
@@ -636,17 +1028,45 @@ export class InputPrompt {
636
1028
  this.state.lines = [historyEntry];
637
1029
  this.state.currentLine = 0;
638
1030
  this.state.cursorPos = historyEntry.length;
639
- this.render();
1031
+ this.emit('change', this.getValue());
640
1032
  }
641
1033
  }
642
1034
  }
643
1035
  handleArrowDown() {
644
- // Autocomplete navigation
1036
+ // Agent autocomplete navigation
1037
+ if (this.agentAutocomplete.active &&
1038
+ this.agentAutocomplete.selectedIndex < this.agentAutocomplete.matches.length - 1) {
1039
+ this.agentAutocomplete.selectedIndex++;
1040
+ // Adjust scroll offset if selection goes below visible area
1041
+ const maxVisibleIndex = this.agentAutocomplete.scrollOffset + MAX_VISIBLE_COMMANDS - 1;
1042
+ if (this.agentAutocomplete.selectedIndex > maxVisibleIndex) {
1043
+ this.agentAutocomplete.scrollOffset = this.agentAutocomplete.selectedIndex - MAX_VISIBLE_COMMANDS + 1;
1044
+ }
1045
+ this.emit('change', this.getValue());
1046
+ return;
1047
+ }
1048
+ // File autocomplete navigation
1049
+ if (this.fileAutocomplete.active &&
1050
+ this.fileAutocomplete.selectedIndex < this.fileAutocomplete.matches.length - 1) {
1051
+ this.fileAutocomplete.selectedIndex++;
1052
+ // Adjust scroll offset if selection goes below visible area
1053
+ const maxVisibleIndex = this.fileAutocomplete.scrollOffset + MAX_VISIBLE_COMMANDS - 1;
1054
+ if (this.fileAutocomplete.selectedIndex > maxVisibleIndex) {
1055
+ this.fileAutocomplete.scrollOffset = this.fileAutocomplete.selectedIndex - MAX_VISIBLE_COMMANDS + 1;
1056
+ }
1057
+ this.emit('change', this.getValue());
1058
+ return;
1059
+ }
1060
+ // Command autocomplete navigation
645
1061
  if (this.autocomplete.active &&
646
- this.autocomplete.selectedIndex < this.autocomplete.matches.length - 1 &&
647
- this.autocomplete.selectedIndex < MAX_VISIBLE_COMMANDS - 1) {
1062
+ this.autocomplete.selectedIndex < this.autocomplete.matches.length - 1) {
648
1063
  this.autocomplete.selectedIndex++;
649
- this.render();
1064
+ // Adjust scroll offset if selection goes below visible area
1065
+ const maxVisibleIndex = this.autocomplete.scrollOffset + MAX_VISIBLE_COMMANDS - 1;
1066
+ if (this.autocomplete.selectedIndex > maxVisibleIndex) {
1067
+ this.autocomplete.scrollOffset = this.autocomplete.selectedIndex - MAX_VISIBLE_COMMANDS + 1;
1068
+ }
1069
+ this.emit('change', this.getValue());
650
1070
  return;
651
1071
  }
652
1072
  const termWidth = terminal.getTerminalWidth();
@@ -660,7 +1080,7 @@ export class InputPrompt {
660
1080
  const newAbsPos = (currentPhysicalRow + 1) * termWidth + cursorColInRow;
661
1081
  this.state.cursorPos = Math.max(0, newAbsPos - currentLinePromptLen);
662
1082
  this.state.cursorPos = Math.min(this.state.cursorPos, this.state.lines[this.state.currentLine].length);
663
- this.render();
1083
+ this.emit('change', this.getValue());
664
1084
  return;
665
1085
  }
666
1086
  // Navigate to next logical line
@@ -670,7 +1090,7 @@ export class InputPrompt {
670
1090
  const nextLineLen = this.state.lines[this.state.currentLine].length;
671
1091
  this.state.cursorPos = Math.max(0, cursorColInRow - nextLinePromptLen);
672
1092
  this.state.cursorPos = Math.min(this.state.cursorPos, nextLineLen);
673
- this.render();
1093
+ this.emit('change', this.getValue());
674
1094
  return;
675
1095
  }
676
1096
  // History forward
@@ -688,29 +1108,29 @@ export class InputPrompt {
688
1108
  this.state.cursorPos = historyEntry.length;
689
1109
  }
690
1110
  this.updateAutocomplete();
691
- this.render();
1111
+ this.emit('change', this.getValue());
692
1112
  }
693
1113
  }
694
1114
  handleArrowLeft() {
695
1115
  if (this.state.cursorPos > 0) {
696
1116
  this.state.cursorPos--;
697
- terminal.write('\x1B[D');
1117
+ this.emit('change', this.getValue());
698
1118
  }
699
1119
  else if (this.state.currentLine > 0) {
700
1120
  this.state.currentLine--;
701
1121
  this.state.cursorPos = this.state.lines[this.state.currentLine].length;
702
- this.render();
1122
+ this.emit('change', this.getValue());
703
1123
  }
704
1124
  }
705
1125
  handleArrowRight() {
706
1126
  if (this.state.cursorPos < this.state.lines[this.state.currentLine].length) {
707
1127
  this.state.cursorPos++;
708
- terminal.write('\x1B[C');
1128
+ this.emit('change', this.getValue());
709
1129
  }
710
1130
  else if (this.state.currentLine < this.state.lines.length - 1) {
711
1131
  this.state.currentLine++;
712
1132
  this.state.cursorPos = 0;
713
- this.render();
1133
+ this.emit('change', this.getValue());
714
1134
  }
715
1135
  }
716
1136
  handleWordLeft() {
@@ -722,12 +1142,12 @@ export class InputPrompt {
722
1142
  while (pos > 0 && line[pos - 1] !== ' ')
723
1143
  pos--;
724
1144
  this.state.cursorPos = pos;
725
- this.render();
1145
+ this.emit('change', this.getValue());
726
1146
  }
727
1147
  else if (this.state.currentLine > 0) {
728
1148
  this.state.currentLine--;
729
1149
  this.state.cursorPos = this.state.lines[this.state.currentLine].length;
730
- this.render();
1150
+ this.emit('change', this.getValue());
731
1151
  }
732
1152
  }
733
1153
  handleWordRight() {
@@ -739,12 +1159,12 @@ export class InputPrompt {
739
1159
  while (pos < line.length && line[pos] === ' ')
740
1160
  pos++;
741
1161
  this.state.cursorPos = pos;
742
- this.render();
1162
+ this.emit('change', this.getValue());
743
1163
  }
744
1164
  else if (this.state.currentLine < this.state.lines.length - 1) {
745
1165
  this.state.currentLine++;
746
1166
  this.state.cursorPos = 0;
747
- this.render();
1167
+ this.emit('change', this.getValue());
748
1168
  }
749
1169
  }
750
1170
  handleBackspace() {
@@ -755,7 +1175,7 @@ export class InputPrompt {
755
1175
  line.slice(0, this.state.cursorPos - 1) + line.slice(this.state.cursorPos);
756
1176
  this.state.cursorPos--;
757
1177
  this.updateAutocomplete();
758
- this.render();
1178
+ this.emit('change', this.getValue());
759
1179
  }
760
1180
  else if (this.state.currentLine > 0) {
761
1181
  const currentLine = this.state.lines[this.state.currentLine];
@@ -765,36 +1185,7 @@ export class InputPrompt {
765
1185
  this.state.currentLine--;
766
1186
  this.state.cursorPos = prevLine.length;
767
1187
  this.updateAutocomplete();
768
- this.render();
769
- }
770
- }
771
- finishInput(result) {
772
- // Clear display
773
- this.clearDropdown();
774
- if (this.linesAboveCursor > 0) {
775
- terminal.moveCursorToLineStart();
776
- terminal.moveCursorUp(this.linesAboveCursor);
777
- }
778
- terminal.clearToEndOfScreen();
779
- // Print clean input (without separators)
780
- for (let i = 0; i < this.state.lines.length; i++) {
781
- const linePrompt = i === 0 ? this.prompt : pc.dim(' \\ ');
782
- terminal.write(linePrompt + this.state.lines[i]);
783
- if (i < this.state.lines.length - 1) {
784
- terminal.write('\n');
785
- }
786
- }
787
- terminal.writeLine('');
788
- // Save to history if submitting
789
- if (result.action === 'submit' || result.action === 'command') {
790
- const input = this.getValue();
791
- this.addToHistory(input);
792
- }
793
- // Stop and resolve
794
- this.stop();
795
- if (this.resolveInput) {
796
- this.resolveInput(result);
797
- this.resolveInput = null;
1188
+ this.emit('change', this.getValue());
798
1189
  }
799
1190
  }
800
1191
  }