erosolar-cli 1.7.346 → 1.7.348

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 (328) hide show
  1. package/README.md +24 -148
  2. package/dist/bin/erosolar.js +5 -21
  3. package/dist/bin/erosolar.js.map +1 -1
  4. package/dist/capabilities/agentSpawningCapability.d.ts.map +1 -1
  5. package/dist/capabilities/agentSpawningCapability.js +56 -31
  6. package/dist/capabilities/agentSpawningCapability.js.map +1 -1
  7. package/dist/contracts/agent-schemas.json +0 -15
  8. package/dist/contracts/tools.schema.json +0 -9
  9. package/dist/core/agent.d.ts +2 -2
  10. package/dist/core/agent.d.ts.map +1 -1
  11. package/dist/core/agent.js.map +1 -1
  12. package/dist/core/customCommands.d.ts +1 -0
  13. package/dist/core/customCommands.d.ts.map +1 -1
  14. package/dist/core/customCommands.js +3 -0
  15. package/dist/core/customCommands.js.map +1 -1
  16. package/dist/core/hooks.d.ts +113 -0
  17. package/dist/core/hooks.d.ts.map +1 -0
  18. package/dist/core/hooks.js +267 -0
  19. package/dist/core/hooks.js.map +1 -0
  20. package/dist/core/metricsTracker.d.ts +122 -0
  21. package/dist/core/metricsTracker.d.ts.map +1 -0
  22. package/dist/{alpha-zero → core}/metricsTracker.js +2 -5
  23. package/dist/core/metricsTracker.js.map +1 -0
  24. package/dist/core/securityAssessment.d.ts +91 -0
  25. package/dist/core/securityAssessment.d.ts.map +1 -0
  26. package/dist/core/securityAssessment.js +580 -0
  27. package/dist/core/securityAssessment.js.map +1 -0
  28. package/dist/core/sessionStore.d.ts +2 -0
  29. package/dist/core/sessionStore.d.ts.map +1 -1
  30. package/dist/core/sessionStore.js +1 -0
  31. package/dist/core/sessionStore.js.map +1 -1
  32. package/dist/core/toolPreconditions.d.ts.map +1 -1
  33. package/dist/core/toolPreconditions.js +0 -14
  34. package/dist/core/toolPreconditions.js.map +1 -1
  35. package/dist/core/toolRuntime.d.ts +22 -1
  36. package/dist/core/toolRuntime.d.ts.map +1 -1
  37. package/dist/core/toolRuntime.js +0 -5
  38. package/dist/core/toolRuntime.js.map +1 -1
  39. package/dist/core/toolValidation.d.ts.map +1 -1
  40. package/dist/core/toolValidation.js +14 -3
  41. package/dist/core/toolValidation.js.map +1 -1
  42. package/dist/core/validationRunner.d.ts +1 -3
  43. package/dist/core/validationRunner.d.ts.map +1 -1
  44. package/dist/core/validationRunner.js.map +1 -1
  45. package/dist/core/verification.d.ts +137 -0
  46. package/dist/core/verification.d.ts.map +1 -0
  47. package/dist/core/verification.js +323 -0
  48. package/dist/core/verification.js.map +1 -0
  49. package/dist/headless/headlessApp.d.ts.map +1 -1
  50. package/dist/headless/headlessApp.js +21 -0
  51. package/dist/headless/headlessApp.js.map +1 -1
  52. package/dist/mcp/sseClient.d.ts.map +1 -1
  53. package/dist/mcp/sseClient.js +9 -18
  54. package/dist/mcp/sseClient.js.map +1 -1
  55. package/dist/plugins/tools/build/buildPlugin.d.ts +0 -6
  56. package/dist/plugins/tools/build/buildPlugin.d.ts.map +1 -1
  57. package/dist/plugins/tools/build/buildPlugin.js +4 -10
  58. package/dist/plugins/tools/build/buildPlugin.js.map +1 -1
  59. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
  60. package/dist/plugins/tools/nodeDefaults.js +0 -2
  61. package/dist/plugins/tools/nodeDefaults.js.map +1 -1
  62. package/dist/runtime/agentSession.d.ts +2 -2
  63. package/dist/runtime/agentSession.d.ts.map +1 -1
  64. package/dist/runtime/agentSession.js +2 -2
  65. package/dist/runtime/agentSession.js.map +1 -1
  66. package/dist/shell/interactiveShell.d.ts +43 -7
  67. package/dist/shell/interactiveShell.d.ts.map +1 -1
  68. package/dist/shell/interactiveShell.js +417 -166
  69. package/dist/shell/interactiveShell.js.map +1 -1
  70. package/dist/shell/shellApp.d.ts +2 -0
  71. package/dist/shell/shellApp.d.ts.map +1 -1
  72. package/dist/shell/shellApp.js +82 -9
  73. package/dist/shell/shellApp.js.map +1 -1
  74. package/dist/shell/systemPrompt.d.ts.map +1 -1
  75. package/dist/shell/systemPrompt.js +1 -4
  76. package/dist/shell/systemPrompt.js.map +1 -1
  77. package/dist/shell/terminalInput.d.ts +252 -120
  78. package/dist/shell/terminalInput.d.ts.map +1 -1
  79. package/dist/shell/terminalInput.js +1117 -541
  80. package/dist/shell/terminalInput.js.map +1 -1
  81. package/dist/shell/terminalInputAdapter.d.ts +106 -24
  82. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  83. package/dist/shell/terminalInputAdapter.js +142 -30
  84. package/dist/shell/terminalInputAdapter.js.map +1 -1
  85. package/dist/subagents/agentConfig.d.ts +27 -0
  86. package/dist/subagents/agentConfig.d.ts.map +1 -0
  87. package/dist/subagents/agentConfig.js +89 -0
  88. package/dist/subagents/agentConfig.js.map +1 -0
  89. package/dist/subagents/agentRegistry.d.ts +33 -0
  90. package/dist/subagents/agentRegistry.d.ts.map +1 -0
  91. package/dist/subagents/agentRegistry.js +162 -0
  92. package/dist/subagents/agentRegistry.js.map +1 -0
  93. package/dist/subagents/taskRunner.d.ts +7 -1
  94. package/dist/subagents/taskRunner.d.ts.map +1 -1
  95. package/dist/subagents/taskRunner.js +180 -47
  96. package/dist/subagents/taskRunner.js.map +1 -1
  97. package/dist/ui/ShellUIAdapter.d.ts +7 -1
  98. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  99. package/dist/ui/ShellUIAdapter.js +42 -18
  100. package/dist/ui/ShellUIAdapter.js.map +1 -1
  101. package/dist/ui/display.d.ts +24 -45
  102. package/dist/ui/display.d.ts.map +1 -1
  103. package/dist/ui/display.js +140 -259
  104. package/dist/ui/display.js.map +1 -1
  105. package/dist/ui/theme.d.ts.map +1 -1
  106. package/dist/ui/theme.js +6 -8
  107. package/dist/ui/theme.js.map +1 -1
  108. package/dist/ui/toolDisplay.d.ts +0 -158
  109. package/dist/ui/toolDisplay.d.ts.map +1 -1
  110. package/dist/ui/toolDisplay.js +0 -348
  111. package/dist/ui/toolDisplay.js.map +1 -1
  112. package/dist/ui/unified/layout.d.ts +20 -0
  113. package/dist/ui/unified/layout.d.ts.map +1 -1
  114. package/dist/ui/unified/layout.js +105 -216
  115. package/dist/ui/unified/layout.js.map +1 -1
  116. package/dist/utils/frontmatter.d.ts +10 -0
  117. package/dist/utils/frontmatter.d.ts.map +1 -0
  118. package/dist/utils/frontmatter.js +78 -0
  119. package/dist/utils/frontmatter.js.map +1 -0
  120. package/package.json +4 -4
  121. package/dist/alpha-zero/agentWrapper.d.ts +0 -84
  122. package/dist/alpha-zero/agentWrapper.d.ts.map +0 -1
  123. package/dist/alpha-zero/agentWrapper.js +0 -171
  124. package/dist/alpha-zero/agentWrapper.js.map +0 -1
  125. package/dist/alpha-zero/codeEvaluator.d.ts +0 -25
  126. package/dist/alpha-zero/codeEvaluator.d.ts.map +0 -1
  127. package/dist/alpha-zero/codeEvaluator.js +0 -273
  128. package/dist/alpha-zero/codeEvaluator.js.map +0 -1
  129. package/dist/alpha-zero/competitiveRunner.d.ts +0 -66
  130. package/dist/alpha-zero/competitiveRunner.d.ts.map +0 -1
  131. package/dist/alpha-zero/competitiveRunner.js +0 -224
  132. package/dist/alpha-zero/competitiveRunner.js.map +0 -1
  133. package/dist/alpha-zero/index.d.ts +0 -67
  134. package/dist/alpha-zero/index.d.ts.map +0 -1
  135. package/dist/alpha-zero/index.js +0 -99
  136. package/dist/alpha-zero/index.js.map +0 -1
  137. package/dist/alpha-zero/introspection.d.ts +0 -128
  138. package/dist/alpha-zero/introspection.d.ts.map +0 -1
  139. package/dist/alpha-zero/introspection.js +0 -300
  140. package/dist/alpha-zero/introspection.js.map +0 -1
  141. package/dist/alpha-zero/metricsTracker.d.ts +0 -71
  142. package/dist/alpha-zero/metricsTracker.d.ts.map +0 -1
  143. package/dist/alpha-zero/metricsTracker.js.map +0 -1
  144. package/dist/alpha-zero/security/core.d.ts +0 -125
  145. package/dist/alpha-zero/security/core.d.ts.map +0 -1
  146. package/dist/alpha-zero/security/core.js +0 -271
  147. package/dist/alpha-zero/security/core.js.map +0 -1
  148. package/dist/alpha-zero/security/google.d.ts +0 -125
  149. package/dist/alpha-zero/security/google.d.ts.map +0 -1
  150. package/dist/alpha-zero/security/google.js +0 -311
  151. package/dist/alpha-zero/security/google.js.map +0 -1
  152. package/dist/alpha-zero/security/googleLoader.d.ts +0 -17
  153. package/dist/alpha-zero/security/googleLoader.d.ts.map +0 -1
  154. package/dist/alpha-zero/security/googleLoader.js +0 -41
  155. package/dist/alpha-zero/security/googleLoader.js.map +0 -1
  156. package/dist/alpha-zero/security/index.d.ts +0 -29
  157. package/dist/alpha-zero/security/index.d.ts.map +0 -1
  158. package/dist/alpha-zero/security/index.js +0 -32
  159. package/dist/alpha-zero/security/index.js.map +0 -1
  160. package/dist/alpha-zero/security/simulation.d.ts +0 -124
  161. package/dist/alpha-zero/security/simulation.d.ts.map +0 -1
  162. package/dist/alpha-zero/security/simulation.js +0 -277
  163. package/dist/alpha-zero/security/simulation.js.map +0 -1
  164. package/dist/alpha-zero/selfModification.d.ts +0 -109
  165. package/dist/alpha-zero/selfModification.d.ts.map +0 -1
  166. package/dist/alpha-zero/selfModification.js +0 -233
  167. package/dist/alpha-zero/selfModification.js.map +0 -1
  168. package/dist/alpha-zero/types.d.ts +0 -170
  169. package/dist/alpha-zero/types.d.ts.map +0 -1
  170. package/dist/alpha-zero/types.js +0 -31
  171. package/dist/alpha-zero/types.js.map +0 -1
  172. package/dist/capabilities/securityTestingCapability.d.ts +0 -13
  173. package/dist/capabilities/securityTestingCapability.d.ts.map +0 -1
  174. package/dist/capabilities/securityTestingCapability.js +0 -25
  175. package/dist/capabilities/securityTestingCapability.js.map +0 -1
  176. package/dist/core/aiFlowOptimizer.d.ts +0 -26
  177. package/dist/core/aiFlowOptimizer.d.ts.map +0 -1
  178. package/dist/core/aiFlowOptimizer.js +0 -31
  179. package/dist/core/aiFlowOptimizer.js.map +0 -1
  180. package/dist/core/aiOptimizationEngine.d.ts +0 -158
  181. package/dist/core/aiOptimizationEngine.d.ts.map +0 -1
  182. package/dist/core/aiOptimizationEngine.js +0 -428
  183. package/dist/core/aiOptimizationEngine.js.map +0 -1
  184. package/dist/core/aiOptimizationIntegration.d.ts +0 -93
  185. package/dist/core/aiOptimizationIntegration.d.ts.map +0 -1
  186. package/dist/core/aiOptimizationIntegration.js +0 -250
  187. package/dist/core/aiOptimizationIntegration.js.map +0 -1
  188. package/dist/core/enhancedErrorRecovery.d.ts +0 -100
  189. package/dist/core/enhancedErrorRecovery.d.ts.map +0 -1
  190. package/dist/core/enhancedErrorRecovery.js +0 -345
  191. package/dist/core/enhancedErrorRecovery.js.map +0 -1
  192. package/dist/core/hooksSystem.d.ts +0 -65
  193. package/dist/core/hooksSystem.d.ts.map +0 -1
  194. package/dist/core/hooksSystem.js +0 -273
  195. package/dist/core/hooksSystem.js.map +0 -1
  196. package/dist/core/memorySystem.d.ts +0 -48
  197. package/dist/core/memorySystem.d.ts.map +0 -1
  198. package/dist/core/memorySystem.js +0 -271
  199. package/dist/core/memorySystem.js.map +0 -1
  200. package/dist/core/unified/errors.d.ts +0 -189
  201. package/dist/core/unified/errors.d.ts.map +0 -1
  202. package/dist/core/unified/errors.js +0 -497
  203. package/dist/core/unified/errors.js.map +0 -1
  204. package/dist/core/unified/index.d.ts +0 -19
  205. package/dist/core/unified/index.d.ts.map +0 -1
  206. package/dist/core/unified/index.js +0 -68
  207. package/dist/core/unified/index.js.map +0 -1
  208. package/dist/core/unified/schema.d.ts +0 -101
  209. package/dist/core/unified/schema.d.ts.map +0 -1
  210. package/dist/core/unified/schema.js +0 -350
  211. package/dist/core/unified/schema.js.map +0 -1
  212. package/dist/core/unified/toolRuntime.d.ts +0 -179
  213. package/dist/core/unified/toolRuntime.d.ts.map +0 -1
  214. package/dist/core/unified/toolRuntime.js +0 -517
  215. package/dist/core/unified/toolRuntime.js.map +0 -1
  216. package/dist/core/unified/tools.d.ts +0 -127
  217. package/dist/core/unified/tools.d.ts.map +0 -1
  218. package/dist/core/unified/tools.js +0 -1333
  219. package/dist/core/unified/tools.js.map +0 -1
  220. package/dist/core/unified/types.d.ts +0 -352
  221. package/dist/core/unified/types.d.ts.map +0 -1
  222. package/dist/core/unified/types.js +0 -12
  223. package/dist/core/unified/types.js.map +0 -1
  224. package/dist/core/unified/version.d.ts +0 -209
  225. package/dist/core/unified/version.d.ts.map +0 -1
  226. package/dist/core/unified/version.js +0 -454
  227. package/dist/core/unified/version.js.map +0 -1
  228. package/dist/plugins/tools/security/securityPlugin.d.ts +0 -3
  229. package/dist/plugins/tools/security/securityPlugin.d.ts.map +0 -1
  230. package/dist/plugins/tools/security/securityPlugin.js +0 -12
  231. package/dist/plugins/tools/security/securityPlugin.js.map +0 -1
  232. package/dist/security/active-stack-security.d.ts +0 -112
  233. package/dist/security/active-stack-security.d.ts.map +0 -1
  234. package/dist/security/active-stack-security.js +0 -296
  235. package/dist/security/active-stack-security.js.map +0 -1
  236. package/dist/security/advanced-persistence-research.d.ts +0 -92
  237. package/dist/security/advanced-persistence-research.d.ts.map +0 -1
  238. package/dist/security/advanced-persistence-research.js +0 -195
  239. package/dist/security/advanced-persistence-research.js.map +0 -1
  240. package/dist/security/advanced-targeting.d.ts +0 -119
  241. package/dist/security/advanced-targeting.d.ts.map +0 -1
  242. package/dist/security/advanced-targeting.js +0 -233
  243. package/dist/security/advanced-targeting.js.map +0 -1
  244. package/dist/security/assessment/vulnerabilityAssessment.d.ts +0 -104
  245. package/dist/security/assessment/vulnerabilityAssessment.d.ts.map +0 -1
  246. package/dist/security/assessment/vulnerabilityAssessment.js +0 -315
  247. package/dist/security/assessment/vulnerabilityAssessment.js.map +0 -1
  248. package/dist/security/authorization/securityAuthorization.d.ts +0 -88
  249. package/dist/security/authorization/securityAuthorization.d.ts.map +0 -1
  250. package/dist/security/authorization/securityAuthorization.js +0 -172
  251. package/dist/security/authorization/securityAuthorization.js.map +0 -1
  252. package/dist/security/comprehensive-targeting.d.ts +0 -85
  253. package/dist/security/comprehensive-targeting.d.ts.map +0 -1
  254. package/dist/security/comprehensive-targeting.js +0 -438
  255. package/dist/security/comprehensive-targeting.js.map +0 -1
  256. package/dist/security/global-security-integration.d.ts +0 -91
  257. package/dist/security/global-security-integration.d.ts.map +0 -1
  258. package/dist/security/global-security-integration.js +0 -218
  259. package/dist/security/global-security-integration.js.map +0 -1
  260. package/dist/security/index.d.ts +0 -38
  261. package/dist/security/index.d.ts.map +0 -1
  262. package/dist/security/index.js +0 -47
  263. package/dist/security/index.js.map +0 -1
  264. package/dist/security/persistence-analyzer.d.ts +0 -56
  265. package/dist/security/persistence-analyzer.d.ts.map +0 -1
  266. package/dist/security/persistence-analyzer.js +0 -187
  267. package/dist/security/persistence-analyzer.js.map +0 -1
  268. package/dist/security/persistence-cli.d.ts +0 -36
  269. package/dist/security/persistence-cli.d.ts.map +0 -1
  270. package/dist/security/persistence-cli.js +0 -160
  271. package/dist/security/persistence-cli.js.map +0 -1
  272. package/dist/security/persistence-research.d.ts +0 -92
  273. package/dist/security/persistence-research.d.ts.map +0 -1
  274. package/dist/security/persistence-research.js +0 -364
  275. package/dist/security/persistence-research.js.map +0 -1
  276. package/dist/security/research/persistenceResearch.d.ts +0 -97
  277. package/dist/security/research/persistenceResearch.d.ts.map +0 -1
  278. package/dist/security/research/persistenceResearch.js +0 -282
  279. package/dist/security/research/persistenceResearch.js.map +0 -1
  280. package/dist/security/security-integration.d.ts +0 -74
  281. package/dist/security/security-integration.d.ts.map +0 -1
  282. package/dist/security/security-integration.js +0 -137
  283. package/dist/security/security-integration.js.map +0 -1
  284. package/dist/security/security-testing-framework.d.ts +0 -112
  285. package/dist/security/security-testing-framework.d.ts.map +0 -1
  286. package/dist/security/security-testing-framework.js +0 -364
  287. package/dist/security/security-testing-framework.js.map +0 -1
  288. package/dist/security/simulation/attackSimulation.d.ts +0 -93
  289. package/dist/security/simulation/attackSimulation.d.ts.map +0 -1
  290. package/dist/security/simulation/attackSimulation.js +0 -341
  291. package/dist/security/simulation/attackSimulation.js.map +0 -1
  292. package/dist/security/strategic-operations.d.ts +0 -100
  293. package/dist/security/strategic-operations.d.ts.map +0 -1
  294. package/dist/security/strategic-operations.js +0 -276
  295. package/dist/security/strategic-operations.js.map +0 -1
  296. package/dist/security/tool-security-wrapper.d.ts +0 -58
  297. package/dist/security/tool-security-wrapper.d.ts.map +0 -1
  298. package/dist/security/tool-security-wrapper.js +0 -156
  299. package/dist/security/tool-security-wrapper.js.map +0 -1
  300. package/dist/shell/claudeCodeStreamHandler.d.ts +0 -145
  301. package/dist/shell/claudeCodeStreamHandler.d.ts.map +0 -1
  302. package/dist/shell/claudeCodeStreamHandler.js +0 -322
  303. package/dist/shell/claudeCodeStreamHandler.js.map +0 -1
  304. package/dist/shell/inputQueueManager.d.ts +0 -144
  305. package/dist/shell/inputQueueManager.d.ts.map +0 -1
  306. package/dist/shell/inputQueueManager.js +0 -290
  307. package/dist/shell/inputQueueManager.js.map +0 -1
  308. package/dist/shell/metricsTracker.d.ts +0 -60
  309. package/dist/shell/metricsTracker.d.ts.map +0 -1
  310. package/dist/shell/metricsTracker.js +0 -119
  311. package/dist/shell/metricsTracker.js.map +0 -1
  312. package/dist/shell/streamingOutputManager.d.ts +0 -115
  313. package/dist/shell/streamingOutputManager.d.ts.map +0 -1
  314. package/dist/shell/streamingOutputManager.js +0 -225
  315. package/dist/shell/streamingOutputManager.js.map +0 -1
  316. package/dist/tools/securityTools.d.ts +0 -22
  317. package/dist/tools/securityTools.d.ts.map +0 -1
  318. package/dist/tools/securityTools.js +0 -448
  319. package/dist/tools/securityTools.js.map +0 -1
  320. package/dist/ui/persistentPrompt.d.ts +0 -50
  321. package/dist/ui/persistentPrompt.d.ts.map +0 -1
  322. package/dist/ui/persistentPrompt.js +0 -92
  323. package/dist/ui/persistentPrompt.js.map +0 -1
  324. package/dist/ui/terminalUISchema.d.ts +0 -195
  325. package/dist/ui/terminalUISchema.d.ts.map +0 -1
  326. package/dist/ui/terminalUISchema.js +0 -113
  327. package/dist/ui/terminalUISchema.js.map +0 -1
  328. package/scripts/deploy-security-capabilities.js +0 -178
