erosolar-cli 1.7.355 → 1.7.356

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 (330) hide show
  1. package/README.md +148 -24
  2. package/dist/alpha-zero/agentWrapper.d.ts +84 -0
  3. package/dist/alpha-zero/agentWrapper.d.ts.map +1 -0
  4. package/dist/alpha-zero/agentWrapper.js +171 -0
  5. package/dist/alpha-zero/agentWrapper.js.map +1 -0
  6. package/dist/alpha-zero/codeEvaluator.d.ts +25 -0
  7. package/dist/alpha-zero/codeEvaluator.d.ts.map +1 -0
  8. package/dist/alpha-zero/codeEvaluator.js +273 -0
  9. package/dist/alpha-zero/codeEvaluator.js.map +1 -0
  10. package/dist/alpha-zero/competitiveRunner.d.ts +66 -0
  11. package/dist/alpha-zero/competitiveRunner.d.ts.map +1 -0
  12. package/dist/alpha-zero/competitiveRunner.js +224 -0
  13. package/dist/alpha-zero/competitiveRunner.js.map +1 -0
  14. package/dist/alpha-zero/index.d.ts +67 -0
  15. package/dist/alpha-zero/index.d.ts.map +1 -0
  16. package/dist/alpha-zero/index.js +99 -0
  17. package/dist/alpha-zero/index.js.map +1 -0
  18. package/dist/alpha-zero/introspection.d.ts +128 -0
  19. package/dist/alpha-zero/introspection.d.ts.map +1 -0
  20. package/dist/alpha-zero/introspection.js +300 -0
  21. package/dist/alpha-zero/introspection.js.map +1 -0
  22. package/dist/alpha-zero/metricsTracker.d.ts +71 -0
  23. package/dist/alpha-zero/metricsTracker.d.ts.map +1 -0
  24. package/dist/{core → alpha-zero}/metricsTracker.js +5 -2
  25. package/dist/alpha-zero/metricsTracker.js.map +1 -0
  26. package/dist/alpha-zero/security/core.d.ts +125 -0
  27. package/dist/alpha-zero/security/core.d.ts.map +1 -0
  28. package/dist/alpha-zero/security/core.js +271 -0
  29. package/dist/alpha-zero/security/core.js.map +1 -0
  30. package/dist/alpha-zero/security/google.d.ts +125 -0
  31. package/dist/alpha-zero/security/google.d.ts.map +1 -0
  32. package/dist/alpha-zero/security/google.js +311 -0
  33. package/dist/alpha-zero/security/google.js.map +1 -0
  34. package/dist/alpha-zero/security/googleLoader.d.ts +17 -0
  35. package/dist/alpha-zero/security/googleLoader.d.ts.map +1 -0
  36. package/dist/alpha-zero/security/googleLoader.js +41 -0
  37. package/dist/alpha-zero/security/googleLoader.js.map +1 -0
  38. package/dist/alpha-zero/security/index.d.ts +29 -0
  39. package/dist/alpha-zero/security/index.d.ts.map +1 -0
  40. package/dist/alpha-zero/security/index.js +32 -0
  41. package/dist/alpha-zero/security/index.js.map +1 -0
  42. package/dist/alpha-zero/security/simulation.d.ts +124 -0
  43. package/dist/alpha-zero/security/simulation.d.ts.map +1 -0
  44. package/dist/alpha-zero/security/simulation.js +277 -0
  45. package/dist/alpha-zero/security/simulation.js.map +1 -0
  46. package/dist/alpha-zero/selfModification.d.ts +109 -0
  47. package/dist/alpha-zero/selfModification.d.ts.map +1 -0
  48. package/dist/alpha-zero/selfModification.js +233 -0
  49. package/dist/alpha-zero/selfModification.js.map +1 -0
  50. package/dist/alpha-zero/types.d.ts +170 -0
  51. package/dist/alpha-zero/types.d.ts.map +1 -0
  52. package/dist/alpha-zero/types.js +31 -0
  53. package/dist/alpha-zero/types.js.map +1 -0
  54. package/dist/bin/erosolar.js +21 -5
  55. package/dist/bin/erosolar.js.map +1 -1
  56. package/dist/capabilities/agentSpawningCapability.d.ts.map +1 -1
  57. package/dist/capabilities/agentSpawningCapability.js +31 -56
  58. package/dist/capabilities/agentSpawningCapability.js.map +1 -1
  59. package/dist/capabilities/securityTestingCapability.d.ts +13 -0
  60. package/dist/capabilities/securityTestingCapability.d.ts.map +1 -0
  61. package/dist/capabilities/securityTestingCapability.js +25 -0
  62. package/dist/capabilities/securityTestingCapability.js.map +1 -0
  63. package/dist/contracts/agent-schemas.json +15 -0
  64. package/dist/contracts/tools.schema.json +9 -0
  65. package/dist/core/agent.d.ts +2 -2
  66. package/dist/core/agent.d.ts.map +1 -1
  67. package/dist/core/agent.js.map +1 -1
  68. package/dist/core/aiFlowOptimizer.d.ts +26 -0
  69. package/dist/core/aiFlowOptimizer.d.ts.map +1 -0
  70. package/dist/core/aiFlowOptimizer.js +31 -0
  71. package/dist/core/aiFlowOptimizer.js.map +1 -0
  72. package/dist/core/aiOptimizationEngine.d.ts +158 -0
  73. package/dist/core/aiOptimizationEngine.d.ts.map +1 -0
  74. package/dist/core/aiOptimizationEngine.js +428 -0
  75. package/dist/core/aiOptimizationEngine.js.map +1 -0
  76. package/dist/core/aiOptimizationIntegration.d.ts +93 -0
  77. package/dist/core/aiOptimizationIntegration.d.ts.map +1 -0
  78. package/dist/core/aiOptimizationIntegration.js +250 -0
  79. package/dist/core/aiOptimizationIntegration.js.map +1 -0
  80. package/dist/core/customCommands.d.ts +0 -1
  81. package/dist/core/customCommands.d.ts.map +1 -1
  82. package/dist/core/customCommands.js +0 -3
  83. package/dist/core/customCommands.js.map +1 -1
  84. package/dist/core/enhancedErrorRecovery.d.ts +100 -0
  85. package/dist/core/enhancedErrorRecovery.d.ts.map +1 -0
  86. package/dist/core/enhancedErrorRecovery.js +345 -0
  87. package/dist/core/enhancedErrorRecovery.js.map +1 -0
  88. package/dist/core/hooksSystem.d.ts +65 -0
  89. package/dist/core/hooksSystem.d.ts.map +1 -0
  90. package/dist/core/hooksSystem.js +273 -0
  91. package/dist/core/hooksSystem.js.map +1 -0
  92. package/dist/core/memorySystem.d.ts +48 -0
  93. package/dist/core/memorySystem.d.ts.map +1 -0
  94. package/dist/core/memorySystem.js +271 -0
  95. package/dist/core/memorySystem.js.map +1 -0
  96. package/dist/core/sessionStore.d.ts +0 -2
  97. package/dist/core/sessionStore.d.ts.map +1 -1
  98. package/dist/core/sessionStore.js +0 -1
  99. package/dist/core/sessionStore.js.map +1 -1
  100. package/dist/core/toolPreconditions.d.ts.map +1 -1
  101. package/dist/core/toolPreconditions.js +14 -0
  102. package/dist/core/toolPreconditions.js.map +1 -1
  103. package/dist/core/toolRuntime.d.ts +1 -22
  104. package/dist/core/toolRuntime.d.ts.map +1 -1
  105. package/dist/core/toolRuntime.js +5 -0
  106. package/dist/core/toolRuntime.js.map +1 -1
  107. package/dist/core/toolValidation.d.ts.map +1 -1
  108. package/dist/core/toolValidation.js +3 -14
  109. package/dist/core/toolValidation.js.map +1 -1
  110. package/dist/core/unified/errors.d.ts +189 -0
  111. package/dist/core/unified/errors.d.ts.map +1 -0
  112. package/dist/core/unified/errors.js +497 -0
  113. package/dist/core/unified/errors.js.map +1 -0
  114. package/dist/core/unified/index.d.ts +19 -0
  115. package/dist/core/unified/index.d.ts.map +1 -0
  116. package/dist/core/unified/index.js +68 -0
  117. package/dist/core/unified/index.js.map +1 -0
  118. package/dist/core/unified/schema.d.ts +101 -0
  119. package/dist/core/unified/schema.d.ts.map +1 -0
  120. package/dist/core/unified/schema.js +350 -0
  121. package/dist/core/unified/schema.js.map +1 -0
  122. package/dist/core/unified/toolRuntime.d.ts +179 -0
  123. package/dist/core/unified/toolRuntime.d.ts.map +1 -0
  124. package/dist/core/unified/toolRuntime.js +517 -0
  125. package/dist/core/unified/toolRuntime.js.map +1 -0
  126. package/dist/core/unified/tools.d.ts +127 -0
  127. package/dist/core/unified/tools.d.ts.map +1 -0
  128. package/dist/core/unified/tools.js +1333 -0
  129. package/dist/core/unified/tools.js.map +1 -0
  130. package/dist/core/unified/types.d.ts +352 -0
  131. package/dist/core/unified/types.d.ts.map +1 -0
  132. package/dist/core/unified/types.js +12 -0
  133. package/dist/core/unified/types.js.map +1 -0
  134. package/dist/core/unified/version.d.ts +209 -0
  135. package/dist/core/unified/version.d.ts.map +1 -0
  136. package/dist/core/unified/version.js +454 -0
  137. package/dist/core/unified/version.js.map +1 -0
  138. package/dist/core/validationRunner.d.ts +3 -1
  139. package/dist/core/validationRunner.d.ts.map +1 -1
  140. package/dist/core/validationRunner.js.map +1 -1
  141. package/dist/headless/headlessApp.d.ts.map +1 -1
  142. package/dist/headless/headlessApp.js +0 -21
  143. package/dist/headless/headlessApp.js.map +1 -1
  144. package/dist/mcp/sseClient.d.ts.map +1 -1
  145. package/dist/mcp/sseClient.js +18 -9
  146. package/dist/mcp/sseClient.js.map +1 -1
  147. package/dist/plugins/tools/build/buildPlugin.d.ts +6 -0
  148. package/dist/plugins/tools/build/buildPlugin.d.ts.map +1 -1
  149. package/dist/plugins/tools/build/buildPlugin.js +10 -4
  150. package/dist/plugins/tools/build/buildPlugin.js.map +1 -1
  151. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
  152. package/dist/plugins/tools/nodeDefaults.js +2 -0
  153. package/dist/plugins/tools/nodeDefaults.js.map +1 -1
  154. package/dist/plugins/tools/security/securityPlugin.d.ts +3 -0
  155. package/dist/plugins/tools/security/securityPlugin.d.ts.map +1 -0
  156. package/dist/plugins/tools/security/securityPlugin.js +12 -0
  157. package/dist/plugins/tools/security/securityPlugin.js.map +1 -0
  158. package/dist/runtime/agentSession.d.ts +2 -2
  159. package/dist/runtime/agentSession.d.ts.map +1 -1
  160. package/dist/runtime/agentSession.js +2 -2
  161. package/dist/runtime/agentSession.js.map +1 -1
  162. package/dist/security/active-stack-security.d.ts +112 -0
  163. package/dist/security/active-stack-security.d.ts.map +1 -0
  164. package/dist/security/active-stack-security.js +296 -0
  165. package/dist/security/active-stack-security.js.map +1 -0
  166. package/dist/security/advanced-persistence-research.d.ts +92 -0
  167. package/dist/security/advanced-persistence-research.d.ts.map +1 -0
  168. package/dist/security/advanced-persistence-research.js +195 -0
  169. package/dist/security/advanced-persistence-research.js.map +1 -0
  170. package/dist/security/advanced-targeting.d.ts +119 -0
  171. package/dist/security/advanced-targeting.d.ts.map +1 -0
  172. package/dist/security/advanced-targeting.js +233 -0
  173. package/dist/security/advanced-targeting.js.map +1 -0
  174. package/dist/security/assessment/vulnerabilityAssessment.d.ts +104 -0
  175. package/dist/security/assessment/vulnerabilityAssessment.d.ts.map +1 -0
  176. package/dist/security/assessment/vulnerabilityAssessment.js +315 -0
  177. package/dist/security/assessment/vulnerabilityAssessment.js.map +1 -0
  178. package/dist/security/authorization/securityAuthorization.d.ts +88 -0
  179. package/dist/security/authorization/securityAuthorization.d.ts.map +1 -0
  180. package/dist/security/authorization/securityAuthorization.js +172 -0
  181. package/dist/security/authorization/securityAuthorization.js.map +1 -0
  182. package/dist/security/comprehensive-targeting.d.ts +85 -0
  183. package/dist/security/comprehensive-targeting.d.ts.map +1 -0
  184. package/dist/security/comprehensive-targeting.js +438 -0
  185. package/dist/security/comprehensive-targeting.js.map +1 -0
  186. package/dist/security/global-security-integration.d.ts +91 -0
  187. package/dist/security/global-security-integration.d.ts.map +1 -0
  188. package/dist/security/global-security-integration.js +218 -0
  189. package/dist/security/global-security-integration.js.map +1 -0
  190. package/dist/security/index.d.ts +38 -0
  191. package/dist/security/index.d.ts.map +1 -0
  192. package/dist/security/index.js +47 -0
  193. package/dist/security/index.js.map +1 -0
  194. package/dist/security/persistence-analyzer.d.ts +56 -0
  195. package/dist/security/persistence-analyzer.d.ts.map +1 -0
  196. package/dist/security/persistence-analyzer.js +187 -0
  197. package/dist/security/persistence-analyzer.js.map +1 -0
  198. package/dist/security/persistence-cli.d.ts +36 -0
  199. package/dist/security/persistence-cli.d.ts.map +1 -0
  200. package/dist/security/persistence-cli.js +160 -0
  201. package/dist/security/persistence-cli.js.map +1 -0
  202. package/dist/security/persistence-research.d.ts +92 -0
  203. package/dist/security/persistence-research.d.ts.map +1 -0
  204. package/dist/security/persistence-research.js +364 -0
  205. package/dist/security/persistence-research.js.map +1 -0
  206. package/dist/security/research/persistenceResearch.d.ts +97 -0
  207. package/dist/security/research/persistenceResearch.d.ts.map +1 -0
  208. package/dist/security/research/persistenceResearch.js +282 -0
  209. package/dist/security/research/persistenceResearch.js.map +1 -0
  210. package/dist/security/security-integration.d.ts +74 -0
  211. package/dist/security/security-integration.d.ts.map +1 -0
  212. package/dist/security/security-integration.js +137 -0
  213. package/dist/security/security-integration.js.map +1 -0
  214. package/dist/security/security-testing-framework.d.ts +112 -0
  215. package/dist/security/security-testing-framework.d.ts.map +1 -0
  216. package/dist/security/security-testing-framework.js +364 -0
  217. package/dist/security/security-testing-framework.js.map +1 -0
  218. package/dist/security/simulation/attackSimulation.d.ts +93 -0
  219. package/dist/security/simulation/attackSimulation.d.ts.map +1 -0
  220. package/dist/security/simulation/attackSimulation.js +341 -0
  221. package/dist/security/simulation/attackSimulation.js.map +1 -0
  222. package/dist/security/strategic-operations.d.ts +100 -0
  223. package/dist/security/strategic-operations.d.ts.map +1 -0
  224. package/dist/security/strategic-operations.js +276 -0
  225. package/dist/security/strategic-operations.js.map +1 -0
  226. package/dist/security/tool-security-wrapper.d.ts +58 -0
  227. package/dist/security/tool-security-wrapper.d.ts.map +1 -0
  228. package/dist/security/tool-security-wrapper.js +156 -0
  229. package/dist/security/tool-security-wrapper.js.map +1 -0
  230. package/dist/shell/claudeCodeStreamHandler.d.ts +145 -0
  231. package/dist/shell/claudeCodeStreamHandler.d.ts.map +1 -0
  232. package/dist/shell/claudeCodeStreamHandler.js +322 -0
  233. package/dist/shell/claudeCodeStreamHandler.js.map +1 -0
  234. package/dist/shell/inputQueueManager.d.ts +144 -0
  235. package/dist/shell/inputQueueManager.d.ts.map +1 -0
  236. package/dist/shell/inputQueueManager.js +290 -0
  237. package/dist/shell/inputQueueManager.js.map +1 -0
  238. package/dist/shell/interactiveShell.d.ts +7 -43
  239. package/dist/shell/interactiveShell.d.ts.map +1 -1
  240. package/dist/shell/interactiveShell.js +166 -418
  241. package/dist/shell/interactiveShell.js.map +1 -1
  242. package/dist/shell/metricsTracker.d.ts +60 -0
  243. package/dist/shell/metricsTracker.d.ts.map +1 -0
  244. package/dist/shell/metricsTracker.js +119 -0
  245. package/dist/shell/metricsTracker.js.map +1 -0
  246. package/dist/shell/shellApp.d.ts +0 -2
  247. package/dist/shell/shellApp.d.ts.map +1 -1
  248. package/dist/shell/shellApp.js +9 -82
  249. package/dist/shell/shellApp.js.map +1 -1
  250. package/dist/shell/streamingOutputManager.d.ts +115 -0
  251. package/dist/shell/streamingOutputManager.d.ts.map +1 -0
  252. package/dist/shell/streamingOutputManager.js +225 -0
  253. package/dist/shell/streamingOutputManager.js.map +1 -0
  254. package/dist/shell/systemPrompt.d.ts.map +1 -1
  255. package/dist/shell/systemPrompt.js +4 -1
  256. package/dist/shell/systemPrompt.js.map +1 -1
  257. package/dist/shell/terminalInput.d.ts +124 -258
  258. package/dist/shell/terminalInput.d.ts.map +1 -1
  259. package/dist/shell/terminalInput.js +608 -1010
  260. package/dist/shell/terminalInput.js.map +1 -1
  261. package/dist/shell/terminalInputAdapter.d.ts +24 -106
  262. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  263. package/dist/shell/terminalInputAdapter.js +30 -140
  264. package/dist/shell/terminalInputAdapter.js.map +1 -1
  265. package/dist/subagents/taskRunner.d.ts +1 -7
  266. package/dist/subagents/taskRunner.d.ts.map +1 -1
  267. package/dist/subagents/taskRunner.js +47 -180
  268. package/dist/subagents/taskRunner.js.map +1 -1
  269. package/dist/tools/learnTools.js +4 -127
  270. package/dist/tools/learnTools.js.map +1 -1
  271. package/dist/tools/securityTools.d.ts +22 -0
  272. package/dist/tools/securityTools.d.ts.map +1 -0
  273. package/dist/tools/securityTools.js +448 -0
  274. package/dist/tools/securityTools.js.map +1 -0
  275. package/dist/ui/ShellUIAdapter.d.ts +1 -7
  276. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  277. package/dist/ui/ShellUIAdapter.js +18 -42
  278. package/dist/ui/ShellUIAdapter.js.map +1 -1
  279. package/dist/ui/display.d.ts +45 -24
  280. package/dist/ui/display.d.ts.map +1 -1
  281. package/dist/ui/display.js +274 -148
  282. package/dist/ui/display.js.map +1 -1
  283. package/dist/ui/persistentPrompt.d.ts +50 -0
  284. package/dist/ui/persistentPrompt.d.ts.map +1 -0
  285. package/dist/ui/persistentPrompt.js +92 -0
  286. package/dist/ui/persistentPrompt.js.map +1 -0
  287. package/dist/ui/terminalUISchema.d.ts +195 -0
  288. package/dist/ui/terminalUISchema.d.ts.map +1 -0
  289. package/dist/ui/terminalUISchema.js +113 -0
  290. package/dist/ui/terminalUISchema.js.map +1 -0
  291. package/dist/ui/theme.d.ts.map +1 -1
  292. package/dist/ui/theme.js +8 -6
  293. package/dist/ui/theme.js.map +1 -1
  294. package/dist/ui/toolDisplay.d.ts +158 -0
  295. package/dist/ui/toolDisplay.d.ts.map +1 -1
  296. package/dist/ui/toolDisplay.js +348 -0
  297. package/dist/ui/toolDisplay.js.map +1 -1
  298. package/dist/ui/unified/layout.d.ts +0 -20
  299. package/dist/ui/unified/layout.d.ts.map +1 -1
  300. package/dist/ui/unified/layout.js +216 -105
  301. package/dist/ui/unified/layout.js.map +1 -1
  302. package/package.json +4 -4
  303. package/scripts/deploy-security-capabilities.js +178 -0
  304. package/dist/core/hooks.d.ts +0 -113
  305. package/dist/core/hooks.d.ts.map +0 -1
  306. package/dist/core/hooks.js +0 -267
  307. package/dist/core/hooks.js.map +0 -1
  308. package/dist/core/metricsTracker.d.ts +0 -122
  309. package/dist/core/metricsTracker.d.ts.map +0 -1
  310. package/dist/core/metricsTracker.js.map +0 -1
  311. package/dist/core/securityAssessment.d.ts +0 -91
  312. package/dist/core/securityAssessment.d.ts.map +0 -1
  313. package/dist/core/securityAssessment.js +0 -580
  314. package/dist/core/securityAssessment.js.map +0 -1
  315. package/dist/core/verification.d.ts +0 -137
  316. package/dist/core/verification.d.ts.map +0 -1
  317. package/dist/core/verification.js +0 -323
  318. package/dist/core/verification.js.map +0 -1
  319. package/dist/subagents/agentConfig.d.ts +0 -27
  320. package/dist/subagents/agentConfig.d.ts.map +0 -1
  321. package/dist/subagents/agentConfig.js +0 -89
  322. package/dist/subagents/agentConfig.js.map +0 -1
  323. package/dist/subagents/agentRegistry.d.ts +0 -33
  324. package/dist/subagents/agentRegistry.d.ts.map +0 -1
  325. package/dist/subagents/agentRegistry.js +0 -162
  326. package/dist/subagents/agentRegistry.js.map +0 -1
  327. package/dist/utils/frontmatter.d.ts +0 -10
  328. package/dist/utils/frontmatter.d.ts.map +0 -1
  329. package/dist/utils/frontmatter.js +0 -78
  330. package/dist/utils/frontmatter.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { createSpinner } from 'nanospinner';
