@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
package/dist/ui/footer.js CHANGED
@@ -17,7 +17,10 @@ import { MODE_INFO } from './types.js';
17
17
  import { getStyles } from '../themes/index.js';
18
18
  import * as terminal from './terminal.js';
19
19
  import { TodoZone } from './todo-zone.js';
20
- import { InputPrompt, DEFAULT_COMMANDS } from './input-prompt-v2.js';
20
+ import { InputPrompt, DEFAULT_COMMANDS } from './input-prompt.js';
21
+ import { SubagentRenderer } from './subagent-renderer.js';
22
+ import { debugLog, debugRender } from '../utils/debug-log.js';
23
+ import { getPhysicalLineCount, getVisibleLength } from './line-utils.js';
21
24
  // =============================================================================
22
25
  // Footer Class
23
26
  // =============================================================================
@@ -25,13 +28,17 @@ export class Footer extends EventEmitter {
25
28
  // Child components
26
29
  todoZone;
27
30
  inputPrompt;
31
+ subagentRenderer;
28
32
  // State
29
33
  agentRunning = false;
30
34
  mode;
31
35
  lastRenderHeight = 0;
32
36
  isRunning = false;
33
37
  isPaused = false; // Prevents rendering even if callback is queued
38
+ isRendering = false; // Prevents re-entrant renders
34
39
  cursorLineFromBottom = 0; // Track cursor position for clear()
40
+ projectName = null; // Current active project name
41
+ externalRendererEnabled = false; // When true, skip internal render loop
35
42
  // Render loop
36
43
  renderInterval;
37
44
  renderTimer = null;
@@ -48,10 +55,15 @@ export class Footer extends EventEmitter {
48
55
  showSeparators: options.showSeparators ?? true,
49
56
  commands: DEFAULT_COMMANDS,
50
57
  });
58
+ this.subagentRenderer = new SubagentRenderer();
51
59
  // Wire up TodoZone animation callback
52
60
  this.todoZone.setAnimationCallback(() => {
53
61
  this.needsRender = true;
54
62
  });
63
+ // Wire up SubagentRenderer update callback
64
+ this.subagentRenderer.setUpdateCallback(() => {
65
+ this.needsRender = true;
66
+ });
55
67
  // Wire up InputPrompt events
