erosolar-cli 1.7.354 → 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 (328) 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 -417
  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 +125 -250
  258. package/dist/shell/terminalInput.d.ts.map +1 -1
  259. package/dist/shell/terminalInput.js +612 -1071
  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 -139
  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 +49 -200
  268. package/dist/subagents/taskRunner.js.map +1 -1
  269. package/dist/tools/securityTools.d.ts +22 -0
  270. package/dist/tools/securityTools.d.ts.map +1 -0
  271. package/dist/tools/securityTools.js +448 -0
  272. package/dist/tools/securityTools.js.map +1 -0
  273. package/dist/ui/ShellUIAdapter.d.ts +1 -7
  274. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  275. package/dist/ui/ShellUIAdapter.js +18 -42
  276. package/dist/ui/ShellUIAdapter.js.map +1 -1
  277. package/dist/ui/display.d.ts +45 -24
  278. package/dist/ui/display.d.ts.map +1 -1
  279. package/dist/ui/display.js +274 -148
  280. package/dist/ui/display.js.map +1 -1
  281. package/dist/ui/persistentPrompt.d.ts +50 -0
  282. package/dist/ui/persistentPrompt.d.ts.map +1 -0
  283. package/dist/ui/persistentPrompt.js +92 -0
  284. package/dist/ui/persistentPrompt.js.map +1 -0
  285. package/dist/ui/terminalUISchema.d.ts +195 -0
  286. package/dist/ui/terminalUISchema.d.ts.map +1 -0
  287. package/dist/ui/terminalUISchema.js +113 -0
  288. package/dist/ui/terminalUISchema.js.map +1 -0
  289. package/dist/ui/theme.d.ts.map +1 -1
  290. package/dist/ui/theme.js +8 -6
  291. package/dist/ui/theme.js.map +1 -1
  292. package/dist/ui/toolDisplay.d.ts +158 -0
  293. package/dist/ui/toolDisplay.d.ts.map +1 -1
  294. package/dist/ui/toolDisplay.js +348 -0
  295. package/dist/ui/toolDisplay.js.map +1 -1
  296. package/dist/ui/unified/layout.d.ts +0 -20
  297. package/dist/ui/unified/layout.d.ts.map +1 -1
  298. package/dist/ui/unified/layout.js +216 -105
  299. package/dist/ui/unified/layout.js.map +1 -1
  300. package/package.json +4 -4
  301. package/scripts/deploy-security-capabilities.js +178 -0
  302. package/dist/core/hooks.d.ts +0 -113
  303. package/dist/core/hooks.d.ts.map +0 -1
  304. package/dist/core/hooks.js +0 -267
  305. package/dist/core/hooks.js.map +0 -1
  306. package/dist/core/metricsTracker.d.ts +0 -122
  307. package/dist/core/metricsTracker.d.ts.map +0 -1
  308. package/dist/core/metricsTracker.js.map +0 -1
  309. package/dist/core/securityAssessment.d.ts +0 -91
  310. package/dist/core/securityAssessment.d.ts.map +0 -1
  311. package/dist/core/securityAssessment.js +0 -580
  312. package/dist/core/securityAssessment.js.map +0 -1
  313. package/dist/core/verification.d.ts +0 -137
  314. package/dist/core/verification.d.ts.map +0 -1
  315. package/dist/core/verification.js +0 -323
  316. package/dist/core/verification.js.map +0 -1
  317. package/dist/subagents/agentConfig.d.ts +0 -27
  318. package/dist/subagents/agentConfig.d.ts.map +0 -1
  319. package/dist/subagents/agentConfig.js +0 -89
  320. package/dist/subagents/agentConfig.js.map +0 -1
  321. package/dist/subagents/agentRegistry.d.ts +0 -33
  322. package/dist/subagents/agentRegistry.d.ts.map +0 -1
  323. package/dist/subagents/agentRegistry.js +0 -162
  324. package/dist/subagents/agentRegistry.js.map +0 -1
  325. package/dist/utils/frontmatter.d.ts +0 -10
  326. package/dist/utils/frontmatter.d.ts.map +0 -1
  327. package/dist/utils/frontmatter.js +0 -78
  328. package/dist/utils/frontmatter.js.map +0 -1