2
- import { clearScreenDown, cursorTo } from 'node:readline';
2
+ import { clearScreenDown, cursorTo, moveCursor } from 'node:readline';
3
3
  import { theme, icons } from './theme.js';
4
4
  import { formatRichContent, renderMessagePanel, renderMessageBody } from './richText.js';
5
5
  import { getTerminalColumns } from './layout.js';
@@ -7,7 +7,7 @@ import { highlightError } from './textHighlighter.js';
7
7
  import { renderSectionHeading } from './designSystem.js';
8
8
  import { isPlainOutputMode } from './outputMode.js';
9
9
  import { writeLock } from './writeLock.js';
10
- import { renderStatusLine } from './unified/layout.js';
10
+ import { renderSessionFrame, renderStatusLine } from './unified/layout.js';
11
11
  import { isStreamingMode } from './globalWriteLock.js';
12
12
  /**
13
13
  * Output lock to prevent race conditions during spinner/stream output.
@@ -159,6 +159,11 @@ const DISPLAY_CONSTANTS = {
159
159
  };
160
160
  // Enhanced spinner frames with smooth animation
161
161
  const SPINNER_FRAMES = ['◐', '◓', '◑', '◒', '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
162
+ const clampWidthToAvailable = (available, min, max) => {
163
+ const bounded = Math.max(1, Math.floor(available));
164
+ const preferred = Math.max(min, Math.min(bounded, max));
165
+ return Math.min(preferred, bounded);
166
+ };
162
167
  /**
163
168
  * Display class manages all terminal UI output for the application.
164
169
  *
@@ -254,10 +259,10 @@ export class Display {
254
259
  interceptor.beforeWrite?.();
255
260
  }
256
261
  }
257
- notifyAfterOutput(content) {
262
+ notifyAfterOutput() {
258
263
  const interceptors = Array.from(this.outputInterceptors);
259
264
  for (let index = interceptors.length - 1; index >= 0; index -= 1) {
260
- interceptors[index]?.afterWrite?.(content);
265
+ interceptors[index]?.afterWrite?.();
261
266
  }
262
267
  }
263
268
  write(value, target = this.outputStream) {
@@ -286,36 +291,29 @@ export class Display {
286
291
  if (typeof content !== 'string' || !content) {
287
292
  return;
288
293
  }
289
- // Normalize carriage returns to avoid overwriting streamed content on the same line.
290
- // Some providers emit '\r' when streaming; convert to '\n' so we append instead of rewriting.
291
- const normalized = content.includes('\r')
292
- ? content.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
293
- : content;
294
- // During streaming, use withLock to prevent interleaving with escape codes.
295
- // This ensures the before/write/after sequence is atomic.
294
+ // During streaming mode, write directly without locks for smooth output
295
+ // StdoutLineTracker patches outputStream.write, so lines are tracked automatically
296
296
  if (isStreamingMode()) {
297
- writeLock.withLock(() => {
298
- this.notifyBeforeOutput();
299
- try {
300
- this.outputStream.write(normalized);
301
- }
302
- catch {
303
- // Ignore write failures to keep UI resilient
304
- }
305
- finally {
306
- this.notifyAfterOutput(normalized);
307
- }
308
- }, 'display.writeRaw.streaming');
297
+ this.notifyBeforeOutput();
298
+ try {
299
+ this.outputStream.write(content);
300
+ }
301
+ catch {
302
+ // Ignore write failures to keep UI resilient
303
+ }
304
+ finally {
305
+ this.notifyAfterOutput();
306
+ }
309
307
  return;
310
308
  }
311
309
  // Outside streaming mode, use locks to coordinate with other UI
312
310
  writeLock.withLock(() => {
313
311
  this.notifyBeforeOutput();
314
312
  try {
315
- this.write(normalized);
313
+ this.write(content);
316
314
  }
317
315
  finally {
318
- this.notifyAfterOutput(normalized);
316
+ this.notifyAfterOutput();
319
317
  }
320
318
  }, 'display.writeRaw');
321
319
  }
@@ -360,7 +358,125 @@ export class Display {
360
358
  }
361
359
  return getTerminalColumns();
362
360
  }
363
- // Banner is now streamed by the shell - no storage needed
361
+ bannerState = null;
362
+ bannerContext = null;
363
+ /**
364
+ * Stores banner information. When silent=true, only stores state without writing.
365
+ * The banner can then be rendered later via renderBannerToContent().
366
+ *
367
+ * @param silent If true, only store state without writing to stdout (default: true)
368
+ */
369
+ showWelcome(profileLabel, profileName, model, provider, workingDir, version, silent = true) {
370
+ // Validate required inputs
371
+ if (!model?.trim() || !provider?.trim() || !workingDir?.trim()) {
372
+ return;
373
+ }
374
+ const width = this.getBannerWidth();
375
+ const banner = this.buildClaudeStyleBanner(profileLabel ?? '', profileName ?? '', model, provider, workingDir, width, version);
376
+ if (!banner) {
377
+ return;
378
+ }
379
+ const height = this.measureBannerHeight(banner);
380
+ this.bannerState = {
381
+ startLine: 0, // Will be set when actually rendered
382
+ height,
383
+ width,
384
+ workingDir,
385
+ version: version?.trim() || undefined,
386
+ model,
387
+ provider,
388
+ profileLabel: profileLabel ?? '',
389
+ profileName: profileName ?? '',
390
+ };
391
+ this.bannerContext = {
392
+ workingDir,
393
+ model,
394
+ provider,
395
+ profileLabel: profileLabel ?? '',
396
+ profileName: profileName ?? '',
397
+ version: version?.trim() || undefined,
398
+ };
399
+ // Only write to stdout if not silent
400
+ if (!silent) {
401
+ const startLine = this.stdoutTracker.totalLines;
402
+ this.withOutput(() => {
403
+ this.writeLine(banner);
404
+ });
405
+ this.bannerState.startLine = startLine;
406
+ }
407
+ }
408
+ /**
409
+ * Get the current version string (if set during showWelcome).
410
+ */
411
+ getVersion() {
412
+ return this.bannerContext?.version;
413
+ }
414
+ /**
415
+ * Get the current banner content as a string.
416
+ * Used by TerminalInput to re-render the banner inside the scroll region.
417
+ * Returns null if no banner has been displayed yet.
418
+ */
419
+ getBannerContent() {
420
+ const state = this.bannerState;
421
+ if (!state) {
422
+ return null;
423
+ }
424
+ const width = this.getBannerWidth();
425
+ return this.buildClaudeStyleBanner(state.profileLabel, state.profileName, state.model, state.provider, state.workingDir, width, state.version);
426
+ }
427
+ /**
428
+ * Render banner directly to stdout (for unified UI re-rendering).
429
+ * This writes the banner without updating tracking state.
430
+ * Returns the number of lines written.
431
+ */
432
+ renderBannerToContent() {
433
+ const banner = this.getBannerContent();
434
+ if (!banner) {
435
+ return 0;
436
+ }
437
+ this.withOutput(() => {
438
+ this.writeLine(banner);
439
+ });
440
+ return this.measureBannerHeight(banner);
441
+ }
442
+ /**
443
+ * Updates the session information banner with new model/provider by appending
444
+ * a new banner entry in the scroll region (no pinned header rewriting).
445
+ */
446
+ updateSessionInfo(model, provider) {
447
+ const state = this.bannerState;
448
+ if (!state) {
449
+ return;
450
+ }
451
+ const width = this.getBannerWidth();
452
+ const banner = this.buildClaudeStyleBanner(state.profileLabel, state.profileName, model, provider, state.workingDir, width, state.version);
453
+ const height = this.measureBannerHeight(banner);
454
+ // If height changed or rewrite failed, do full re-render
455
+ if (height !== state.height || !this.tryRewriteBanner(state, banner)) {
456
+ this.renderAndStoreBanner(state, model, provider);
457
+ this.bannerContext = {
458
+ workingDir: state.workingDir,
459
+ model: state.model,
460
+ provider: state.provider,
461
+ profileLabel: state.profileLabel,
462
+ profileName: state.profileName,
463
+ version: state.version,
464
+ };
465
+ return;
466
+ }
467
+ state.model = model;
468
+ state.provider = provider;
469
+ state.width = width;
470
+ state.height = height;
471
+ this.bannerContext = {
472
+ workingDir: state.workingDir,
473
+ model,
474
+ provider,
475
+ profileLabel: state.profileLabel,
476
+ profileName: state.profileName,
477
+ version: state.version,
478
+ };
479
+ }
364
480
  showThinking(message = 'Thinking...') {
365
481
  // If we already have a spinner, just update its text instead of creating a new one
366
482
  if (this.activeSpinner) {
@@ -640,7 +756,7 @@ export class Display {
640
756
  if (!normalized) {
641
757
  return [];
642
758
  }
643
- const width = Math.max(DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, Math.min(this.getColumnWidth(), DISPLAY_CONSTANTS.MAX_ACTION_WIDTH));
759
+ const width = clampWidthToAvailable(this.getColumnWidth(), DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, DISPLAY_CONSTANTS.MAX_ACTION_WIDTH);
644
760
  const samplePrefix = this.buildSubActionPrefixes(status, true).prefix;
645
761
  const contentWidth = Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - this.visibleLength(samplePrefix));
646
762
  const blocks = formatRichContent(normalized, contentWidth);
@@ -730,7 +846,8 @@ export class Display {
730
846
  { text: `${theme.success('✓')} ${normalized}`, tone: 'success' },
731
847
  elapsed ? { text: `(${elapsed})`, tone: 'muted' } : null,
732
848
  ].filter(Boolean);
733
- const line = renderStatusLine(parts, this.getColumnWidth() - 2);
849
+ const width = clampWidthToAvailable(this.getColumnWidth() - 2, 8, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH);
850
+ const line = renderStatusLine(parts, width);
734
851
  this.withOutput(() => {
735
852
  this.writeLine(line);
736
853
  });
@@ -740,23 +857,6 @@ export class Display {
740
857
  // Tools are available but not listed verbosely on startup
741
858
  // Parameter prefixed with underscore to indicate intentionally unused
742
859
  }
743
- /**
744
- * Show a compact launch panel of slash commands with wrapped descriptions.
745
- */
746
- showCommandPalette(commands, options) {
747
- if (!commands || commands.length === 0) {
748
- return;
749
- }
750
- const panel = this.buildCommandPalette(commands, options);
751
- if (!panel.trim()) {
752
- return;
753
- }
754
- this.withOutput(() => {
755
- this.writeLine();
756
- this.writeLine(panel);
757
- this.writeLine();
758
- });
759
- }
760
860
  /**
761
861
  * Show ready message with keyboard shortcuts hint (compact)
762
862
  * Note: Commands are now shown in the banner, so this is a no-op
@@ -775,17 +875,58 @@ export class Display {
775
875
  // Claude Code style: don't show providers on startup, keep it minimal
776
876
  }
777
877
  /**
778
- * Show unified status bar - Claude Code style (minimal)
779
- * Note: No-op - status is shown in mode controls line
878
+ * Show unified status bar - Now a no-op.
879
+ * Status is shown in the inline input area's mode controls line.
780
880
  */
781
881
  showUnifiedStatusBar(_providers) {
782
- // No-op - banner is streamed as content, status shown in control bar
882
+ // No-op: Status is shown in the inline input area's mode controls line
783
883
  }
784
884
  /**
785
- * Show streaming header - no-op, banner is streamed as content
885
+ * Show streaming header in a unified frame with model/provider/workspace context.
886
+ * Keeps the streaming section visually connected to the launch banner.
786
887
  */
787
- showStreamingHeader(_options) {
788
- // No-op - banner is streamed as content by the shell
888
+ showStreamingHeader(options) {
889
+ const banner = this.bannerState;
890
+ const model = banner ? this.formatModelLabel(banner.model) : null;
891
+ const provider = banner?.provider ?? '';
892
+ const workspace = banner?.workingDir ?? '';
893
+ if (!model && !provider && !workspace) {
894
+ return;
895
+ }
896
+ const width = clampWidthToAvailable(this.getColumnWidth() - 4, DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH);
897
+ const spaceAbove = options?.spaceAbove ?? true;
898
+ const spaceBelow = options?.spaceBelow ?? true;
899
+ // Plain output keeps the header lightweight
900
+ if (isPlainOutputMode()) {
901
+ const label = model ? `Streaming ${model}` : 'Streaming response';
902
+ this.withOutput(() => {
903
+ if (spaceAbove) {
904
+ this.writeLine();
905
+ }
906
+ this.writeLine(`--- ${label} ---`);
907
+ if (spaceBelow) {
908
+ this.writeLine();
909
+ }
910
+ });
911
+ return;
912
+ }
913
+ const sessionFrame = renderSessionFrame({
914
+ profileLabel: banner?.profileLabel ?? '',
915
+ profileName: banner?.profileName ?? '',
916
+ model: model ?? '',
917
+ provider,
918
+ workspace,
919
+ width,
920
+ });
921
+ this.withOutput(() => {
922
+ if (spaceAbove) {
923
+ this.writeLine();
924
+ }
925
+ this.writeLine(sessionFrame);
926
+ if (spaceBelow) {
927
+ this.writeLine();
928
+ }
929
+ });
789
930
  }
790
931
  /**
791
932
  * Show model commands help
@@ -828,7 +969,7 @@ export class Display {
828
969
  if (index < 1 || total < 1 || index > total) {
829
970
  return;
830
971
  }
831
- const width = Math.max(DISPLAY_CONSTANTS.MIN_THOUGHT_WIDTH, Math.min(this.getColumnWidth(), DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
972
+ const width = clampWidthToAvailable(this.getColumnWidth(), DISPLAY_CONSTANTS.MIN_THOUGHT_WIDTH, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH);
832
973
  const heading = renderSectionHeading(`Plan ${index}/${total}`, {
833
974
  subtitle: step,
834
975
  icon: icons.arrow,
@@ -850,14 +991,78 @@ export class Display {
850
991
  }
851
992
  });
852
993
  this.stdoutTracker.reset();
853
- // Banner is streamed content - no re-render on clear
994
+ if (this.bannerState) {
995
+ this.renderAndStoreBanner(this.bannerState, this.bannerState.model, this.bannerState.provider);
996
+ }
854
997
  }
855
998
  newLine() {
856
999
  this.withOutput(() => {
857
1000
  this.writeLine();
858
1001
  });
859
1002
  }
860
- // Legacy banner methods removed - banner is streamed as content by the shell
1003
+ getBannerWidth() {
1004
+ const availableColumns = this.getColumnWidth();
1005
+ const available = availableColumns - DISPLAY_CONSTANTS.BANNER_PADDING;
1006
+ return clampWidthToAvailable(available, DISPLAY_CONSTANTS.MIN_BANNER_WIDTH, DISPLAY_CONSTANTS.MAX_BANNER_WIDTH);
1007
+ }
1008
+ measureBannerHeight(banner) {
1009
+ if (!banner) {
1010
+ return 0;
1011
+ }
1012
+ return banner.split('\n').length;
1013
+ }
1014
+ /**
1015
+ * Attempts to rewrite the banner in place using terminal cursor manipulation.
1016
+ * Returns true if successful, false if rewrite is not possible.
1017
+ */
1018
+ tryRewriteBanner(state, banner) {
1019
+ if (!this.outputStream.isTTY) {
1020
+ return false;
1021
+ }
1022
+ if (!banner || state.height <= 0) {
1023
+ return false;
1024
+ }
1025
+ const linesWritten = this.stdoutTracker.totalLines;
1026
+ const linesAfterBanner = linesWritten - (state.startLine + state.height);
1027
+ if (linesAfterBanner < 0) {
1028
+ return false;
1029
+ }
1030
+ const totalOffset = linesAfterBanner + state.height;
1031
+ const maxRows = this.outputStream.rows;
1032
+ if (typeof maxRows === 'number' && maxRows > 0 && totalOffset > maxRows) {
1033
+ return false;
1034
+ }
1035
+ try {
1036
+ this.withOutput(() => {
1037
+ moveCursor(this.outputStream, 0, -totalOffset);
1038
+ cursorTo(this.outputStream, 0);
1039
+ this.stdoutTracker.withSuspended(() => {
1040
+ this.outputStream.write(`${banner}\n`);
1041
+ });
1042
+ if (linesAfterBanner > 0) {
1043
+ moveCursor(this.outputStream, 0, linesAfterBanner);
1044
+ }
1045
+ cursorTo(this.outputStream, 0);
1046
+ });
1047
+ return true;
1048
+ }
1049
+ catch {
1050
+ return false;
1051
+ }
1052
+ }
1053
+ renderAndStoreBanner(state, model, provider) {
1054
+ const width = this.getBannerWidth();
1055
+ const banner = this.buildClaudeStyleBanner(state.profileLabel, state.profileName, model, provider, state.workingDir, width, state.version);
1056
+ const startLine = this.stdoutTracker.totalLines;
1057
+ this.withOutput(() => {
1058
+ this.writeLine(banner);
1059
+ });
1060
+ state.startLine = startLine;
1061
+ state.height = this.measureBannerHeight(banner);
1062
+ state.width = width;
1063
+ state.model = model;
1064
+ state.provider = provider;
1065
+ }
861
1066
  formatModelLabel(model) {
862
1067
  if (/gpt-5\.1-?codex/i.test(model)) {
863
1068
  return model;
@@ -924,7 +1129,8 @@ export class Display {
924
1129
  }
925
1130
  resolveMessageWidth() {
926
1131
  const columns = this.getColumnWidth();
927
- return Math.max(DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, Math.min(columns - DISPLAY_CONSTANTS.MESSAGE_PADDING, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
1132
+ const available = columns - DISPLAY_CONSTANTS.MESSAGE_PADDING;
1133
+ return clampWidthToAvailable(available, DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH);
928
1134
  }
929
1135
  formatTelemetryLine(metadata) {
930
1136
  if (!metadata) {
@@ -955,96 +1161,16 @@ export class Display {
955
1161
  }
956
1162
  return `${seconds}s`;
957
1163
  }
958
- buildCommandPalette(commands, options) {
959
- if (!commands.length) {
960
- return '';
961
- }
962
- const width = Math.max(DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, Math.min(this.getColumnWidth() - 2, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
963
- const indent = ' ';
964
- const grouped = this.groupPaletteCommands(commands);
965
- const commandWidth = this.computeCommandColumnWidth(commands, width, indent.length);
966
- const descWidth = Math.max(DISPLAY_CONSTANTS.MIN_WRAP_WIDTH, width - indent.length - commandWidth - 1);
967
- const title = options?.title ?? 'Slash commands';
968
- const intro = options?.intro ?? 'Describe a task or pick a command below:';
969
- const lines = [];
970
- lines.push(theme.gradient.primary(title));
971
- const introLines = this.wrapLine(intro, width);
972
- for (const line of introLines) {
973
- lines.push(theme.ui.muted(line));
974
- }
975
- lines.push('');
976
- grouped.forEach(({ category, items }, index) => {
977
- const label = this.formatPaletteCategory(category);
978
- if (label) {
979
- lines.push(theme.bold(label));
980
- }
981
- for (const item of items) {
982
- const wrappedDesc = this.wrapLine(item.description, descWidth);
983
- const paddedCommand = item.command.padEnd(commandWidth);
984
- const commandLabel = theme.primary(paddedCommand);
985
- const firstLine = wrappedDesc[0] ?? '';
986
- lines.push(`${indent}${commandLabel} ${this.colorizePaletteText(firstLine, item.tone)}`);
987
- for (const extra of wrappedDesc.slice(1)) {
988
- lines.push(`${indent}${' '.repeat(commandWidth)} ${this.colorizePaletteText(extra, item.tone)}`);
989
- }
990
- }
991
- if (index < grouped.length - 1) {
992
- lines.push('');
993
- }
1164
+ buildClaudeStyleBanner(_profileLabel, _profileName, model, provider, workingDir, _width, version) {
1165
+ return renderSessionFrame({
1166
+ profileLabel: _profileLabel,
1167
+ profileName: _profileName,
1168
+ model: this.formatModelLabel(model),
1169
+ provider,
1170
+ workspace: workingDir,
1171
+ version,
1172
+ width: _width,
994
1173
  });
995
- return lines.join('\n').trimEnd();
996
- }
997
- groupPaletteCommands(commands) {
998
- const FALLBACK = 'other';
999
- const groups = new Map();
1000
- for (const item of commands) {
1001
- const key = (item.category ?? FALLBACK).toLowerCase();
1002
- const bucket = groups.get(key) ?? [];
1003
- bucket.push(item);
1004
- groups.set(key, bucket);
1005
- }
1006
- const order = ['configuration', 'workspace', 'diagnostics', 'other'];
1007
- const orderedKeys = [
1008
- ...order.filter((key) => groups.has(key)),
1009
- ...Array.from(groups.keys()).filter((key) => !order.includes(key)),
1010
- ];
1011
- return orderedKeys.map((category) => ({ category, items: groups.get(category) ?? [] }));
1012
- }
1013
- computeCommandColumnWidth(commands, totalWidth, indentWidth) {
1014
- const longest = commands.reduce((max, item) => Math.max(max, this.visibleLength(item.command)), 0);
1015
- const maxAllowed = Math.max(12, Math.min(longest + 2, Math.floor(totalWidth * 0.35)));
1016
- const budget = Math.max(10, totalWidth - indentWidth - DISPLAY_CONSTANTS.MIN_WRAP_WIDTH);
1017
- return Math.min(maxAllowed, budget);
1018
- }
1019
- formatPaletteCategory(category) {
1020
- if (!category) {
1021
- return 'Other';
1022
- }
1023
- switch (category.toLowerCase()) {
1024
- case 'configuration':
1025
- return 'Configuration';
1026
- case 'workspace':
1027
- return 'Workspace';
1028
- case 'diagnostics':
1029
- return 'Diagnostics';
1030
- case 'other':
1031
- return 'Other';
1032
- default:
1033
- return category[0]?.toUpperCase() + category.slice(1);
1034
- }
1035
- }
1036
- colorizePaletteText(text, tone) {
1037
- switch (tone) {
1038
- case 'warn':
1039
- return theme.warning(text);
1040
- case 'success':
1041
- return theme.success(text);
1042
- case 'info':
1043
- return theme.info(text);
1044
- case 'muted':
1045
- default:
1046
- return theme.ui.muted(text);
1047
- }
1048
1174
  }
1049
1175
  /**
1050
1176
  * Wraps text with a prefix on the first line and optional continuation prefix.
@@ -1054,9 +1180,9 @@ export class Display {
1054
1180
  if (!text) {
1055
1181
  return prefix.trimEnd();
1056
1182
  }
1057
- const width = Math.max(DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, Math.min(this.getColumnWidth(), DISPLAY_CONSTANTS.MAX_ACTION_WIDTH));
1183
+ const width = clampWidthToAvailable(this.getColumnWidth(), DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, DISPLAY_CONSTANTS.MAX_ACTION_WIDTH);
1058
1184
  const prefixWidth = this.visibleLength(prefix);
1059
- const available = Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - prefixWidth);
1185
+ const available = Math.max(1, Math.min(width - prefixWidth, Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - prefixWidth)));
1060
1186
  const indent = typeof options?.continuationPrefix === 'string'
1061
1187
  ? options.continuationPrefix
1062
1188
  : ' '.repeat(Math.max(0, prefixWidth));
@@ -1108,14 +1234,14 @@ export class Display {
1108
1234
  border: theme.ui.border,
1109
1235
  label: theme.info,
1110
1236
  };
1111
- const width = Math.min(this.getColumnWidth() - 4, 70);
1237
+ const width = clampWidthToAvailable(this.getColumnWidth() - 4, DISPLAY_CONSTANTS.MIN_THOUGHT_WIDTH, DISPLAY_CONSTANTS.MAX_THOUGHT_WIDTH);
1112
1238
  const lines = [];
1113
1239
  // Simple header
1114
1240
  lines.push(`${theme.ui.muted('⏺')} ${thinkingStyle.label('Thinking')}`);
1115
1241
  // Parse and format the thinking content with simple indentation
1116
1242
  const contentLines = content.split('\n').filter(line => line.trim());
1117
1243
  for (const line of contentLines) {
1118
- const wrapped = this.wrapLine(line.trim(), width - 4);
1244
+ const wrapped = this.wrapLine(line.trim(), Math.max(1, width - 4));
1119
1245
  for (const wrappedLine of wrapped) {
1120
1246
  lines.push(` ${thinkingStyle.text(wrappedLine)}`);
1121
1247
  }