56
68
  this.inputPrompt.on('submit', (input) => {
57
69
  this.emit('submit', input);
@@ -71,6 +83,11 @@ export class Footer extends EventEmitter {
71
83
  this.inputPrompt.on('change', () => {
72
84
  this.needsRender = true;
73
85
  });
86
+ // Toggle subagent expand on Ctrl+O
87
+ this.inputPrompt.on('toggleSubagentExpand', () => {
88
+ this.subagentRenderer.toggleExpand();
89
+ this.needsRender = true;
90
+ });
74
91
  }
75
92
  // ===========================================================================
76
93
  // Public API
@@ -88,7 +105,7 @@ export class Footer extends EventEmitter {
88
105
  // Start render loop
89
106
  this.render();
90
107
  this.renderTimer = setInterval(() => {
91
- if (this.needsRender && !this.isPaused) {
108
+ if (this.isRunning && this.needsRender && !this.isPaused) {
92
109
  this.render();
93
110
  this.needsRender = false;
94
111
  }
@@ -121,14 +138,31 @@ export class Footer extends EventEmitter {
121
138
  this.agentRunning = running;
122
139
  this.todoZone.setAgentRunning(running);
123
140
  this.inputPrompt.setQueueMode(running);
124
- // When stopping, immediately clear and re-render to remove spinner
125
- // This prevents ghost spinner from render interval races
126
- if (wasRunning && !running) {
127
- this.clear();
128
- this.render();
141
+ // When starting a new run, clear previous subagents
142
+ if (!wasRunning && running) {
143
+ this.subagentRenderer.clear();
144
+ // When external renderer manages rendering, just mark needsRender
145
+ if (this.externalRendererEnabled) {
146
+ this.needsRender = true;
147
+ }
148
+ else {
149
+ // Legacy mode: IMMEDIATELY render so lastRenderHeight includes spinner
150
+ this.clear();
151
+ this.render();
152
+ }
129
153
  }
130
- else {
131
- this.needsRender = true;
154
+ // When stopping, clear subagents
155
+ if (wasRunning && !running) {
156
+ this.subagentRenderer.clear();
157
+ // When external renderer manages rendering, just mark needsRender
158
+ if (this.externalRendererEnabled) {
159
+ this.needsRender = true;
160
+ }
161
+ else {
162
+ // Legacy mode: immediately re-render to remove spinner
163
+ this.clear();
164
+ this.render();
165
+ }
132
166
  }
133
167
  }
134
168
  /**
@@ -150,6 +184,19 @@ export class Footer extends EventEmitter {
150
184
  getMode() {
151
185
  return this.mode;
152
186
  }
187
+ /**
188
+ * Set the current project name (displayed in footer)
189
+ */
190
+ setProjectName(name) {
191
+ this.projectName = name;
192
+ this.needsRender = true;
193
+ }
194
+ /**
195
+ * Get the current project name
196
+ */
197
+ getProjectName() {
198
+ return this.projectName;
199
+ }
153
200
  /**
154
201
  * Update the todo list
155
202
  */
@@ -185,6 +232,69 @@ export class Footer extends EventEmitter {
185
232
  addTokens(count) {
186
233
  this.todoZone.addTokens(count);
187
234
  }
235
+ // ===========================================================================
236
+ // Subagent API
237
+ // ===========================================================================
238
+ /**
239
+ * Called when a subagent starts
240
+ */
241
+ onSubagentStart(agentId, agentType, description) {
242
+ this.subagentRenderer.onSubagentStart(agentId, agentType, description);
243
+ }
244
+ /**
245
+ * Called when a subagent uses a tool
246
+ */
247
+ onSubagentToolUse(agentId, toolName, summary) {
248
+ this.subagentRenderer.onToolUse(agentId, toolName, summary);
249
+ }
250
+ /**
251
+ * Called when a subagent completes
252
+ *
253
+ * CRITICAL (legacy mode only): We must pause rendering immediately to prevent a race condition.
254
+ * The render loop could fire between onSubagentEnd and clearForOutput, which would:
255
+ * 1. See the subagent as 'done'
256
+ * 2. Render with 0 subagent lines
257
+ * 3. Update lastRenderHeight to a smaller value
258
+ * 4. Then clearForOutput would not clear enough lines → ghost lines!
259
+ *
260
+ * In external renderer mode: No pause needed since the internal render loop is disabled.
261
+ */
262
+ onSubagentEnd(agentId, success, tokenCount, error) {
263
+ // Only pause in legacy mode (internal render loop is running)
264
+ // In external renderer mode, the loop is already stopped
265
+ if (!this.externalRendererEnabled) {
266
+ // Pause rendering BEFORE updating state to prevent race condition
267
+ // The caller (repl.ts) will call clearForOutput() and forceRender() which will unpause
268
+ this.isPaused = true;
269
+ }
270
+ this.subagentRenderer.onSubagentEnd(agentId, success, tokenCount, error);
271
+ this.needsRender = true;
272
+ }
273
+ /**
274
+ * Toggle expanded view for subagents (Ctrl+O)
275
+ */
276
+ toggleSubagentExpand() {
277
+ this.subagentRenderer.toggleExpand();
278
+ }
279
+ /**
280
+ * Check if there are active subagents
281
+ */
282
+ hasActiveSubagents() {
283
+ return this.subagentRenderer.hasActiveSubagents();
284
+ }
285
+ /**
286
+ * Clear subagent tracking (after completion displayed)
287
+ */
288
+ clearSubagents() {
289
+ this.subagentRenderer.clear();
290
+ this.needsRender = true;
291
+ }
292
+ /**
293
+ * Get the last subagent's stats before clearing
294
+ */
295
+ getLastSubagentStats() {
296
+ return this.subagentRenderer.getLastAgentStats();
297
+ }
188
298
  /**
189
299
  * Get queued inputs
190
300
  */
@@ -217,22 +327,58 @@ export class Footer extends EventEmitter {
217
327
  /**
218
328
  * Clear footer before scrolling output
219
329
  * Call this before writing to the scrolling zone
330
+ * Pauses interval rendering to prevent ghost lines during output
331
+ *
332
+ * When external renderer is enabled (scroll region mode):
333
+ * - Footer doesn't need clearing (it's in a separate scroll region)
334
+ * - Just mark that we need a re-render
220
335
  */
221
336
  clearForOutput() {
337
+ // When external renderer manages rendering, footer is in a fixed region
338
+ // and doesn't need to be cleared before output
339
+ if (this.externalRendererEnabled) {
340
+ debugRender('Footer:clearForOutput', 'external renderer mode - skipping clear');
341
+ return;
342
+ }
343
+ // Pause interval rendering to prevent ghost lines during output
344
+ this.isPaused = true;
345
+ debugRender('Footer:clearForOutput', `lastRenderHeight=${String(this.lastRenderHeight)}`);
346
+ // Clear the footer
222
347
  this.clear();
223
348
  }
224
349
  /**
225
350
  * Force an immediate render
351
+ * Resumes interval rendering after clearForOutput()
352
+ *
353
+ * When external renderer is enabled (scroll region mode):
354
+ * - Just mark that we need a re-render (TerminalRenderer's loop will pick it up)
226
355
  */
227
356
  forceRender() {
357
+ // When external renderer manages rendering, just mark that we need re-render
358
+ if (this.externalRendererEnabled) {
359
+ debugRender('Footer:forceRender', 'external renderer mode - marking needsRender');
360
+ this.needsRender = true;
361
+ return;
362
+ }
363
+ // Resume interval rendering BEFORE calling render (so render() isn't skipped)
364
+ this.isPaused = false;
228
365
  this.render();
229
366
  this.needsRender = false;
230
367
  }
368
+ /**
369
+ * Restart input prompt after a command completes
370
+ * Use this for non-overlay commands where finishInput() stopped the prompt
371
+ */
372
+ restartInput() {
373
+ this.inputPrompt.start();
374
+ this.render();
375
+ }
231
376
  /**
232
377
  * Pause footer completely (for overlays)
233
378
  * Stops render loop, input capture, and animation
234
379
  */
235
380
  pauseAnimation() {
381
+ debugLog('Footer:pauseAnimation', 'start');
236
382
  // Set paused flag FIRST - this prevents any queued render callbacks from executing
237
383
  this.isPaused = true;
238
384
  // Stop render loop
@@ -240,12 +386,16 @@ export class Footer extends EventEmitter {
240
386
  clearInterval(this.renderTimer);
241
387
  this.renderTimer = null;
242
388
  }
389
+ debugLog('Footer:pauseAnimation', 'render loop stopped');
243
390
  // Stop input capture
244
391
  this.inputPrompt.stop();
392
+ debugLog('Footer:pauseAnimation', 'input stopped');
245
393
  // Stop animation
246
394
  this.todoZone.pauseAnimation();
395
+ debugLog('Footer:pauseAnimation', 'animation stopped');
247
396
  // Clear footer from screen
248
397
  this.clear();
398
+ debugLog('Footer:pauseAnimation', 'footer cleared');
249
399
  }
250
400
  /**
251
401
  * Resume footer after pause
@@ -261,7 +411,7 @@ export class Footer extends EventEmitter {
261
411
  // Restart render loop
262
412
  this.render();
263
413
  this.renderTimer = setInterval(() => {
264
- if (this.needsRender && !this.isPaused) {
414
+ if (this.isRunning && this.needsRender && !this.isPaused) {
265
415
  this.render();
266
416
  this.needsRender = false;
267
417
  }
@@ -273,6 +423,99 @@ export class Footer extends EventEmitter {
273
423
  getLastRenderHeight() {
274
424
  return this.lastRenderHeight;
275
425
  }
426
+ /**
427
+ * Get all footer lines for rendering (for TerminalRenderer integration)
428
+ * Returns the lines WITHOUT writing to terminal
429
+ */
430
+ getFooterLines() {
431
+ const allLines = [];
432
+ const s = getStyles();
433
+ // 1. Subagent zone (only if agent is running)
434
+ if (this.agentRunning) {
435
+ const subagentLines = this.subagentRenderer.render();
436
+ allLines.push(...subagentLines);
437
+ }
438
+ // 2. Todo zone (spinner + todos)
439
+ const todoLines = this.todoZone.render();
440
+ allLines.push(...todoLines);
441
+ // 3. Queued inputs
442
+ const queuedInputs = this.inputPrompt.getQueuedInputs();
443
+ for (const queued of queuedInputs) {
444
+ allLines.push(s.muted(`queued: "${queued}"`));
445
+ }
446
+ // 4. Input prompt
447
+ const inputLines = this.inputPrompt.render();
448
+ allLines.push(...inputLines);
449
+ // 5. Mode indicator (below input, before autocomplete)
450
+ allLines.push(this.renderModeIndicator());
451
+ // 6. Autocomplete dropdown (if active)
452
+ const autocompleteLines = this.inputPrompt.getAutocompleteLines();
453
+ allLines.push(...autocompleteLines);
454
+ return allLines;
455
+ }
456
+ /**
457
+ * Get cursor position within footer content (for TerminalRenderer)
458
+ * Returns { row, col } where row is 0-indexed from start of footer lines
459
+ */
460
+ getFooterCursorPosition() {
461
+ const cursorInfo = this.inputPrompt.getCursorInfo();
462
+ // Calculate lines before input prompt
463
+ let linesBeforeInput = 0;
464
+ // Subagent lines
465
+ if (this.agentRunning) {
466
+ linesBeforeInput += this.subagentRenderer.render().length;
467
+ }
468
+ // Todo lines
469
+ linesBeforeInput += this.todoZone.render().length;
470
+ // Queued inputs
471
+ linesBeforeInput += this.inputPrompt.getQueuedInputs().length;
472
+ // cursorInfo.row is 1-indexed (row 1 = first line after separator)
473
+ // Convert to 0-indexed row within footer
474
+ const row = linesBeforeInput + cursorInfo.row - 1;
475
+ return { row, col: cursorInfo.col };
476
+ }
477
+ /**
478
+ * Enable external renderer mode (TerminalRenderer manages rendering)
479
+ * When enabled:
480
+ * - Footer's internal render loop is disabled
481
+ * - Input capture remains active
482
+ * - Event forwarding remains active
483
+ * - TerminalRenderer calls getFooterLines() and renders
484
+ */
485
+ setExternalRenderer(enabled) {
486
+ this.externalRendererEnabled = enabled;
487
+ if (enabled && this.renderTimer) {
488
+ // Stop the internal render loop (TerminalRenderer will handle rendering)
489
+ clearInterval(this.renderTimer);
490
+ this.renderTimer = null;
491
+ debugLog('Footer:setExternalRenderer', 'enabled - internal render loop stopped');
492
+ }
493
+ else if (!enabled && this.isRunning && !this.renderTimer) {
494
+ // Restart the internal render loop
495
+ this.renderTimer = setInterval(() => {
496
+ if (this.isRunning && this.needsRender && !this.isPaused) {
497
+ this.render();
498
+ this.needsRender = false;
499
+ }
500
+ }, this.renderInterval);
501
+ debugLog('Footer:setExternalRenderer', 'disabled - internal render loop restarted');
502
+ }
503
+ }
504
+ /**
505
+ * Check if external renderer is enabled
506
+ */
507
+ isExternalRendererEnabled() {
508
+ return this.externalRendererEnabled;
509
+ }
510
+ /**
511
+ * Request a render from external renderer
512
+ * Call this to notify TerminalRenderer that footer needs re-rendering
513
+ */
514
+ requestExternalRender() {
515
+ this.needsRender = true;
516
+ // External renderer will poll this or listen for events
517
+ // For now, just set the flag
518
+ }
276
519
  /**
277
520
  * Refresh the prompt with current theme colors
278
521
  * Call after theme changes to apply new colors immediately
@@ -287,17 +530,26 @@ export class Footer extends EventEmitter {
287
530
  // ===========================================================================
288
531
  /**
289
532
  * Clear the footer area
533
+ *
534
+ * Strategy: Simply move to the top of the CURRENT footer and clear to end.
535
+ * - When footer SHRINKS: clearToEndOfScreen handles the extra lines
536
+ * - When footer GROWS: terminal scrolls naturally when we write more lines
537
+ *
538
+ * We NEVER move up into the scrolling zone - that would eat content!
290
539
  */
291
540
  clear() {
541
+ debugRender('Footer:clear', `lastHeight=${String(this.lastRenderHeight)} cursorFromBottom=${String(this.cursorLineFromBottom)}`);
292
542
  if (this.lastRenderHeight > 0) {
293
543
  // Move to start of current line
294
544
  terminal.moveCursorToLineStart();
295
- // First move DOWN to the last line of footer (cursor may be positioned in input area)
296
- if (this.cursorLineFromBottom > 0) {
297
- terminal.moveCursorDown(this.cursorLineFromBottom);
545
+ // Calculate DIRECT movement to top of footer
546
+ const cursorRowFromTop = this.lastRenderHeight - this.cursorLineFromBottom;
547
+ const rowsToMoveUp = cursorRowFromTop - 1;
548
+ debugRender('Footer:clear', `cursorRowFromTop=${String(cursorRowFromTop)} rowsToMoveUp=${String(rowsToMoveUp)}`);
549
+ if (rowsToMoveUp > 0) {
550
+ terminal.moveCursorUp(rowsToMoveUp);
298
551
  }
299
- // Now move up to the first line of footer
300
- terminal.moveCursorUp(this.lastRenderHeight - 1);
552
+ // Clear from here to end of screen
301
553
  terminal.clearToEndOfScreen();
302
554
  }
303
555
  this.lastRenderHeight = 0;
@@ -307,47 +559,68 @@ export class Footer extends EventEmitter {
307
559
  * Render the entire footer
308
560
  */
309
561
  render() {
310
- // Clear and redraw footer
311
- this.clear();
312
- const allLines = [];
313
- // 1. Todo zone (spinner + todos)
314
- const todoLines = this.todoZone.render();
315
- allLines.push(...todoLines);
316
- // 2. Queued inputs
317
- const s = getStyles();
318
- const queuedInputs = this.inputPrompt.getQueuedInputs();
319
- for (const queued of queuedInputs) {
320
- allLines.push(s.muted(`queued: "${queued}"`));
321
- }
322
- // 3. Input prompt
323
- const inputLines = this.inputPrompt.render();
324
- allLines.push(...inputLines);
325
- // 4. Mode indicator (below input, before autocomplete)
326
- allLines.push(this.renderModeIndicator());
327
- // 5. Autocomplete dropdown (if active)
328
- const autocompleteLines = this.inputPrompt.getAutocompleteLines();
329
- allLines.push(...autocompleteLines);
330
- // Write all lines
331
- for (let i = 0; i < allLines.length; i++) {
332
- terminal.write(allLines[i]);
333
- if (i < allLines.length - 1) {
334
- terminal.write('\n');
562
+ // Don't render if footer has been stopped, we're paused, or already rendering
563
+ if (!this.isRunning || this.isPaused || this.isRendering)
564
+ return;
565
+ // Prevent re-entrant renders
566
+ this.isRendering = true;
567
+ try {
568
+ // Build all lines FIRST to know new height before clearing
569
+ const allLines = [];
570
+ let subagentLineCount = 0;
571
+ // 1. Subagent zone (only if agent is running - prevents showing after completion)
572
+ if (this.agentRunning) {
573
+ const subagentLines = this.subagentRenderer.render();
574
+ subagentLineCount = subagentLines.length;
575
+ allLines.push(...subagentLines);
335
576
  }
577
+ // 2. Todo zone (spinner + todos)
578
+ const todoLines = this.todoZone.render();
579
+ allLines.push(...todoLines);
580
+ // 3. Queued inputs
581
+ const s = getStyles();
582
+ const queuedInputs = this.inputPrompt.getQueuedInputs();
583
+ for (const queued of queuedInputs) {
584
+ allLines.push(s.muted(`queued: "${queued}"`));
585
+ }
586
+ // 4. Input prompt
587
+ const inputLines = this.inputPrompt.render();
588
+ allLines.push(...inputLines);
589
+ // 5. Mode indicator (below input, before autocomplete)
590
+ allLines.push(this.renderModeIndicator());
591
+ // 6. Autocomplete dropdown (if active)
592
+ const autocompleteLines = this.inputPrompt.getAutocompleteLines();
593
+ allLines.push(...autocompleteLines);
594
+ // Calculate new height BEFORE clearing (to clear extra lines if growing)
595
+ const termWidth = process.stdout.columns || 80;
596
+ let newHeight = 0;
597
+ const lineHeights = [];
598
+ for (const line of allLines) {
599
+ // Use unified line-utils for consistent height calculation
600
+ const lineHeight = getPhysicalLineCount(line, termWidth);
601
+ lineHeights.push(lineHeight);
602
+ newHeight += lineHeight;
603
+ }
604
+ debugRender('Footer:render', `subagent=${String(subagentLineCount)} todo=${String(todoLines.length)} queued=${String(queuedInputs.length)} input=${String(inputLines.length)} autocomplete=${String(autocompleteLines.length)}`);
605
+ debugRender('Footer:render', `termWidth=${String(termWidth)} lineHeights=[${lineHeights.join(',')}] newHeight=${String(newHeight)}`);
606
+ // Clear the current footer (terminal scrolls naturally if we write more lines)
607
+ this.clear();
608
+ // Write all lines
609
+ for (let i = 0; i < allLines.length; i++) {
610
+ terminal.write(allLines[i]);
611
+ if (i < allLines.length - 1) {
612
+ terminal.write('\n');
613
+ }
614
+ }
615
+ // Track height for next clear
616
+ this.lastRenderHeight = newHeight;
617
+ debugRender('Footer:render', `lastRenderHeight set to ${String(newHeight)}`);
618
+ // Position cursor within input prompt
619
+ this.positionCursor(allLines, subagentLineCount + todoLines.length + queuedInputs.length);
336
620
  }
337
- // Track height - account for line wrapping in terminal
338
- const termWidth = process.stdout.columns || 80;
339
- let actualTerminalRows = 0;
340
- for (const line of allLines) {
341
- // Strip ANSI codes to get visible length
342
- // eslint-disable-next-line no-control-regex
343
- const visibleLength = line.replace(/\x1b\[[0-9;]*m/g, '').length;
344
- // Each line takes at least 1 row, plus extra rows if it wraps
345
- actualTerminalRows += Math.max(1, Math.ceil(visibleLength / termWidth));
621
+ finally {
622
+ this.isRendering = false;
346
623
  }
347
- this.lastRenderHeight = actualTerminalRows;
348
- // Position cursor within input prompt
349
- // linesBeforeInput = todoLines + queuedInputs (mode indicator is now AFTER input)
350
- this.positionCursor(allLines, todoLines.length + queuedInputs.length);
351
624
  }
352
625
  /**
353
626
  * Render mode indicator line
@@ -355,30 +628,43 @@ export class Footer extends EventEmitter {
355
628
  renderModeIndicator() {
356
629
  const s = getStyles();
357
630
  const modeInfo = MODE_INFO[this.mode];
631
+ const termWidth = process.stdout.columns || 80;
632
+ // Build left side (mode indicator)
633
+ let leftPart;
358
634
  switch (this.mode) {
359
635
  case 'normal':
360
- // Subtle indicator in normal mode
361
- return s.muted(`mode: ${modeInfo.label} (Shift+Tab to change)`);
636
+ leftPart = s.muted(`mode: ${modeInfo.label} (Shift+Tab to change)`);
637
+ break;
362
638
  case 'auto-accept':
363
- // Warning color for auto-accept
364
- return s.warning(`⚡ mode: ${modeInfo.label}`) + s.muted(' (Shift+Tab to change)');
639
+ leftPart = s.warning(`⚡ mode: ${modeInfo.label}`) + s.muted(' (Shift+Tab to change)');
640
+ break;
365
641
  case 'plan':
366
- // Different color for plan mode
367
- return s.info(`📋 mode: ${modeInfo.label}`) + s.muted(' (Shift+Tab to change)');
642
+ leftPart = s.info(`📋 mode: ${modeInfo.label}`) + s.muted(' (Shift+Tab to change)');
643
+ break;
368
644
  default:
369
- return s.muted(`mode: ${modeInfo.label} (Shift+Tab to change)`);
645
+ leftPart = s.muted(`mode: ${modeInfo.label} (Shift+Tab to change)`);
646
+ }
647
+ // If no project, just return the mode indicator
648
+ if (!this.projectName) {
649
+ return leftPart;
370
650
  }
651
+ // Build right side (project name)
652
+ const projectText = `Project: ${this.projectName}`;
653
+ const rightPart = s.muted(projectText);
654
+ // Calculate visible lengths (using unified line-utils)
655
+ const leftVisible = getVisibleLength(leftPart);
656
+ const rightVisible = projectText.length;
657
+ // Calculate padding to right-align project name
658
+ const padding = Math.max(2, termWidth - leftVisible - rightVisible);
659
+ return leftPart + ' '.repeat(padding) + rightPart;
371
660
  }
372
661
  /**
373
662
  * Calculate how many terminal rows a line takes (accounting for wrapping)
663
+ * Uses unified line-utils for consistent calculation across all components.
374
664
  */
375
665
  getTerminalRowsForLine(line) {
376
666
  const termWidth = process.stdout.columns || 80;
377
- // Strip ANSI codes to get visible length
378
- // eslint-disable-next-line no-control-regex
379
- const visibleLength = line.replace(/\x1b\[[0-9;]*m/g, '').length;
380
- // Each line takes at least 1 row, plus extra rows if it wraps
381
- return Math.max(1, Math.ceil(visibleLength / termWidth));
667
+ return getPhysicalLineCount(line, termWidth);
382
668
  }
383
669
  /**
384
670
  * Position cursor correctly within the input prompt
@@ -406,11 +692,17 @@ export class Footer extends EventEmitter {
406
692
  for (let i = inputEndIndex; i < allLines.length; i++) {
407
693
  rowsAfterInput += this.getTerminalRowsForLine(allLines[i]);
408
694
  }
409
- // Cursor target row from start of footer = rows before input + cursor row within input
695
+ // Cursor target row from start of footer (1-indexed) = rows before input + cursor row within input
410
696
  const cursorRowFromStart = rowsBeforeInput + cursorInfo.row;
411
- // Total rows to move up from end = total rows - cursor row - 1
697
+ // Total rows in footer
412
698
  const totalRows = rowsBeforeInput + inputTotalRows + rowsAfterInput;
699
+ // After writing all lines, cursor is at the END of the last row (row totalRows).
700
+ // cursorInfo.row starts at 1 (after separator), so actual footer row = cursorRowFromStart + 1
701
+ // To move to actual row, we move up: totalRows - (cursorRowFromStart + 1) = totalRows - cursorRowFromStart - 1
702
+ // Example: totalRows=7, cursorRowFromStart=4, actual row=5 → move up 2 rows (from row 7 to row 5)
413
703
  const rowsToMoveUp = totalRows - cursorRowFromStart - 1;
704
+ debugRender('Footer:positionCursor', `cursorInfo={row:${String(cursorInfo.row)},col:${String(cursorInfo.col)}} rowsBefore=${String(rowsBeforeInput)} inputRows=${String(inputTotalRows)} rowsAfter=${String(rowsAfterInput)}`);
705
+ debugRender('Footer:positionCursor', `totalRows=${String(totalRows)} cursorRowFromStart=${String(cursorRowFromStart)} rowsToMoveUp=${String(rowsToMoveUp)}`);
414
706
  if (rowsToMoveUp > 0) {
415
707
  terminal.moveCursorUp(rowsToMoveUp);
416
708
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Guardrail Confirmation Overlay
3
+ *
4
+ * Shows when a guardrail with action='confirm' is triggered.
5
+ * User must approve or deny the risky operation.
6
+ */
7
+ export interface GuardrailConfirmOptions {
8
+ /** Guardrail name */
9
+ name: string;
10
+ /** Guardrail description/message */
11
+ message: string;
12
+ /** Action type (for display) */
13
+ action: 'warn' | 'confirm' | 'block';
14
+ /** Tool that triggered the guardrail */
15
+ toolName: string;
16
+ /** The input that matched the pattern */
17
+ matchedInput: string;
18
+ /** Category (e.g., 'git', 'filesystem', 'database') */
19
+ category?: string;
20
+ }
21
+ export interface GuardrailConfirmResult {
22
+ /** Whether to proceed with the operation */
23
+ approved: boolean;
24
+ }
25
+ /**
26
+ * Show the guardrail confirmation overlay.
27
+ * Returns { approved: true } if user allows, { approved: false } if denied.
28
+ */
29
+ export declare function showGuardrailOverlay(options: GuardrailConfirmOptions): Promise<GuardrailConfirmResult>;