@@ -2,7 +2,7 @@ import { stdin as input, stdout as output, exit } from 'node:process';
2
2
  import { exec } from 'node:child_process';
3
3
  import { promisify } from 'node:util';
4
4
  import { display } from '../ui/display.js';
5
- import { theme, formatUserPrompt } from '../ui/theme.js';
5
+ import { theme } from '../ui/theme.js';
6
6
  import { getContextWindowTokens } from '../core/contextWindow.js';
7
7
  import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
8
8
  import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, } from '../core/preferences.js';
@@ -19,11 +19,11 @@ import { SkillRepository } from '../skills/skillRepository.js';
19
19
  import { createSkillTools } from '../tools/skillTools.js';
20
20
  import { FileChangeTracker } from './fileChangeTracker.js';
21
21
  import { formatShortcutsHelp } from '../ui/shortcutsHelp.js';
22
- import { MetricsTracker } from './metricsTracker.js';
22
+ import { MetricsTracker } from '../core/metricsTracker.js';
23
23
  import { listAvailablePlugins } from '../plugins/index.js';
24
- import { loadMemory, listMemoryPaths, getDefaultProjectMemoryPath, getUserMemoryEditPath, } from '../core/memorySystem.js';
25
24
  import { TerminalInputAdapter } from './terminalInputAdapter.js';