@@ -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 } from '../ui/theme.js';
5
+ import { theme, formatUserPrompt } 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 '../core/metricsTracker.js';
22
+ import { MetricsTracker } from './metricsTracker.js';
23
23
  import { listAvailablePlugins } from '../plugins/index.js';
24
+ import { loadMemory, listMemoryPaths, getDefaultProjectMemoryPath, getUserMemoryEditPath, } from '../core/memorySystem.js';
24
25
  import { TerminalInputAdapter } from './terminalInputAdapter.js';
25
- import { renderSessionFrame } from '../ui/unified/layout.js';
26
- import { isUpdateInProgress, maybeOfferCliUpdate } from './updateManager.js';
26
+ import { isUpdateInProgress } 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,7 +35,6 @@ const DROPDOWN_COLORS = [
35
35
  theme.success,
36
36
  theme.warning,
37
37
  ];
38
- const STREAMING_SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
39
38
  // Load MODEL_PRESETS from centralized schema
40
39
  const MODEL_PRESETS = getModels().map((model) => ({
41
40
  id: model.id,
@@ -50,13 +49,11 @@ const MODEL_PRESETS = getModels().map((model) => ({
50
49
  const BASE_SLASH_COMMANDS = getSlashCommands().map((cmd) => ({
51
50
  command: cmd.command,
52
51
  description: cmd.description,
53
- category: cmd.category,
54
52
  }));
55
53
  // Load PROVIDER_LABELS from centralized schema
56
54
  const PROVIDER_LABELS = Object.fromEntries(getProviders().map((provider) => [provider.id, provider.label]));
57
55
  // Allow enough time for paste detection to kick in before flushing buffered lines
58
56
  const CONTEXT_USAGE_THRESHOLD = 0.9;
59
- const CONTEXT_AUTOCOMPACT_PERCENT = Math.round(CONTEXT_USAGE_THRESHOLD * 100);
60
57
  const CONTEXT_RECENT_MESSAGE_COUNT = 12;
61
58
  const CONTEXT_CLEANUP_CHARS_PER_CHUNK = 6000;
62
59
  const CONTEXT_CLEANUP_MAX_OUTPUT_TOKENS = 800;
@@ -97,12 +94,11 @@ export class InteractiveShell {
97
94
  uiAdapter;
98
95
  uiUpdates;
99
96
  _fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
100
- alphaZeroMetrics; // Alpha Zero 2 performance tracking
97
+ sessionMetrics; // Session performance tracking
101
98
  statusSubscription = null;
102
99
  followUpQueue = [];
103
100
  isDrainingQueue = false;
104
101
  activeContextWindowTokens = null;
105
- latestTokenUsage = { used: null, limit: null };
106
102
  sessionPreferences;
107
103
  autosaveEnabled;
108
104
  autoContinueEnabled;
@@ -133,9 +129,6 @@ export class InteractiveShell {
133
129
  statusLineState = null;
134
130
  statusMessageOverride = null;
135
131
  promptRefreshTimer = null;
136
- launchPaletteShown = false;
137
- version;
138
- alternateScreenEnabled;
139
132
  constructor(config) {
140
133
  this.profile = config.profile;
141
134
  this.profileLabel = config.profileLabel;
@@ -149,8 +142,6 @@ export class InteractiveShell {
149
142
  this.autoContinueEnabled = this.sessionPreferences.autoContinue;
150
143
  this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
151
144
  this._enabledPlugins = config.enabledPlugins ?? [];
152
- this.version = config.version ?? '0.0.0';
153
- this.alternateScreenEnabled = config.alternateScreen ?? false;
154
145
  this.initializeSessionHistory();
155
146
  this.sessionState = {
156
147
  provider: config.initialModel.provider,
@@ -171,7 +162,6 @@ export class InteractiveShell {
171
162
  this.slashCommands.push({
172
163
  command: '/agents',
173
164
  description: 'Select the default agent profile (applies on next launch)',
174
- category: 'configuration',
175
165
  });
176
166
  }
177
167
  this.customCommands = loadCustomSlashCommands();
@@ -180,21 +170,18 @@ export class InteractiveShell {
180
170
  this.slashCommands.push({
181
171
  command: custom.command,
182
172
  description: `${custom.description} (custom)`,
183
- category: custom.category ?? 'other',
184
173
  });
185
174
  }
186
175
  if (!this.slashCommands.some((cmd) => cmd.command === '/exit')) {
187
176
  this.slashCommands.push({
188
177
  command: '/exit',
189
178
  description: 'Quit the CLI immediately',
190
- category: 'other',
191
179
  });
192
180
  }
193
181
  // Add /plugins command
194
182
  this.slashCommands.push({
195
183
  command: '/plugins',
196
184
  description: 'Show available and loaded plugins',
197
- category: 'configuration',
198
185
  });
199
186
  this.statusTracker = config.statusTracker;
200
187
  this.ui = config.ui;
@@ -226,28 +213,20 @@ export class InteractiveShell {
226
213
  onEditModeChange: (mode) => this.handleEditModeChange(mode),
227
214
  onToggleVerify: () => this.toggleVerificationMode(),
228
215
  onToggleAutoContinue: () => this.toggleAutoContinueMode(),
229
- onToggleThinking: () => this.cycleThinkingMode(),
230
- onClearContext: () => this.handleClearContext(),
231
216
  });
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();
232
224
  // Initialize Alpha Zero 2 metrics tracking
233
- this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
225
+ this.sessionMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
234
226
  this.setupStatusTracking();
235
227
  this.refreshContextGauge();
236
- // Start terminal input (sets up handlers)
237
228
  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
249
229
  this.refreshControlBar();
250
- this.terminalInput.forceRender();
251
230
  this.rebuildAgent();
252
231
  this.setupHandlers();
253
232
  this.refreshBannerSessionInfo();
@@ -267,10 +246,6 @@ export class InteractiveShell {
267
246
  this.activeSessionId = stored.id;
268
247
  this.activeSessionTitle = stored.title;
269
248
  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
- }
274
249
  return;
275
250
  }
276
251
  display.showWarning(`Session "${this.sessionRestoreConfig.sessionId}" not found. Starting fresh session.`);
@@ -284,10 +259,6 @@ export class InteractiveShell {
284
259
  this.activeSessionId = null;
285
260
  this.activeSessionTitle = autosave.title;
286
261
  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
- }
291
262
  return;
292
263
  }
293
264
  display.showWarning('No autosaved session found. Starting fresh session.');
@@ -302,24 +273,14 @@ export class InteractiveShell {
302
273
  this.sessionResumeNotice = null;
303
274
  }
304
275
  async start(initialPrompt) {
305
- // Check for updates in background (non-blocking)
306
- if (this.version) {
307
- void maybeOfferCliUpdate(this.version);
308
- }
309
276
  if (initialPrompt) {
310
277
  this.logUserPrompt(initialPrompt);
311
278
  await this.processInputBlock(initialPrompt);
312
279
  return;
313
280
  }
314
- this.showLaunchCommandPalette();
315
281
  // Ensure the terminal input is visible
316
282
  this.terminalInput.render();
317
283
  }
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
- }
323
284
  /**
324
285
  * TerminalInputAdapter submit handler
325
286
  */
@@ -333,8 +294,9 @@ export class InteractiveShell {
333
294
  this.handleInputChange('');
334
295
  return;
335
296
  }
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.
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);
338
300
  this.logUserPrompt(approved);
339
301
  void this.processInputBlock(approved).catch((err) => {
340
302
  display.showError(err instanceof Error ? err.message : String(err), err);
@@ -424,59 +386,6 @@ export class InteractiveShell {
424
386
  : 'The model will not be auto-prompted to continue.') +
425
387
  ' Toggle with alt+c.');
426
388
  }
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
- }
480
389
  /**
481
390
  * Gate submissions when edit permission mode is active.
482
391
  * Returns the text to send when approved, or null when waiting for confirmation.
@@ -576,10 +485,6 @@ export class InteractiveShell {
576
485
  return;
577
486
  }
578
487
  this.shuttingDown = true;
579
- const shouldRestoreScrollback = this.alternateScreenEnabled && this.terminalInput.isAlternateScreenActive();
580
- const scrollbackSnapshot = shouldRestoreScrollback
581
- ? this.terminalInput.getScrollbackSnapshot()
582
- : null;
583
488
  // Stop any active spinner to prevent process hang
584
489
  display.stopThinking(false);
585
490
  this.stopStreamingHeartbeat();
@@ -588,34 +493,18 @@ export class InteractiveShell {
588
493
  this.teardownStatusTracking();
589
494
  // Clear any pending cleanup to prevent hanging
590
495
  this.pendingCleanup = null;
591
- // Reset terminal state before disposing adapters
592
- this.terminalInput.exitStreamingScrollRegion();
593
- if (this.alternateScreenEnabled) {
594
- this.terminalInput.exitAlternateScreen();
595
- }
596
496
  // Dispose terminal input handler
597
497
  this.terminalInput.dispose();
598
498
  // Dispose unified UI adapter
599
499
  this.uiAdapter.dispose();
600
- if (scrollbackSnapshot && scrollbackSnapshot.length > 0) {
601
- this.restoreScrollbackSnapshot(scrollbackSnapshot);
602
- }
603
500
  display.newLine();
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)));
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)));
607
506
  exit(0);
608
507
  }
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
- }
619
508
  /**
620
509
  * Update status bar message
621
510
  */
@@ -783,14 +672,13 @@ export class InteractiveShell {
783
672
  });
784
673
  }
785
674
  setProcessingStatus(detail) {
786
- this.latestTokenUsage = { used: null, limit: this.latestTokenUsage.limit };
787
675
  this.statusTracker.setBase('Working on your request', {
788
676
  detail: this.describeStatusDetail(detail),
789
677
  tone: 'info',
790
678
  });
791
679
  }
792
680
  describeStatusDetail(detail) {
793
- const parts = detail?.trim() ? [detail.trim()] : [];
681
+ const parts = [detail?.trim() || this.describeModelDetail()];
794
682
  const queued = this.followUpQueue.length;
795
683
  if (queued > 0) {
796
684
  parts.push(`${queued} follow-up${queued === 1 ? '' : 's'} queued`);
@@ -803,18 +691,12 @@ export class InteractiveShell {
803
691
  }
804
692
  refreshContextGauge() {
805
693
  const tokens = getContextWindowTokens(this.sessionState.model);
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
- }
694
+ this.activeContextWindowTokens =
695
+ typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
814
696
  }
815
697
  updateContextUsage(percentage) {
816
698
  this.uiAdapter.updateContextUsage(percentage);
817
- this.terminalInput.setContextUsage(percentage, CONTEXT_AUTOCOMPACT_PERCENT);
699
+ this.terminalInput.setContextUsage(percentage);
818
700
  }
819
701
  refreshControlBar() {
820
702
  this.terminalInput.setModeToggles({
@@ -822,9 +704,9 @@ export class InteractiveShell {
822
704
  autoContinueEnabled: this.autoContinueEnabled,
823
705
  verificationHotkey: 'alt+v',
824
706
  autoContinueHotkey: 'alt+c',
825
- thinkingModeLabel: this.thinkingMode,
826
- thinkingHotkey: '/thinking',
827
707
  });
708
+ // Update persistent model info display
709
+ this.terminalInput.setModelInfo(this.describeModelDetail());
828
710
  this.refreshStatusLine();
829
711
  this.terminalInput.render();
830
712
  }
@@ -855,25 +737,6 @@ export class InteractiveShell {
855
737
  // Set main status (tool execution, etc.) - shown when not overridden
856
738
  const statusText = this.formatStatusLine(this.statusLineState);
857
739
  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
- });
877
740
  if (forceRender) {
878
741
  this.terminalInput.render();
879
742
  }
@@ -933,14 +796,15 @@ export class InteractiveShell {
933
796
  this.terminalInput.render();
934
797
  }
935
798
  /**
936
- * Log user prompt to the scroll region so it's part of the conversation flow.
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.
937
801
  */
938
802
  logUserPrompt(text) {
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);
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());
944
808
  }
945
809
  requestPromptRefresh(force = false) {
946
810
  if (force) {
@@ -965,40 +829,16 @@ export class InteractiveShell {
965
829
  this.stopStreamingHeartbeat();
966
830
  // Enter global streaming mode - blocks all non-streaming UI output
967
831
  enterStreamingMode();
968
- // Set up scroll region for streaming content
969
- this.terminalInput.enterStreamingScrollRegion();
970
832
  this.uiUpdates.setMode('streaming');
971
833
  this.streamingHeartbeatStart = Date.now();
972
834
  this.streamingHeartbeatFrame = 0;
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
- });
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.
996
838
  }
997
839
  stopStreamingHeartbeat() {
998
840
  // Exit global streaming mode - allows UI to render again
999
841
  exitStreamingMode();
1000
- // Exit scroll region mode
1001
- this.terminalInput.exitStreamingScrollRegion();
1002
842
  this.uiUpdates.stopHeartbeat('streaming');
1003
843
  this.streamingHeartbeatStart = null;
1004
844
  this.streamingHeartbeatFrame = 0;
@@ -1010,28 +850,10 @@ export class InteractiveShell {
1010
850
  // Force refresh to update the input area now that streaming has ended
1011
851
  this.refreshStatusLine(true);
1012
852
  }
1013
- buildStreamingStatus(label, elapsedSeconds) {
853
+ buildStreamingStatus(label) {
1014
854
  const detail = this.describeModelDetail();
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`;
855
+ const prefix = theme.info('');
856
+ return detail ? `${prefix} ${label} ${theme.ui.muted('·')} ${detail}` : `${prefix} ${label}`;
1035
857
  }
1036
858
  refreshQueueIndicators() {
1037
859
  if (this.isProcessing) {
@@ -1279,6 +1101,17 @@ export class InteractiveShell {
1279
1101
  case '/discover':
1280
1102
  await this.discoverModelsCommand();
1281
1103
  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;
1282
1115
  default:
1283
1116
  if (!(await this.tryCustomSlashCommand(command, input))) {
1284
1117
  display.showWarning(`Unknown command "${command}".`);
@@ -1587,6 +1420,99 @@ export class InteractiveShell {
1587
1420
  // Display keyboard shortcuts help (Claude Code style)
1588
1421
  display.showSystemMessage(formatShortcutsHelp());
1589
1422
  }
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
+ }
1590
1516
  showFileChangeSummary() {
1591
1517
  const summary = this._fileChangeTracker.getSummary();
1592
1518
  const changes = this._fileChangeTracker.getAllChanges();
@@ -1629,11 +1555,11 @@ export class InteractiveShell {
1629
1555
  display.showSystemMessage(lines.join('\n'));
1630
1556
  }
1631
1557
  showAlphaZeroMetrics() {
1632
- const summary = this.alphaZeroMetrics.getPerformanceSummary();
1558
+ const summary = this.sessionMetrics.getPerformanceSummary();
1633
1559
  display.showSystemMessage(summary);
1634
1560
  }
1635
1561
  showImprovementSuggestions() {
1636
- const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
1562
+ const suggestions = this.sessionMetrics.getImprovementSuggestions();
1637
1563
  if (suggestions.length === 0) {
1638
1564
  display.showInfo('No improvement suggestions at this time. Keep using the shell to generate metrics!');
1639
1565
  return;
@@ -1679,9 +1605,7 @@ export class InteractiveShell {
1679
1605
  }
1680
1606
  }
1681
1607
  lines.push(theme.secondary('CLI Flags:'));
1682
- lines.push(' --alpha-zero Enable Alpha Zero 2 RL framework');
1683
1608
  lines.push(' --coding Enable enhanced coding tools');
1684
- lines.push(' --security Enable security research tools');
1685
1609
  lines.push(' --all-plugins Enable all optional plugins');
1686
1610
  display.showSystemMessage(lines.join('\n'));
1687
1611
  }
@@ -1730,7 +1654,6 @@ export class InteractiveShell {
1730
1654
  model: this.sessionState.model,
1731
1655
  workspaceRoot: this.workingDir,
1732
1656
  messages: history,
1733
- scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
1734
1657
  });
1735
1658
  this.cachedHistory = history;
1736
1659
  this.updateActiveSession(summary, true);
@@ -1909,7 +1832,6 @@ export class InteractiveShell {
1909
1832
  workspaceRoot: this.workingDir,
1910
1833
  title: this.activeSessionTitle,
1911
1834
  messages: this.cachedHistory,
1912
- scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
1913
1835
  });
1914
1836
  }
1915
1837
  describeWorkspaceOptions() {
@@ -1927,75 +1849,6 @@ export class InteractiveShell {
1927
1849
  }
1928
1850
  return `${warning.label}: ${warning.reason}.`;
1929
1851
  }
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
- }
1999
1852
  buildSlashCommandList(header) {
2000
1853
  const lines = [theme.gradient.primary(header), ''];
2001
1854
  for (const command of this.slashCommands) {
@@ -2464,7 +2317,7 @@ export class InteractiveShell {
2464
2317
  this.autosaveIfEnabled();
2465
2318
  // Track metrics with Alpha Zero 2
2466
2319
  const elapsedMs = Date.now() - requestStartTime;
2467
- this.alphaZeroMetrics.recordMessage(elapsedMs);
2320
+ this.sessionMetrics.recordMessage(elapsedMs);
2468
2321
  if (!responseText?.trim()) {
2469
2322
  display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
2470
2323
  }
@@ -2482,10 +2335,14 @@ export class InteractiveShell {
2482
2335
  this.stopStreamingHeartbeat();
2483
2336
  this.isProcessing = false;
2484
2337
  this.terminalInput.setStreaming(false);
2338
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2485
2339
  this.uiAdapter.endProcessing('Ready for prompts');
2486
2340
  this.setIdleStatus();
2487
2341
  display.newLine();
2488
2342
  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();
2489
2346
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
2490
2347
  // CRITICAL: Ensure readline prompt is active for user input
2491
2348
  // Claude Code style: New prompt naturally appears at bottom
@@ -2562,14 +2419,13 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
2562
2419
  try {
2563
2420
  // Send the request and capture the response (streaming disabled)
2564
2421
  display.showThinking('Responding...');
2565
- this.refreshStatusLine(true);
2566
2422
  const response = await agent.send(currentPrompt, true);
2567
2423
  await this.awaitPendingCleanup();
2568
2424
  this.captureHistorySnapshot();
2569
2425
  this.autosaveIfEnabled();
2570
2426
  // Track metrics
2571
2427
  const elapsedMs = Date.now() - overallStartTime;
2572
- this.alphaZeroMetrics.recordMessage(elapsedMs);
2428
+ this.sessionMetrics.recordMessage(elapsedMs);
2573
2429
  if (!response?.trim()) {
2574
2430
  display.showWarning('Model returned an empty response. Retrying this iteration...');
2575
2431
  consecutiveNoProgress++;
@@ -2706,6 +2562,7 @@ What's the next action?`;
2706
2562
  this.stopStreamingHeartbeat();
2707
2563
  this.isProcessing = false;
2708
2564
  this.terminalInput.setStreaming(false);
2565
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2709
2566
  this.uiAdapter.endProcessing('Ready for prompts');
2710
2567
  this.setIdleStatus();
2711
2568
  this.updateStatusMessage(null);
@@ -3062,10 +2919,8 @@ What's the next action?`;
3062
2919
  try {
3063
2920
  // Send the error to the agent for fixing
3064
2921
  display.showThinking('Analyzing build errors');
3065
- this.refreshStatusLine(true);
3066
2922
  const response = await this.agent.send(prompt, true);
3067
2923
  display.stopThinking();
3068
- this.refreshStatusLine(true);
3069
2924
  if (response) {
3070
2925
  display.showAssistantMessage(response, { isFinal: true });
3071
2926
  }
@@ -3092,8 +2947,8 @@ What's the next action?`;
3092
2947
  };
3093
2948
  this.agent = this.runtimeSession.createAgent(selection, {
3094
2949
  onStreamChunk: (chunk) => {
3095
- // Stream output using clean streamContent() - chat box floats below
3096
- this.terminalInput.streamContent(chunk);
2950
+ // Stream output directly - no spinner during streaming to avoid race conditions
2951
+ display.stream(chunk);
3097
2952
  },
3098
2953
  onStreamFallback: (info) => this.handleStreamingFallback(info),
3099
2954
  onAssistantMessage: (content, metadata) => {
@@ -3115,16 +2970,18 @@ What's the next action?`;
3115
2970
  display.showAssistantMessage(finalContent, enriched);
3116
2971
  }
3117
2972
  }
3118
- // Status shown in mode controls bar - no separate status line needed
2973
+ // Show status line at end (Claude Code style: "• Context X% used • Ready for prompts (2s)")
3119
2974
  display.stopThinking();
3120
- // Update context usage for mode controls display
2975
+ // Calculate context usage
2976
+ let contextInfo;
3121
2977
  if (enriched.contextWindowTokens && metadata.usage) {
3122
2978
  const total = this.totalTokens(metadata.usage);
3123
2979
  if (total && total > 0) {
3124
2980
  const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
3125
- this.updateContextUsage(percentage);
2981
+ contextInfo = { percentage, tokens: total };
3126
2982
  }
3127
2983
  }
2984
+ display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
3128
2985
  // Auto-verify changes: build first (catches type errors), then tests
3129
2986
  void this.enforceAutoBuild('final-response');
3130
2987
  void this.enforceAutoTests('final-response');
@@ -3194,6 +3051,7 @@ What's the next action?`;
3194
3051
  this.stopStreamingHeartbeat();
3195
3052
  this.updateStatusMessage(null);
3196
3053
  this.terminalInput.setStreaming(false);
3054
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3197
3055
  this.terminalInput.render();
3198
3056
  },
3199
3057
  onVerificationNeeded: () => {
@@ -3230,6 +3088,7 @@ What's the next action?`;
3230
3088
  resetChatBoxAfterModelSwap() {
3231
3089
  this.updateStatusMessage(null);
3232
3090
  this.terminalInput.setStreaming(false);
3091
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3233
3092
  this.terminalInput.render();
3234
3093
  this.ensureReadlineReady();
3235
3094
  }
@@ -3294,14 +3153,9 @@ What's the next action?`;
3294
3153
  return null;
3295
3154
  }
3296
3155
  const usageRatio = total / windowTokens;
3297
- this.latestTokenUsage = {
3298
- used: total,
3299
- limit: windowTokens,
3300
- };
3301
3156
  // Always update context usage in the UI
3302
3157
  const percentUsed = Math.round(usageRatio * 100);
3303
3158
  this.updateContextUsage(percentUsed);
3304
- this.refreshStatusLine(true);
3305
3159
  if (usageRatio < CONTEXT_USAGE_THRESHOLD) {
3306
3160
  return null;
3307
3161
  }
@@ -3568,113 +3422,6 @@ What's the next action?`;
3568
3422
  this.sessionState.reasoningEffort = preset.reasoningEffort;
3569
3423
  }
3570
3424
  }
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
- }
3678
3425
  refreshBannerSessionInfo() {
3679
3426
  const nextState = {
3680
3427
  model: this.sessionState.model,
@@ -3685,11 +3432,13 @@ What's the next action?`;
3685
3432
  return;
3686
3433
  }
3687
3434
  this.refreshContextGauge();
3688
- // Banner is no longer stored in display - it was streamed as content
3689
- // Model/provider changes are visible in the control bar
3435
+ display.updateSessionInfo(nextState.model, nextState.provider);
3436
+ // Update the persistent model info display in terminal input
3437
+ this.terminalInput.setModelInfo(this.describeModelDetail());
3690
3438
  if (!this.isProcessing) {
3691
3439
  this.setIdleStatus();
3692
3440
  }
3441
+ // Pinned header rows are disabled; scroll region handles all output.
3693
3442
  this.bannerSessionState = nextState;
3694
3443
  }
3695
3444
  providerLabel(id) {