erosolar-cli 1.7.355 → 1.7.356

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