26
- import { isUpdateInProgress } from './updateManager.js';
25
+ import { renderSessionFrame } from '../ui/unified/layout.js';
26
+ import { isUpdateInProgress, maybeOfferCliUpdate } from './updateManager.js';
27
27
  import { writeLock } from '../ui/writeLock.js';
28
28
  import { enterStreamingMode, exitStreamingMode } from '../ui/globalWriteLock.js';
29
29
  const execAsync = promisify(exec);
@@ -35,6 +35,7 @@ const DROPDOWN_COLORS = [
35
35
  theme.success,
36
36
  theme.warning,
37
37
  ];
38
+ const STREAMING_SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
38
39
  // Load MODEL_PRESETS from centralized schema
39
40
  const MODEL_PRESETS = getModels().map((model) => ({
40
41
  id: model.id,
@@ -49,11 +50,13 @@ const MODEL_PRESETS = getModels().map((model) => ({
49
50
  const BASE_SLASH_COMMANDS = getSlashCommands().map((cmd) => ({
50
51
  command: cmd.command,
51
52
  description: cmd.description,
53
+ category: cmd.category,
52
54
  }));
53
55
  // Load PROVIDER_LABELS from centralized schema
54
56
  const PROVIDER_LABELS = Object.fromEntries(getProviders().map((provider) => [provider.id, provider.label]));
55
57
  // Allow enough time for paste detection to kick in before flushing buffered lines
56
58
  const CONTEXT_USAGE_THRESHOLD = 0.9;
59
+ const CONTEXT_AUTOCOMPACT_PERCENT = Math.round(CONTEXT_USAGE_THRESHOLD * 100);
57
60
  const CONTEXT_RECENT_MESSAGE_COUNT = 12;
58
61
  const CONTEXT_CLEANUP_CHARS_PER_CHUNK = 6000;
59
62
  const CONTEXT_CLEANUP_MAX_OUTPUT_TOKENS = 800;
@@ -94,11 +97,12 @@ export class InteractiveShell {
94
97
  uiAdapter;
95
98
  uiUpdates;
96
99
  _fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
97
- sessionMetrics; // Session performance tracking
100
+ alphaZeroMetrics; // Alpha Zero 2 performance tracking
98
101
  statusSubscription = null;
99
102
  followUpQueue = [];
100
103
  isDrainingQueue = false;
101
104
  activeContextWindowTokens = null;
105
+ latestTokenUsage = { used: null, limit: null };
102
106
  sessionPreferences;
103
107
  autosaveEnabled;
104
108
  autoContinueEnabled;
@@ -129,6 +133,9 @@ export class InteractiveShell {
129
133
  statusLineState = null;
130
134
  statusMessageOverride = null;
131
135
  promptRefreshTimer = null;
136
+ launchPaletteShown = false;
137
+ version;
138
+ alternateScreenEnabled;
132
139
  constructor(config) {
133
140
  this.profile = config.profile;
134
141
  this.profileLabel = config.profileLabel;
@@ -142,6 +149,8 @@ export class InteractiveShell {
142
149
  this.autoContinueEnabled = this.sessionPreferences.autoContinue;
143
150
  this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
144
151
  this._enabledPlugins = config.enabledPlugins ?? [];
152
+ this.version = config.version ?? '0.0.0';
153
+ this.alternateScreenEnabled = config.alternateScreen ?? true;
145
154
  this.initializeSessionHistory();
146
155
  this.sessionState = {
147
156
  provider: config.initialModel.provider,
@@ -162,6 +171,7 @@ export class InteractiveShell {
162
171
  this.slashCommands.push({
163
172
  command: '/agents',
164
173
  description: 'Select the default agent profile (applies on next launch)',
174
+ category: 'configuration',
165
175
  });
166
176
  }
167
177
  this.customCommands = loadCustomSlashCommands();
@@ -170,18 +180,21 @@ export class InteractiveShell {
170
180
  this.slashCommands.push({
171
181
  command: custom.command,
172
182
  description: `${custom.description} (custom)`,
183
+ category: custom.category ?? 'other',
173
184
  });
174
185
  }
175
186
  if (!this.slashCommands.some((cmd) => cmd.command === '/exit')) {
176
187
  this.slashCommands.push({
177
188
  command: '/exit',
178
189
  description: 'Quit the CLI immediately',
190
+ category: 'other',
179
191
  });
180
192
  }
181
193
  // Add /plugins command
182
194
  this.slashCommands.push({
183
195
  command: '/plugins',
184
196
  description: 'Show available and loaded plugins',
197
+ category: 'configuration',
185
198
  });
186
199
  this.statusTracker = config.statusTracker;
187
200
  this.ui = config.ui;
@@ -213,20 +226,28 @@ export class InteractiveShell {
213
226
  onEditModeChange: (mode) => this.handleEditModeChange(mode),
214
227
  onToggleVerify: () => this.toggleVerificationMode(),
215
228
  onToggleAutoContinue: () => this.toggleAutoContinueMode(),
229
+ onToggleThinking: () => this.cycleThinkingMode(),
230
+ onClearContext: () => this.handleClearContext(),
216
231
  });
217
- // Register output interceptor for cursor positioning during streaming
218
- this.terminalInput.registerOutputInterceptor(display);
219
- // Set banner content to be written during unified UI initialization
220
- this.terminalInput.setBannerContent(display.getBannerContent());
221
- // Initialize unified UI - clears screen, sets up scroll region, writes banner,
222
- // and renders input area at bottom
223
- this.terminalInput.initializeUnifiedUI();
224
232
  // Initialize Alpha Zero 2 metrics tracking
225
- this.sessionMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
233
+ this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
226
234
  this.setupStatusTracking();
227
235
  this.refreshContextGauge();
236
+ // Start terminal input (sets up handlers)
228
237
  this.terminalInput.start();
238
+ // Enter alternate screen buffer when enabled, otherwise clear the main screen for layout
239
+ if (this.alternateScreenEnabled) {
240
+ this.terminalInput.enterAlternateScreen();
241
+ }
242
+ else if (output.isTTY) {
243
+ this.terminalInput.clearScreen();
244
+ }
245
+ // Stream banner first - this sets up scroll region dynamically
246
+ const banner = this.buildBanner();
247
+ this.terminalInput.streamContent(banner + '\n\n');
248
+ // Render chat box after banner is streamed
229
249
  this.refreshControlBar();
250
+ this.terminalInput.forceRender();
230
251
  this.rebuildAgent();
231
252
  this.setupHandlers();
232
253
  this.refreshBannerSessionInfo();
@@ -246,6 +267,10 @@ export class InteractiveShell {
246
267
  this.activeSessionId = stored.id;
247
268
  this.activeSessionTitle = stored.title;
248
269
  this.sessionResumeNotice = `Resumed session "${stored.title}".`;
270
+ // Restore scrollback buffer if available
271
+ if (stored.scrollbackBuffer && Array.isArray(stored.scrollbackBuffer)) {
272
+ this.terminalInput.loadScrollbackBuffer(stored.scrollbackBuffer);
273
+ }
249
274
  return;
250
275
  }
251
276
  display.showWarning(`Session "${this.sessionRestoreConfig.sessionId}" not found. Starting fresh session.`);
@@ -259,6 +284,10 @@ export class InteractiveShell {
259
284
  this.activeSessionId = null;
260
285
  this.activeSessionTitle = autosave.title;
261
286
  this.sessionResumeNotice = 'Restored last autosaved session.';
287
+ // Restore scrollback buffer if available
288
+ if (autosave.scrollbackBuffer && Array.isArray(autosave.scrollbackBuffer)) {
289
+ this.terminalInput.loadScrollbackBuffer(autosave.scrollbackBuffer);
290
+ }
262
291
  return;
263
292
  }
264
293
  display.showWarning('No autosaved session found. Starting fresh session.');
@@ -273,14 +302,24 @@ export class InteractiveShell {
273
302
  this.sessionResumeNotice = null;
274
303
  }
275
304
  async start(initialPrompt) {
305
+ // Check for updates in background (non-blocking)
306
+ if (this.version) {
307
+ void maybeOfferCliUpdate(this.version);
308
+ }
276
309
  if (initialPrompt) {
277
310
  this.logUserPrompt(initialPrompt);
278
311
  await this.processInputBlock(initialPrompt);
279
312
  return;
280
313
  }
314
+ this.showLaunchCommandPalette();
281
315
  // Ensure the terminal input is visible
282
316
  this.terminalInput.render();
283
317
  }
318
+ showLaunchCommandPalette() {
319
+ // Disabled: Quick commands palette takes up too much space
320
+ // Users can type /help to see available commands
321
+ this.launchPaletteShown = true;
322
+ }
284
323
  /**
285
324
  * TerminalInputAdapter submit handler
286
325
  */
@@ -294,9 +333,8 @@ export class InteractiveShell {
294
333
  this.handleInputChange('');
295
334
  return;
296
335
  }
297
- // Enter streaming mode BEFORE logging prompt - this positions cursor correctly
298
- // so content appears right after the banner, not at bottom with blank space above
299
- this.terminalInput.setStreaming(true);
336
+ // DON'T clear the input here - keep it visible while streaming.
337
+ // The input will be cleared after streaming completes in the finally block.
300
338
  this.logUserPrompt(approved);
301
339
  void this.processInputBlock(approved).catch((err) => {
302
340
  display.showError(err instanceof Error ? err.message : String(err), err);
@@ -386,6 +424,59 @@ export class InteractiveShell {
386
424
  : 'The model will not be auto-prompted to continue.') +
387
425
  ' Toggle with alt+c.');
388
426
  }
427
+ /**
428
+ * Cycle through thinking modes (Alt+T keyboard shortcut).
429
+ */
430
+ cycleThinkingMode() {
431
+ const modes = ['concise', 'balanced', 'extended'];
432
+ const currentIndex = modes.indexOf(this.thinkingMode);
433
+ const nextIndex = (currentIndex + 1) % modes.length;
434
+ const nextMode = modes[nextIndex];
435
+ this.thinkingMode = nextMode;
436
+ saveSessionPreferences({ thinkingMode: this.thinkingMode });
437
+ this.refreshControlBar();
438
+ const descriptions = {
439
+ concise: 'Minimal reasoning, faster responses',
440
+ balanced: 'Default reasoning depth',
441
+ extended: 'Deep reasoning, thorough analysis',
442
+ };
443
+ display.showInfo(`Thinking mode: ${theme.info(nextMode)} - ${descriptions[nextMode]}. (Alt+T to cycle)`);
444
+ }
445
+ /**
446
+ * Handle context clear/compact request (Alt+X keyboard shortcut).
447
+ */
448
+ handleClearContext() {
449
+ if (this.isProcessing) {
450
+ display.showWarning('Cannot clear context while processing. Wait for completion or press Ctrl+C.');
451
+ return;
452
+ }
453
+ // Trigger context compaction
454
+ display.showInfo('Compacting context... This will summarize the conversation and free up space.');
455
+ void this.performContextCompaction();
456
+ }
457
+ /**
458
+ * Perform context compaction by summarizing conversation history.
459
+ */
460
+ async performContextCompaction() {
461
+ try {
462
+ // For now, just clear the history and show a message
463
+ // A full implementation would summarize the conversation
464
+ const oldLength = this.cachedHistory.length;
465
+ if (oldLength === 0) {
466
+ display.showInfo('Context is already empty.');
467
+ return;
468
+ }
469
+ // Keep the last few messages for continuity
470
+ const keepCount = Math.min(4, oldLength);
471
+ this.cachedHistory = this.cachedHistory.slice(-keepCount);
472
+ display.showSuccess(`Context compacted: ${oldLength} messages reduced to ${keepCount}. ` +
473
+ `Context usage reset. Continue your conversation.`);
474
+ this.refreshControlBar();
475
+ }
476
+ catch (error) {
477
+ display.showError('Failed to compact context', error);
478
+ }
479
+ }
389
480
  /**
390
481
  * Gate submissions when edit permission mode is active.
391
482
  * Returns the text to send when approved, or null when waiting for confirmation.
@@ -485,6 +576,10 @@ export class InteractiveShell {
485
576
  return;
486
577
  }
487
578
  this.shuttingDown = true;
579
+ const shouldRestoreScrollback = this.alternateScreenEnabled && this.terminalInput.isAlternateScreenActive();
580
+ const scrollbackSnapshot = shouldRestoreScrollback
581
+ ? this.terminalInput.getScrollbackSnapshot()
582
+ : null;
488
583
  // Stop any active spinner to prevent process hang
489
584
  display.stopThinking(false);
490
585
  this.stopStreamingHeartbeat();
@@ -493,18 +588,34 @@ export class InteractiveShell {
493
588
  this.teardownStatusTracking();
494
589
  // Clear any pending cleanup to prevent hanging
495
590
  this.pendingCleanup = null;
591
+ // Reset terminal state before disposing adapters
592
+ this.terminalInput.exitStreamingScrollRegion();
593
+ if (this.alternateScreenEnabled) {
594
+ this.terminalInput.exitAlternateScreen();
595
+ }
496
596
  // Dispose terminal input handler
497
597
  this.terminalInput.dispose();
498
598
  // Dispose unified UI adapter
499
599
  this.uiAdapter.dispose();
600
+ if (scrollbackSnapshot && scrollbackSnapshot.length > 0) {
601
+ this.restoreScrollbackSnapshot(scrollbackSnapshot);
602
+ }
500
603
  display.newLine();
501
- const version = display.getVersion() || 'unknown';
502
- console.log(theme.gradient.warm(''.repeat(50)));
503
- console.log(` ${theme.gradient.cool('✨ Goodbye!')} ${theme.ui.muted('·')} ${theme.info(`erosolar-cli v${version}`)}`);
504
- console.log(` ${theme.ui.muted('Support:')} ${theme.info('support@ero.solar')}`);
505
- console.log(theme.gradient.warm('━'.repeat(50)));
604
+ console.log(theme.ui.muted(''.repeat(44)));
605
+ console.log(theme.ui.muted(' Goodbye! · support@ero.solar'));
606
+ console.log(theme.ui.muted(''.repeat(44)));
506
607
  exit(0);
507
608
  }
609
+ restoreScrollbackSnapshot(lines) {
610
+ if (!lines.length) {
611
+ return;
612
+ }
613
+ const transcript = lines.join('\n');
614
+ const separator = theme.ui.muted('─'.repeat(44));
615
+ const header = theme.ui.muted('Restored scrollback from this session:');
616
+ // Write directly to stdout after exiting alternate screen to preserve the transcript
617
+ process.stdout.write(`\n${separator}\n${header}\n${transcript}\n${separator}\n`);
618
+ }
508
619
  /**
509
620
  * Update status bar message
510
621
  */
@@ -672,13 +783,14 @@ export class InteractiveShell {
672
783
  });
673
784
  }
674
785
  setProcessingStatus(detail) {
786
+ this.latestTokenUsage = { used: null, limit: this.latestTokenUsage.limit };
675
787
  this.statusTracker.setBase('Working on your request', {
676
788
  detail: this.describeStatusDetail(detail),
677
789
  tone: 'info',
678
790
  });
679
791
  }
680
792
  describeStatusDetail(detail) {
681
- const parts = [detail?.trim() || this.describeModelDetail()];
793
+ const parts = detail?.trim() ? [detail.trim()] : [];
682
794
  const queued = this.followUpQueue.length;
683
795
  if (queued > 0) {
684
796
  parts.push(`${queued} follow-up${queued === 1 ? '' : 's'} queued`);
@@ -691,12 +803,18 @@ export class InteractiveShell {
691
803
  }
692
804
  refreshContextGauge() {
693
805
  const tokens = getContextWindowTokens(this.sessionState.model);
694
- this.activeContextWindowTokens =
695
- typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
806
+ const normalizedTokens = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
807
+ this.activeContextWindowTokens = normalizedTokens;
808
+ if (normalizedTokens !== null) {
809
+ this.latestTokenUsage = {
810
+ used: this.latestTokenUsage.used,
811
+ limit: normalizedTokens,
812
+ };
813
+ }
696
814
  }
697
815
  updateContextUsage(percentage) {
698
816
  this.uiAdapter.updateContextUsage(percentage);
699
- this.terminalInput.setContextUsage(percentage);
817
+ this.terminalInput.setContextUsage(percentage, CONTEXT_AUTOCOMPACT_PERCENT);
700
818
  }
701
819
  refreshControlBar() {
702
820
  this.terminalInput.setModeToggles({
@@ -704,9 +822,9 @@ export class InteractiveShell {
704
822
  autoContinueEnabled: this.autoContinueEnabled,
705
823
  verificationHotkey: 'alt+v',
706
824
  autoContinueHotkey: 'alt+c',
825
+ thinkingModeLabel: this.thinkingMode,
826
+ thinkingHotkey: '/thinking',
707
827
  });
708
- // Update persistent model info display
709
- this.terminalInput.setModelInfo(this.describeModelDetail());
710
828
  this.refreshStatusLine();
711
829
  this.terminalInput.render();
712
830
  }
@@ -737,6 +855,25 @@ export class InteractiveShell {
737
855
  // Set main status (tool execution, etc.) - shown when not overridden
738
856
  const statusText = this.formatStatusLine(this.statusLineState);
739
857
  this.terminalInput.setStatusMessage(statusText);
858
+ // Surface meta header (elapsed + context usage) above the divider
859
+ const elapsedSeconds = this.statusLineState
860
+ ? Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000))
861
+ : null;
862
+ const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
863
+ const tokensUsed = this.latestTokenUsage.used;
864
+ const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
865
+ this.terminalInput.setMetaStatus({
866
+ elapsedSeconds,
867
+ tokensUsed,
868
+ tokenLimit,
869
+ thinkingMs,
870
+ thinkingHasContent: display.isSpinnerActive(),
871
+ });
872
+ // Keep model/provider visible in the controls bar
873
+ this.terminalInput.setModelContext({
874
+ model: this.sessionState.model,
875
+ provider: this.providerLabel(this.sessionState.provider),
876
+ });
740
877
  if (forceRender) {
741
878
  this.terminalInput.render();
742
879
  }
@@ -796,15 +933,14 @@ export class InteractiveShell {
796
933
  this.terminalInput.render();
797
934
  }
798
935
  /**
799
- * Log the user's prompt as a visible message in the conversation.
800
- * This creates a persistent log entry that remains visible during and after streaming.
936
+ * Log user prompt to the scroll region so it's part of the conversation flow.
801
937
  */
802
938
  logUserPrompt(text) {
803
- // Display the user's prompt with the standard prefix
804
- const prefix = formatUserPrompt();
805
- display.stream(`\n${prefix}${text}\n\n`);
806
- // Update content end row so chat box renders below the user prompt
807
- this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
939
+ if (!text.trim())
940
+ return;
941
+ // Format with user prompt prefix and write to scroll region
942
+ const formatted = `${theme.user('>')} ${text}\n`;
943
+ this.terminalInput.writeToScrollRegion(formatted);
808
944
  }
809
945
  requestPromptRefresh(force = false) {
810
946
  if (force) {
@@ -829,16 +965,40 @@ export class InteractiveShell {
829
965
  this.stopStreamingHeartbeat();
830
966
  // Enter global streaming mode - blocks all non-streaming UI output
831
967
  enterStreamingMode();
968
+ // Set up scroll region for streaming content
969
+ this.terminalInput.enterStreamingScrollRegion();
832
970
  this.uiUpdates.setMode('streaming');
833
971
  this.streamingHeartbeatStart = Date.now();
834
972
  this.streamingHeartbeatFrame = 0;
835
- // Note: We don't start a heartbeat during streaming anymore
836
- // because the UI shouldn't be rendering during streaming.
837
- // The streaming status is shown in the streaming header instead.
973
+ const initialFrame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
974
+ this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
975
+ display.updateStreamingStatus(this.streamingStatusLabel);
976
+ this.refreshStatusLine(true);
977
+ // Periodically refresh the pinned input/status region while streaming so
978
+ // elapsed time remains visible without interrupting the scroll region.
979
+ this.uiUpdates.startHeartbeat('streaming', {
980
+ intervalMs: 1000,
981
+ lane: 'heartbeat',
982
+ mode: ['streaming', 'processing'],
983
+ coalesceKey: 'streaming:heartbeat',
984
+ run: () => {
985
+ const elapsedSeconds = this.streamingHeartbeatStart
986
+ ? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
987
+ : 0;
988
+ this.streamingHeartbeatFrame =
989
+ (this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
990
+ const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
991
+ this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
992
+ display.updateStreamingStatus(this.streamingStatusLabel);
993
+ this.refreshStatusLine(true);
994
+ },
995
+ });
838
996
  }
839
997
  stopStreamingHeartbeat() {
840
998
  // Exit global streaming mode - allows UI to render again
841
999
  exitStreamingMode();
1000
+ // Exit scroll region mode
1001
+ this.terminalInput.exitStreamingScrollRegion();
842
1002
  this.uiUpdates.stopHeartbeat('streaming');
843
1003
  this.streamingHeartbeatStart = null;
844
1004
  this.streamingHeartbeatFrame = 0;
@@ -850,10 +1010,28 @@ export class InteractiveShell {
850
1010
  // Force refresh to update the input area now that streaming has ended
851
1011
  this.refreshStatusLine(true);
852
1012
  }
853
- buildStreamingStatus(label) {
1013
+ buildStreamingStatus(label, elapsedSeconds) {
854
1014
  const detail = this.describeModelDetail();
855
- const prefix = theme.info('');
856
- return detail ? `${prefix} ${label} ${theme.ui.muted('·')} ${detail}` : `${prefix} ${label}`;
1015
+ const elapsedLabel = typeof elapsedSeconds === 'number' && elapsedSeconds >= 0
1016
+ ? theme.ui.muted(this.formatElapsedShort(elapsedSeconds))
1017
+ : null;
1018
+ const prefix = theme.info('⏺');
1019
+ const parts = [label];
1020
+ if (detail) {
1021
+ parts.push(theme.ui.muted('·'), detail);
1022
+ }
1023
+ if (elapsedLabel) {
1024
+ parts.push(theme.ui.muted('·'), elapsedLabel);
1025
+ }
1026
+ return `${prefix} ${parts.join(' ')}`.trim();
1027
+ }
1028
+ formatElapsedShort(seconds) {
1029
+ if (seconds < 60) {
1030
+ return `${seconds}s`;
1031
+ }
1032
+ const minutes = Math.floor(seconds / 60);
1033
+ const remaining = seconds % 60;
1034
+ return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
857
1035
  }
858
1036
  refreshQueueIndicators() {
859
1037
  if (this.isProcessing) {
@@ -1101,17 +1279,6 @@ export class InteractiveShell {
1101
1279
  case '/discover':
1102
1280
  await this.discoverModelsCommand();
1103
1281
  break;
1104
- case '/memory':
1105
- this.handleMemoryCommand(input);
1106
- break;
1107
- case '/clear':
1108
- display.clear();
1109
- this.cachedHistory = [];
1110
- display.showInfo('Conversation cleared.');
1111
- break;
1112
- case '/help':
1113
- this.showHelp();
1114
- break;
1115
1282
  default:
1116
1283
  if (!(await this.tryCustomSlashCommand(command, input))) {
1117
1284
  display.showWarning(`Unknown command "${command}".`);
@@ -1420,99 +1587,6 @@ export class InteractiveShell {
1420
1587
  // Display keyboard shortcuts help (Claude Code style)
1421
1588
  display.showSystemMessage(formatShortcutsHelp());
1422
1589
  }
1423
- handleMemoryCommand(input) {
1424
- const tokens = input.trim().split(/\s+/).slice(1);
1425
- const action = (tokens.shift() ?? 'show').toLowerCase();
1426
- switch (action) {
1427
- case '':
1428
- case 'show':
1429
- case 'list': {
1430
- this.showMemoryStatus();
1431
- break;
1432
- }
1433
- case 'paths': {
1434
- this.showMemoryPaths();
1435
- break;
1436
- }
1437
- case 'edit': {
1438
- const level = (tokens[0] ?? 'project').toLowerCase();
1439
- this.openMemoryForEdit(level);
1440
- break;
1441
- }
1442
- default:
1443
- display.showWarning('Usage: /memory [show|paths|edit <user|project>]');
1444
- break;
1445
- }
1446
- }
1447
- showMemoryStatus() {
1448
- const memory = loadMemory(this.workingDir);
1449
- const lines = [];
1450
- lines.push(theme.bold('Persistent Memory'));
1451
- lines.push('');
1452
- if (memory.sources.length === 0) {
1453
- lines.push(theme.secondary('No memory files found.'));
1454
- lines.push('');
1455
- lines.push(`Create ${theme.info('EROSOLAR.md')} in your project to add persistent context.`);
1456
- lines.push(`Use ${theme.info('/memory edit project')} to create one.`);
1457
- }
1458
- else {
1459
- for (const source of memory.sources) {
1460
- const levelLabel = source.level === 'enterprise' ? 'Enterprise' :
1461
- source.level === 'user' ? 'User' : 'Project';
1462
- const preview = source.content.slice(0, 200).replace(/\n/g, ' ').trim();
1463
- const truncated = source.content.length > 200 ? '...' : '';
1464
- lines.push(`${theme.success('●')} ${theme.bold(levelLabel)}: ${source.path}`);
1465
- lines.push(` ${theme.dim(preview + truncated)}`);
1466
- lines.push('');
1467
- }
1468
- if (memory.importedPaths.length > memory.sources.length) {
1469
- lines.push(theme.secondary(`Imported ${memory.importedPaths.length - memory.sources.length} additional files via @imports.`));
1470
- }
1471
- }
1472
- display.showSystemMessage(lines.join('\n'));
1473
- }
1474
- showMemoryPaths() {
1475
- const paths = listMemoryPaths(this.workingDir);
1476
- const lines = [];
1477
- lines.push(theme.bold('Memory File Locations'));
1478
- lines.push('');
1479
- for (const { level, path, exists } of paths) {
1480
- const icon = exists ? theme.success('✓') : theme.dim('○');
1481
- const levelLabel = level.charAt(0).toUpperCase() + level.slice(1);
1482
- lines.push(`${icon} ${levelLabel}: ${path}`);
1483
- }
1484
- lines.push('');
1485
- lines.push(theme.secondary('Create any of these files to add persistent memory.'));
1486
- lines.push(theme.secondary('Use @path/to/file.md syntax to import other files.'));
1487
- display.showSystemMessage(lines.join('\n'));
1488
- }
1489
- openMemoryForEdit(level) {
1490
- let targetPath;
1491
- if (level === 'user') {
1492
- targetPath = getUserMemoryEditPath();
1493
- }
1494
- else if (level === 'project') {
1495
- targetPath = getDefaultProjectMemoryPath(this.workingDir);
1496
- }
1497
- else {
1498
- display.showWarning('Specify "user" or "project" to edit. Enterprise memory is read-only.');
1499
- return;
1500
- }
1501
- display.showInfo(`Memory file: ${targetPath}`);
1502
- display.showInfo('Create or edit this file to add persistent context for the AI.');
1503
- display.showInfo('');
1504
- display.showInfo('Example EROSOLAR.md content:');
1505
- display.showInfo('');
1506
- display.showInfo(theme.dim(`# Project Guidelines
1507
-
1508
- When working in this codebase:
1509
- - Follow TypeScript strict mode conventions
1510
- - Use functional patterns where appropriate
1511
- - Run tests before committing
1512
-
1513
- @./docs/coding-standards.md
1514
- `));
1515
- }
1516
1590
  showFileChangeSummary() {
1517
1591
  const summary = this._fileChangeTracker.getSummary();
1518
1592
  const changes = this._fileChangeTracker.getAllChanges();
@@ -1555,11 +1629,11 @@ When working in this codebase:
1555
1629
  display.showSystemMessage(lines.join('\n'));
1556
1630
  }
1557
1631
  showAlphaZeroMetrics() {
1558
- const summary = this.sessionMetrics.getPerformanceSummary();
1632
+ const summary = this.alphaZeroMetrics.getPerformanceSummary();
1559
1633
  display.showSystemMessage(summary);
1560
1634
  }
1561
1635
  showImprovementSuggestions() {
1562
- const suggestions = this.sessionMetrics.getImprovementSuggestions();
1636
+ const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
1563
1637
  if (suggestions.length === 0) {
1564
1638
  display.showInfo('No improvement suggestions at this time. Keep using the shell to generate metrics!');
1565
1639
  return;
@@ -1605,7 +1679,9 @@ When working in this codebase:
1605
1679
  }
1606
1680
  }
1607
1681
  lines.push(theme.secondary('CLI Flags:'));
1682
+ lines.push(' --alpha-zero Enable Alpha Zero 2 RL framework');
1608
1683
  lines.push(' --coding Enable enhanced coding tools');
1684
+ lines.push(' --security Enable security research tools');
1609
1685
  lines.push(' --all-plugins Enable all optional plugins');
1610
1686
  display.showSystemMessage(lines.join('\n'));
1611
1687
  }
@@ -1654,6 +1730,7 @@ When working in this codebase:
1654
1730
  model: this.sessionState.model,
1655
1731
  workspaceRoot: this.workingDir,
1656
1732
  messages: history,
1733
+ scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
1657
1734
  });
1658
1735
  this.cachedHistory = history;
1659
1736
  this.updateActiveSession(summary, true);
@@ -1832,6 +1909,7 @@ When working in this codebase:
1832
1909
  workspaceRoot: this.workingDir,
1833
1910
  title: this.activeSessionTitle,
1834
1911
  messages: this.cachedHistory,
1912
+ scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
1835
1913
  });
1836
1914
  }
1837
1915
  describeWorkspaceOptions() {
@@ -1849,6 +1927,75 @@ When working in this codebase:
1849
1927
  }
1850
1928
  return `${warning.label}: ${warning.reason}.`;
1851
1929
  }
1930
+ buildLaunchCommandPalette() {
1931
+ const entries = [];
1932
+ const secretsSummary = this.summarizeSecretsForPalette();
1933
+ const toolSummary = this.getToolSelectionSummary();
1934
+ const autosaveLabel = this.autosaveEnabled ? 'on' : 'off';
1935
+ for (const command of this.slashCommands) {
1936
+ const entry = {
1937
+ command: command.command,
1938
+ description: command.description,
1939
+ category: command.category ?? 'other',
1940
+ };
1941
+ switch (command.command) {
1942
+ case '/secrets':
1943
+ if (secretsSummary.text) {
1944
+ entry.description = `${command.description} (${secretsSummary.text})`;
1945
+ entry.tone = secretsSummary.tone;
1946
+ }
1947
+ break;
1948
+ case '/tools':
1949
+ if (toolSummary) {
1950
+ entry.description = `${command.description} (${toolSummary})`;
1951
+ }
1952
+ break;
1953
+ case '/sessions':
1954
+ entry.description = `${command.description} (autosave ${autosaveLabel})`;
1955
+ break;
1956
+ case '/model':
1957
+ entry.description = `${command.description} (current: ${this.sessionState.model})`;
1958
+ break;
1959
+ case '/provider':
1960
+ entry.description = `${command.description} (current: ${this.providerLabel(this.sessionState.provider)})`;
1961
+ break;
1962
+ default:
1963
+ break;
1964
+ }
1965
+ entries.push(entry);
1966
+ }
1967
+ return entries;
1968
+ }
1969
+ summarizeSecretsForPalette() {
1970
+ const definitions = listSecretDefinitions();
1971
+ if (!definitions.length) {
1972
+ return { text: null };
1973
+ }
1974
+ const missing = definitions.filter((definition) => !getSecretValue(definition.id));
1975
+ if (missing.length === 0) {
1976
+ return { text: 'all configured', tone: 'success' };
1977
+ }
1978
+ const labels = missing.map((definition) => definition.label ?? definition.id);
1979
+ return { text: `missing ${this.formatList(labels)}`, tone: 'warn' };
1980
+ }
1981
+ getToolSelectionSummary() {
1982
+ const toolSettings = loadToolSettings();
1983
+ const selection = buildEnabledToolSet(toolSettings);
1984
+ const options = getToolToggleOptions();
1985
+ if (!options.length) {
1986
+ return null;
1987
+ }
1988
+ const enabledCount = options.filter((option) => selection.has(option.id)).length;
1989
+ return `${enabledCount}/${options.length} enabled`;
1990
+ }
1991
+ formatList(values, maxItems = 3) {
1992
+ if (!values.length) {
1993
+ return '';
1994
+ }
1995
+ const shown = values.slice(0, maxItems);
1996
+ const suffix = values.length > maxItems ? ', …' : '';
1997
+ return `${shown.join(', ')}${suffix}`;
1998
+ }
1852
1999
  buildSlashCommandList(header) {
1853
2000
  const lines = [theme.gradient.primary(header), ''];
1854
2001
  for (const command of this.slashCommands) {
@@ -2317,7 +2464,7 @@ When working in this codebase:
2317
2464
  this.autosaveIfEnabled();
2318
2465
  // Track metrics with Alpha Zero 2
2319
2466
  const elapsedMs = Date.now() - requestStartTime;
2320
- this.sessionMetrics.recordMessage(elapsedMs);
2467
+ this.alphaZeroMetrics.recordMessage(elapsedMs);
2321
2468
  if (!responseText?.trim()) {
2322
2469
  display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
2323
2470
  }
@@ -2335,14 +2482,10 @@ When working in this codebase:
2335
2482
  this.stopStreamingHeartbeat();
2336
2483
  this.isProcessing = false;
2337
2484
  this.terminalInput.setStreaming(false);
2338
- this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2339
2485
  this.uiAdapter.endProcessing('Ready for prompts');
2340
2486
  this.setIdleStatus();
2341
2487
  display.newLine();
2342
2488
  this.updateStatusMessage(null);
2343
- // Claude Code style: Show unified status bar before prompt
2344
- // This creates consistent UI between startup and post-streaming
2345
- this.showUnifiedStatusBar();
2346
2489
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
2347
2490
  // CRITICAL: Ensure readline prompt is active for user input
2348
2491
  // Claude Code style: New prompt naturally appears at bottom
@@ -2419,13 +2562,14 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
2419
2562
  try {
2420
2563
  // Send the request and capture the response (streaming disabled)
2421
2564
  display.showThinking('Responding...');
2565
+ this.refreshStatusLine(true);
2422
2566
  const response = await agent.send(currentPrompt, true);
2423
2567
  await this.awaitPendingCleanup();
2424
2568
  this.captureHistorySnapshot();
2425
2569
  this.autosaveIfEnabled();
2426
2570
  // Track metrics
2427
2571
  const elapsedMs = Date.now() - overallStartTime;
2428
- this.sessionMetrics.recordMessage(elapsedMs);
2572
+ this.alphaZeroMetrics.recordMessage(elapsedMs);
2429
2573
  if (!response?.trim()) {
2430
2574
  display.showWarning('Model returned an empty response. Retrying this iteration...');
2431
2575
  consecutiveNoProgress++;
@@ -2562,7 +2706,6 @@ What's the next action?`;
2562
2706
  this.stopStreamingHeartbeat();
2563
2707
  this.isProcessing = false;
2564
2708
  this.terminalInput.setStreaming(false);
2565
- this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2566
2709
  this.uiAdapter.endProcessing('Ready for prompts');
2567
2710
  this.setIdleStatus();
2568
2711
  this.updateStatusMessage(null);
@@ -2919,8 +3062,10 @@ What's the next action?`;
2919
3062
  try {
2920
3063
  // Send the error to the agent for fixing
2921
3064
  display.showThinking('Analyzing build errors');
3065
+ this.refreshStatusLine(true);
2922
3066
  const response = await this.agent.send(prompt, true);
2923
3067
  display.stopThinking();
3068
+ this.refreshStatusLine(true);
2924
3069
  if (response) {
2925
3070
  display.showAssistantMessage(response, { isFinal: true });
2926
3071
  }
@@ -2947,8 +3092,8 @@ What's the next action?`;
2947
3092
  };
2948
3093
  this.agent = this.runtimeSession.createAgent(selection, {
2949
3094
  onStreamChunk: (chunk) => {
2950
- // Stream output directly - no spinner during streaming to avoid race conditions
2951
- display.stream(chunk);
3095
+ // Stream output using clean streamContent() - chat box floats below
3096
+ this.terminalInput.streamContent(chunk);
2952
3097
  },
2953
3098
  onStreamFallback: (info) => this.handleStreamingFallback(info),
2954
3099
  onAssistantMessage: (content, metadata) => {
@@ -2970,18 +3115,16 @@ What's the next action?`;
2970
3115
  display.showAssistantMessage(finalContent, enriched);
2971
3116
  }
2972
3117
  }
2973
- // Show status line at end (Claude Code style: "• Context X% used • Ready for prompts (2s)")
3118
+ // Status shown in mode controls bar - no separate status line needed
2974
3119
  display.stopThinking();
2975
- // Calculate context usage
2976
- let contextInfo;
3120
+ // Update context usage for mode controls display
2977
3121
  if (enriched.contextWindowTokens && metadata.usage) {
2978
3122
  const total = this.totalTokens(metadata.usage);
2979
3123
  if (total && total > 0) {
2980
3124
  const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
2981
- contextInfo = { percentage, tokens: total };
3125
+ this.updateContextUsage(percentage);
2982
3126
  }
2983
3127
  }
2984
- display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
2985
3128
  // Auto-verify changes: build first (catches type errors), then tests
2986
3129
  void this.enforceAutoBuild('final-response');
2987
3130
  void this.enforceAutoTests('final-response');
@@ -3051,7 +3194,6 @@ What's the next action?`;
3051
3194
  this.stopStreamingHeartbeat();
3052
3195
  this.updateStatusMessage(null);
3053
3196
  this.terminalInput.setStreaming(false);
3054
- this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3055
3197
  this.terminalInput.render();
3056
3198
  },
3057
3199
  onVerificationNeeded: () => {
@@ -3088,7 +3230,6 @@ What's the next action?`;
3088
3230
  resetChatBoxAfterModelSwap() {
3089
3231
  this.updateStatusMessage(null);
3090
3232
  this.terminalInput.setStreaming(false);
3091
- this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3092
3233
  this.terminalInput.render();
3093
3234
  this.ensureReadlineReady();
3094
3235
  }
@@ -3153,9 +3294,14 @@ What's the next action?`;
3153
3294
  return null;
3154
3295
  }
3155
3296
  const usageRatio = total / windowTokens;
3297
+ this.latestTokenUsage = {
3298
+ used: total,
3299
+ limit: windowTokens,
3300
+ };
3156
3301
  // Always update context usage in the UI
3157
3302
  const percentUsed = Math.round(usageRatio * 100);
3158
3303
  this.updateContextUsage(percentUsed);
3304
+ this.refreshStatusLine(true);
3159
3305
  if (usageRatio < CONTEXT_USAGE_THRESHOLD) {
3160
3306
  return null;
3161
3307
  }
@@ -3422,6 +3568,113 @@ What's the next action?`;
3422
3568
  this.sessionState.reasoningEffort = preset.reasoningEffort;
3423
3569
  }
3424
3570
  }
3571
+ /**
3572
+ * Build the session banner with comprehensive feature status.
3573
+ */
3574
+ buildBanner() {
3575
+ const terminalWidth = output.columns ?? 100;
3576
+ const width = Math.min(terminalWidth - 4, 110);
3577
+ // Collect tool categories for display
3578
+ const toolCategories = this.collectToolCategories();
3579
+ return renderSessionFrame({
3580
+ profileLabel: this.profileLabel,
3581
+ profileName: this.profile,
3582
+ model: this.sessionState.model,
3583
+ provider: this.sessionState.provider,
3584
+ workspace: this.workingDir,
3585
+ version: this.version,
3586
+ width,
3587
+ features: {
3588
+ verification: this.verificationEnabled,
3589
+ autoContinue: this.autoContinueEnabled,
3590
+ thinkingMode: this.thinkingMode,
3591
+ plugins: this._enabledPlugins,
3592
+ tools: toolCategories,
3593
+ sessionId: this.activeSessionId ?? undefined,
3594
+ },
3595
+ });
3596
+ }
3597
+ /**
3598
+ * Collect tool categories for banner display.
3599
+ */
3600
+ collectToolCategories() {
3601
+ const categories = [];
3602
+ try {
3603
+ const providerTools = this.runtimeSession.toolRuntime.listProviderTools();
3604
+ if (providerTools.length > 0) {
3605
+ // Group by category (first word of tool name or namespace)
3606
+ const groups = new Map();
3607
+ for (const tool of providerTools) {
3608
+ const category = this.extractToolCategory(tool.name);
3609
+ groups.set(category, (groups.get(category) || 0) + 1);
3610
+ }
3611
+ // Convert to array sorted by count
3612
+ const sorted = Array.from(groups.entries())
3613
+ .sort((a, b) => b[1] - a[1])
3614
+ .slice(0, 5); // Top 5 categories
3615
+ for (const [name, count] of sorted) {
3616
+ categories.push({ name, count, icon: this.getToolCategoryIcon(name) });
3617
+ }
3618
+ }
3619
+ }
3620
+ catch {
3621
+ // Ignore errors in tool collection
3622
+ }
3623
+ return categories;
3624
+ }
3625
+ /**
3626
+ * Extract category from tool name.
3627
+ */
3628
+ extractToolCategory(toolName) {
3629
+ // Common tool prefixes
3630
+ const prefixMap = {
3631
+ git: 'Git',
3632
+ npm: 'NPM',
3633
+ bash: 'Shell',
3634
+ file: 'Files',
3635
+ read: 'Files',
3636
+ write: 'Files',
3637
+ edit: 'Files',
3638
+ search: 'Search',
3639
+ glob: 'Search',
3640
+ grep: 'Search',
3641
+ web: 'Web',
3642
+ fetch: 'Web',
3643
+ test: 'Testing',
3644
+ build: 'Build',
3645
+ deploy: 'Deploy',
3646
+ cloud: 'Cloud',
3647
+ browser: 'Browser',
3648
+ };
3649
+ const lower = toolName.toLowerCase();
3650
+ for (const [prefix, category] of Object.entries(prefixMap)) {
3651
+ if (lower.startsWith(prefix)) {
3652
+ return category;
3653
+ }
3654
+ }
3655
+ // Default to first word capitalized
3656
+ const firstWord = toolName.split(/[_\-\s]/)[0] || 'Other';
3657
+ return firstWord.charAt(0).toUpperCase() + firstWord.slice(1).toLowerCase();
3658
+ }
3659
+ /**
3660
+ * Get icon for tool category.
3661
+ */
3662
+ getToolCategoryIcon(category) {
3663
+ const icons = {
3664
+ Git: '⎇',
3665
+ NPM: '📦',
3666
+ Shell: '⌘',
3667
+ Files: '📁',
3668
+ Search: '🔍',
3669
+ Web: '🌐',
3670
+ Testing: '🧪',
3671
+ Build: '🔧',
3672
+ Deploy: '🚀',
3673
+ Cloud: '☁',
3674
+ Browser: '🌐',
3675
+ };
3676
+ return icons[category] || '⚙';
3677
+ }
3425
3678
  refreshBannerSessionInfo() {
3426
3679
  const nextState = {
3427
3680
  model: this.sessionState.model,
@@ -3432,13 +3685,11 @@ What's the next action?`;
3432
3685
  return;
3433
3686
  }
3434
3687
  this.refreshContextGauge();
3435
- display.updateSessionInfo(nextState.model, nextState.provider);
3436
- // Update the persistent model info display in terminal input
3437
- this.terminalInput.setModelInfo(this.describeModelDetail());
3688
+ // Banner is no longer stored in display - it was streamed as content
3689
+ // Model/provider changes are visible in the control bar
3438
3690
  if (!this.isProcessing) {
3439
3691
  this.setIdleStatus();
3440
3692
  }
3441
- // Pinned header rows are disabled; scroll region handles all output.
3442
3693
  this.bannerSessionState = nextState;
3443
3694
  }
3444
3695
  providerLabel(id) {