erosolar-cli 1.7.315 → 1.7.317

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 (323) 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/toolPreconditions.d.ts.map +1 -1
  97. package/dist/core/toolPreconditions.js +14 -0
  98. package/dist/core/toolPreconditions.js.map +1 -1
  99. package/dist/core/toolRuntime.d.ts +1 -22
  100. package/dist/core/toolRuntime.d.ts.map +1 -1
  101. package/dist/core/toolRuntime.js +5 -0
  102. package/dist/core/toolRuntime.js.map +1 -1
  103. package/dist/core/toolValidation.d.ts.map +1 -1
  104. package/dist/core/toolValidation.js +3 -14
  105. package/dist/core/toolValidation.js.map +1 -1
  106. package/dist/core/unified/errors.d.ts +189 -0
  107. package/dist/core/unified/errors.d.ts.map +1 -0
  108. package/dist/core/unified/errors.js +497 -0
  109. package/dist/core/unified/errors.js.map +1 -0
  110. package/dist/core/unified/index.d.ts +19 -0
  111. package/dist/core/unified/index.d.ts.map +1 -0
  112. package/dist/core/unified/index.js +68 -0
  113. package/dist/core/unified/index.js.map +1 -0
  114. package/dist/core/unified/schema.d.ts +101 -0
  115. package/dist/core/unified/schema.d.ts.map +1 -0
  116. package/dist/core/unified/schema.js +350 -0
  117. package/dist/core/unified/schema.js.map +1 -0
  118. package/dist/core/unified/toolRuntime.d.ts +179 -0
  119. package/dist/core/unified/toolRuntime.d.ts.map +1 -0
  120. package/dist/core/unified/toolRuntime.js +517 -0
  121. package/dist/core/unified/toolRuntime.js.map +1 -0
  122. package/dist/core/unified/tools.d.ts +127 -0
  123. package/dist/core/unified/tools.d.ts.map +1 -0
  124. package/dist/core/unified/tools.js +1333 -0
  125. package/dist/core/unified/tools.js.map +1 -0
  126. package/dist/core/unified/types.d.ts +352 -0
  127. package/dist/core/unified/types.d.ts.map +1 -0
  128. package/dist/core/unified/types.js +12 -0
  129. package/dist/core/unified/types.js.map +1 -0
  130. package/dist/core/unified/version.d.ts +209 -0
  131. package/dist/core/unified/version.d.ts.map +1 -0
  132. package/dist/core/unified/version.js +454 -0
  133. package/dist/core/unified/version.js.map +1 -0
  134. package/dist/core/validationRunner.d.ts +3 -1
  135. package/dist/core/validationRunner.d.ts.map +1 -1
  136. package/dist/core/validationRunner.js.map +1 -1
  137. package/dist/headless/headlessApp.d.ts.map +1 -1
  138. package/dist/headless/headlessApp.js +0 -21
  139. package/dist/headless/headlessApp.js.map +1 -1
  140. package/dist/mcp/sseClient.d.ts.map +1 -1
  141. package/dist/mcp/sseClient.js +18 -9
  142. package/dist/mcp/sseClient.js.map +1 -1
  143. package/dist/plugins/tools/build/buildPlugin.d.ts +6 -0
  144. package/dist/plugins/tools/build/buildPlugin.d.ts.map +1 -1
  145. package/dist/plugins/tools/build/buildPlugin.js +10 -4
  146. package/dist/plugins/tools/build/buildPlugin.js.map +1 -1
  147. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
  148. package/dist/plugins/tools/nodeDefaults.js +2 -0
  149. package/dist/plugins/tools/nodeDefaults.js.map +1 -1
  150. package/dist/plugins/tools/security/securityPlugin.d.ts +3 -0
  151. package/dist/plugins/tools/security/securityPlugin.d.ts.map +1 -0
  152. package/dist/plugins/tools/security/securityPlugin.js +12 -0
  153. package/dist/plugins/tools/security/securityPlugin.js.map +1 -0
  154. package/dist/runtime/agentSession.d.ts +2 -2
  155. package/dist/runtime/agentSession.d.ts.map +1 -1
  156. package/dist/runtime/agentSession.js +2 -2
  157. package/dist/runtime/agentSession.js.map +1 -1
  158. package/dist/security/active-stack-security.d.ts +112 -0
  159. package/dist/security/active-stack-security.d.ts.map +1 -0
  160. package/dist/security/active-stack-security.js +296 -0
  161. package/dist/security/active-stack-security.js.map +1 -0
  162. package/dist/security/advanced-persistence-research.d.ts +92 -0
  163. package/dist/security/advanced-persistence-research.d.ts.map +1 -0
  164. package/dist/security/advanced-persistence-research.js +195 -0
  165. package/dist/security/advanced-persistence-research.js.map +1 -0
  166. package/dist/security/advanced-targeting.d.ts +119 -0
  167. package/dist/security/advanced-targeting.d.ts.map +1 -0
  168. package/dist/security/advanced-targeting.js +233 -0
  169. package/dist/security/advanced-targeting.js.map +1 -0
  170. package/dist/security/assessment/vulnerabilityAssessment.d.ts +104 -0
  171. package/dist/security/assessment/vulnerabilityAssessment.d.ts.map +1 -0
  172. package/dist/security/assessment/vulnerabilityAssessment.js +315 -0
  173. package/dist/security/assessment/vulnerabilityAssessment.js.map +1 -0
  174. package/dist/security/authorization/securityAuthorization.d.ts +88 -0
  175. package/dist/security/authorization/securityAuthorization.d.ts.map +1 -0
  176. package/dist/security/authorization/securityAuthorization.js +172 -0
  177. package/dist/security/authorization/securityAuthorization.js.map +1 -0
  178. package/dist/security/comprehensive-targeting.d.ts +85 -0
  179. package/dist/security/comprehensive-targeting.d.ts.map +1 -0
  180. package/dist/security/comprehensive-targeting.js +438 -0
  181. package/dist/security/comprehensive-targeting.js.map +1 -0
  182. package/dist/security/global-security-integration.d.ts +91 -0
  183. package/dist/security/global-security-integration.d.ts.map +1 -0
  184. package/dist/security/global-security-integration.js +218 -0
  185. package/dist/security/global-security-integration.js.map +1 -0
  186. package/dist/security/index.d.ts +38 -0
  187. package/dist/security/index.d.ts.map +1 -0
  188. package/dist/security/index.js +47 -0
  189. package/dist/security/index.js.map +1 -0
  190. package/dist/security/persistence-analyzer.d.ts +56 -0
  191. package/dist/security/persistence-analyzer.d.ts.map +1 -0
  192. package/dist/security/persistence-analyzer.js +187 -0
  193. package/dist/security/persistence-analyzer.js.map +1 -0
  194. package/dist/security/persistence-cli.d.ts +36 -0
  195. package/dist/security/persistence-cli.d.ts.map +1 -0
  196. package/dist/security/persistence-cli.js +160 -0
  197. package/dist/security/persistence-cli.js.map +1 -0
  198. package/dist/security/persistence-research.d.ts +92 -0
  199. package/dist/security/persistence-research.d.ts.map +1 -0
  200. package/dist/security/persistence-research.js +364 -0
  201. package/dist/security/persistence-research.js.map +1 -0
  202. package/dist/security/research/persistenceResearch.d.ts +97 -0
  203. package/dist/security/research/persistenceResearch.d.ts.map +1 -0
  204. package/dist/security/research/persistenceResearch.js +282 -0
  205. package/dist/security/research/persistenceResearch.js.map +1 -0
  206. package/dist/security/security-integration.d.ts +74 -0
  207. package/dist/security/security-integration.d.ts.map +1 -0
  208. package/dist/security/security-integration.js +137 -0
  209. package/dist/security/security-integration.js.map +1 -0
  210. package/dist/security/security-testing-framework.d.ts +112 -0
  211. package/dist/security/security-testing-framework.d.ts.map +1 -0
  212. package/dist/security/security-testing-framework.js +364 -0
  213. package/dist/security/security-testing-framework.js.map +1 -0
  214. package/dist/security/simulation/attackSimulation.d.ts +93 -0
  215. package/dist/security/simulation/attackSimulation.d.ts.map +1 -0
  216. package/dist/security/simulation/attackSimulation.js +341 -0
  217. package/dist/security/simulation/attackSimulation.js.map +1 -0
  218. package/dist/security/strategic-operations.d.ts +100 -0
  219. package/dist/security/strategic-operations.d.ts.map +1 -0
  220. package/dist/security/strategic-operations.js +276 -0
  221. package/dist/security/strategic-operations.js.map +1 -0
  222. package/dist/security/tool-security-wrapper.d.ts +58 -0
  223. package/dist/security/tool-security-wrapper.d.ts.map +1 -0
  224. package/dist/security/tool-security-wrapper.js +156 -0
  225. package/dist/security/tool-security-wrapper.js.map +1 -0
  226. package/dist/shell/claudeCodeStreamHandler.d.ts +145 -0
  227. package/dist/shell/claudeCodeStreamHandler.d.ts.map +1 -0
  228. package/dist/shell/claudeCodeStreamHandler.js +322 -0
  229. package/dist/shell/claudeCodeStreamHandler.js.map +1 -0
  230. package/dist/shell/inputQueueManager.d.ts +144 -0
  231. package/dist/shell/inputQueueManager.d.ts.map +1 -0
  232. package/dist/shell/inputQueueManager.js +290 -0
  233. package/dist/shell/inputQueueManager.js.map +1 -0
  234. package/dist/shell/interactiveShell.d.ts +7 -16
  235. package/dist/shell/interactiveShell.d.ts.map +1 -1
  236. package/dist/shell/interactiveShell.js +164 -229
  237. package/dist/shell/interactiveShell.js.map +1 -1
  238. package/dist/shell/metricsTracker.d.ts +60 -0
  239. package/dist/shell/metricsTracker.d.ts.map +1 -0
  240. package/dist/shell/metricsTracker.js +119 -0
  241. package/dist/shell/metricsTracker.js.map +1 -0
  242. package/dist/shell/shellApp.d.ts +0 -2
  243. package/dist/shell/shellApp.d.ts.map +1 -1
  244. package/dist/shell/shellApp.js +9 -40
  245. package/dist/shell/shellApp.js.map +1 -1
  246. package/dist/shell/streamingOutputManager.d.ts +115 -0
  247. package/dist/shell/streamingOutputManager.d.ts.map +1 -0
  248. package/dist/shell/streamingOutputManager.js +225 -0
  249. package/dist/shell/streamingOutputManager.js.map +1 -0
  250. package/dist/shell/systemPrompt.d.ts.map +1 -1
  251. package/dist/shell/systemPrompt.js +4 -1
  252. package/dist/shell/systemPrompt.js.map +1 -1
  253. package/dist/shell/terminalInput.d.ts +117 -131
  254. package/dist/shell/terminalInput.d.ts.map +1 -1
  255. package/dist/shell/terminalInput.js +533 -572
  256. package/dist/shell/terminalInput.js.map +1 -1
  257. package/dist/shell/terminalInputAdapter.d.ts +20 -61
  258. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  259. package/dist/shell/terminalInputAdapter.js +30 -73
  260. package/dist/shell/terminalInputAdapter.js.map +1 -1
  261. package/dist/subagents/taskRunner.d.ts +1 -7
  262. package/dist/subagents/taskRunner.d.ts.map +1 -1
  263. package/dist/subagents/taskRunner.js +47 -180
  264. package/dist/subagents/taskRunner.js.map +1 -1
  265. package/dist/tools/securityTools.d.ts +22 -0
  266. package/dist/tools/securityTools.d.ts.map +1 -0
  267. package/dist/tools/securityTools.js +448 -0
  268. package/dist/tools/securityTools.js.map +1 -0
  269. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  270. package/dist/ui/ShellUIAdapter.js +12 -13
  271. package/dist/ui/ShellUIAdapter.js.map +1 -1
  272. package/dist/ui/display.d.ts +45 -24
  273. package/dist/ui/display.d.ts.map +1 -1
  274. package/dist/ui/display.js +259 -140
  275. package/dist/ui/display.js.map +1 -1
  276. package/dist/ui/persistentPrompt.d.ts +50 -0
  277. package/dist/ui/persistentPrompt.d.ts.map +1 -0
  278. package/dist/ui/persistentPrompt.js +92 -0
  279. package/dist/ui/persistentPrompt.js.map +1 -0
  280. package/dist/ui/terminalUISchema.d.ts +195 -0
  281. package/dist/ui/terminalUISchema.d.ts.map +1 -0
  282. package/dist/ui/terminalUISchema.js +113 -0
  283. package/dist/ui/terminalUISchema.js.map +1 -0
  284. package/dist/ui/theme.d.ts.map +1 -1
  285. package/dist/ui/theme.js +8 -6
  286. package/dist/ui/theme.js.map +1 -1
  287. package/dist/ui/toolDisplay.d.ts +158 -0
  288. package/dist/ui/toolDisplay.d.ts.map +1 -1
  289. package/dist/ui/toolDisplay.js +348 -0
  290. package/dist/ui/toolDisplay.js.map +1 -1
  291. package/dist/ui/unified/layout.d.ts +0 -1
  292. package/dist/ui/unified/layout.d.ts.map +1 -1
  293. package/dist/ui/unified/layout.js +25 -15
  294. package/dist/ui/unified/layout.js.map +1 -1
  295. package/package.json +4 -4
  296. package/scripts/deploy-security-capabilities.js +178 -0
  297. package/dist/core/hooks.d.ts +0 -113
  298. package/dist/core/hooks.d.ts.map +0 -1
  299. package/dist/core/hooks.js +0 -267
  300. package/dist/core/hooks.js.map +0 -1
  301. package/dist/core/metricsTracker.d.ts +0 -122
  302. package/dist/core/metricsTracker.d.ts.map +0 -1
  303. package/dist/core/metricsTracker.js.map +0 -1
  304. package/dist/core/securityAssessment.d.ts +0 -91
  305. package/dist/core/securityAssessment.d.ts.map +0 -1
  306. package/dist/core/securityAssessment.js +0 -580
  307. package/dist/core/securityAssessment.js.map +0 -1
  308. package/dist/core/verification.d.ts +0 -137
  309. package/dist/core/verification.d.ts.map +0 -1
  310. package/dist/core/verification.js +0 -323
  311. package/dist/core/verification.js.map +0 -1
  312. package/dist/subagents/agentConfig.d.ts +0 -27
  313. package/dist/subagents/agentConfig.d.ts.map +0 -1
  314. package/dist/subagents/agentConfig.js +0 -89
  315. package/dist/subagents/agentConfig.js.map +0 -1
  316. package/dist/subagents/agentRegistry.d.ts +0 -33
  317. package/dist/subagents/agentRegistry.d.ts.map +0 -1
  318. package/dist/subagents/agentRegistry.js +0 -162
  319. package/dist/subagents/agentRegistry.js.map +0 -1
  320. package/dist/utils/frontmatter.d.ts +0 -10
  321. package/dist/utils/frontmatter.d.ts.map +0 -1
  322. package/dist/utils/frontmatter.js +0 -78
  323. 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,8 +129,6 @@ export class InteractiveShell {
133
129
  statusLineState = null;
134
130
  statusMessageOverride = null;
135
131
  promptRefreshTimer = null;
136
- launchPaletteShown = false;
137
- version;
138
132
  constructor(config) {
139
133
  this.profile = config.profile;
140
134
  this.profileLabel = config.profileLabel;
@@ -148,7 +142,6 @@ export class InteractiveShell {
148
142
  this.autoContinueEnabled = this.sessionPreferences.autoContinue;
149
143
  this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
150
144
  this._enabledPlugins = config.enabledPlugins ?? [];
151
- this.version = config.version ?? '0.0.0';
152
145
  this.initializeSessionHistory();
153
146
  this.sessionState = {
154
147
  provider: config.initialModel.provider,
@@ -169,7 +162,6 @@ export class InteractiveShell {
169
162
  this.slashCommands.push({
170
163
  command: '/agents',
171
164
  description: 'Select the default agent profile (applies on next launch)',
172
- category: 'configuration',
173
165
  });
174
166
  }
175
167
  this.customCommands = loadCustomSlashCommands();
@@ -178,21 +170,18 @@ export class InteractiveShell {
178
170
  this.slashCommands.push({
179
171
  command: custom.command,
180
172
  description: `${custom.description} (custom)`,
181
- category: custom.category ?? 'other',
182
173
  });
183
174
  }
184
175
  if (!this.slashCommands.some((cmd) => cmd.command === '/exit')) {
185
176
  this.slashCommands.push({
186
177
  command: '/exit',
187
178
  description: 'Quit the CLI immediately',
188
- category: 'other',
189
179
  });
190
180
  }
191
181
  // Add /plugins command
192
182
  this.slashCommands.push({
193
183
  command: '/plugins',
194
184
  description: 'Show available and loaded plugins',
195
- category: 'configuration',
196
185
  });
197
186
  this.statusTracker = config.statusTracker;
198
187
  this.ui = config.ui;
@@ -225,21 +214,19 @@ export class InteractiveShell {
225
214
  onToggleVerify: () => this.toggleVerificationMode(),
226
215
  onToggleAutoContinue: () => this.toggleAutoContinueMode(),
227
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();
228
224
  // Initialize Alpha Zero 2 metrics tracking
229
- this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
225
+ this.sessionMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
230
226
  this.setupStatusTracking();
231
227
  this.refreshContextGauge();
232
- // Start terminal input (sets up handlers)
233
228
  this.terminalInput.start();
234
- // Clear the entire terminal screen (including the launching command)
235
- // and position cursor at top for a clean UI
236
- this.terminalInput.clearScreen();
237
- // Stream banner first - this sets up scroll region dynamically
238
- const banner = this.buildBanner();
239
- this.terminalInput.streamContent(banner + '\n\n');
240
- // Render chat box after banner is streamed
241
229
  this.refreshControlBar();
242
- this.terminalInput.forceRender();
243
230
  this.rebuildAgent();
244
231
  this.setupHandlers();
245
232
  this.refreshBannerSessionInfo();
@@ -286,24 +273,14 @@ export class InteractiveShell {
286
273
  this.sessionResumeNotice = null;
287
274
  }
288
275
  async start(initialPrompt) {
289
- // Check for updates in background (non-blocking)
290
- if (this.version) {
291
- void maybeOfferCliUpdate(this.version);
292
- }
293
276
  if (initialPrompt) {
294
277
  this.logUserPrompt(initialPrompt);
295
278
  await this.processInputBlock(initialPrompt);
296
279
  return;
297
280
  }
298
- this.showLaunchCommandPalette();
299
281
  // Ensure the terminal input is visible
300
282
  this.terminalInput.render();
301
283
  }
302
- showLaunchCommandPalette() {
303
- // Disabled: Quick commands palette takes up too much space
304
- // Users can type /help to see available commands
305
- this.launchPaletteShown = true;
306
- }
307
284
  /**
308
285
  * TerminalInputAdapter submit handler
309
286
  */
@@ -317,8 +294,9 @@ export class InteractiveShell {
317
294
  this.handleInputChange('');
318
295
  return;
319
296
  }
320
- // DON'T clear the input here - keep it visible while streaming.
321
- // 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);
322
300
  this.logUserPrompt(approved);
323
301
  void this.processInputBlock(approved).catch((err) => {
324
302
  display.showError(err instanceof Error ? err.message : String(err), err);
@@ -520,9 +498,11 @@ export class InteractiveShell {
520
498
  // Dispose unified UI adapter
521
499
  this.uiAdapter.dispose();
522
500
  display.newLine();
523
- console.log(theme.ui.muted(''.repeat(44)));
524
- console.log(theme.ui.muted(' Goodbye! · support@ero.solar'));
525
- 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)));
526
506
  exit(0);
527
507
  }
528
508
  /**
@@ -692,14 +672,13 @@ export class InteractiveShell {
692
672
  });
693
673
  }
694
674
  setProcessingStatus(detail) {
695
- this.latestTokenUsage = { used: null, limit: this.latestTokenUsage.limit };
696
675
  this.statusTracker.setBase('Working on your request', {
697
676
  detail: this.describeStatusDetail(detail),
698
677
  tone: 'info',
699
678
  });
700
679
  }
701
680
  describeStatusDetail(detail) {
702
- const parts = detail?.trim() ? [detail.trim()] : [];
681
+ const parts = [detail?.trim() || this.describeModelDetail()];
703
682
  const queued = this.followUpQueue.length;
704
683
  if (queued > 0) {
705
684
  parts.push(`${queued} follow-up${queued === 1 ? '' : 's'} queued`);
@@ -712,18 +691,12 @@ export class InteractiveShell {
712
691
  }
713
692
  refreshContextGauge() {
714
693
  const tokens = getContextWindowTokens(this.sessionState.model);
715
- const normalizedTokens = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
716
- this.activeContextWindowTokens = normalizedTokens;
717
- if (normalizedTokens !== null) {
718
- this.latestTokenUsage = {
719
- used: this.latestTokenUsage.used,
720
- limit: normalizedTokens,
721
- };
722
- }
694
+ this.activeContextWindowTokens =
695
+ typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
723
696
  }
724
697
  updateContextUsage(percentage) {
725
698
  this.uiAdapter.updateContextUsage(percentage);
726
- this.terminalInput.setContextUsage(percentage, CONTEXT_AUTOCOMPACT_PERCENT);
699
+ this.terminalInput.setContextUsage(percentage);
727
700
  }
728
701
  refreshControlBar() {
729
702
  this.terminalInput.setModeToggles({
@@ -731,9 +704,9 @@ export class InteractiveShell {
731
704
  autoContinueEnabled: this.autoContinueEnabled,
732
705
  verificationHotkey: 'alt+v',
733
706
  autoContinueHotkey: 'alt+c',
734
- thinkingModeLabel: this.thinkingMode,
735
- thinkingHotkey: '/thinking',
736
707
  });
708
+ // Update persistent model info display
709
+ this.terminalInput.setModelInfo(this.describeModelDetail());
737
710
  this.refreshStatusLine();
738
711
  this.terminalInput.render();
739
712
  }
@@ -764,25 +737,6 @@ export class InteractiveShell {
764
737
  // Set main status (tool execution, etc.) - shown when not overridden
765
738
  const statusText = this.formatStatusLine(this.statusLineState);
766
739
  this.terminalInput.setStatusMessage(statusText);
767
- // Surface meta header (elapsed + context usage) above the divider
768
- const elapsedSeconds = this.statusLineState
769
- ? Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000))
770
- : null;
771
- const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
772
- const tokensUsed = this.latestTokenUsage.used;
773
- const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
774
- this.terminalInput.setMetaStatus({
775
- elapsedSeconds,
776
- tokensUsed,
777
- tokenLimit,
778
- thinkingMs,
779
- thinkingHasContent: display.isSpinnerActive(),
780
- });
781
- // Keep model/provider visible in the controls bar
782
- this.terminalInput.setModelContext({
783
- model: this.sessionState.model,
784
- provider: this.providerLabel(this.sessionState.provider),
785
- });
786
740
  if (forceRender) {
787
741
  this.terminalInput.render();
788
742
  }
@@ -842,14 +796,13 @@ export class InteractiveShell {
842
796
  this.terminalInput.render();
843
797
  }
844
798
  /**
845
- * 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.
846
801
  */
847
802
  logUserPrompt(text) {
848
- if (!text.trim())
849
- return;
850
- // Format with user prompt prefix and write to scroll region
851
- const formatted = `${theme.user('>')} ${text}\n`;
852
- 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`);
853
806
  }
854
807
  requestPromptRefresh(force = false) {
855
808
  if (force) {
@@ -877,29 +830,9 @@ export class InteractiveShell {
877
830
  this.uiUpdates.setMode('streaming');
878
831
  this.streamingHeartbeatStart = Date.now();
879
832
  this.streamingHeartbeatFrame = 0;
880
- const initialFrame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
881
- this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
882
- display.updateStreamingStatus(this.streamingStatusLabel);
883
- this.refreshStatusLine(true);
884
- // Periodically refresh the pinned input/status region while streaming so
885
- // elapsed time remains visible without interrupting the scroll region.
886
- this.uiUpdates.startHeartbeat('streaming', {
887
- intervalMs: 1000,
888
- lane: 'heartbeat',
889
- mode: ['streaming', 'processing'],
890
- coalesceKey: 'streaming:heartbeat',
891
- run: () => {
892
- const elapsedSeconds = this.streamingHeartbeatStart
893
- ? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
894
- : 0;
895
- this.streamingHeartbeatFrame =
896
- (this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
897
- const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
898
- this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
899
- display.updateStreamingStatus(this.streamingStatusLabel);
900
- this.refreshStatusLine(true);
901
- },
902
- });
833
+ // Note: We don't start a heartbeat during streaming anymore
834
+ // because the UI shouldn't be rendering during streaming.
835
+ // The streaming status is shown in the streaming header instead.
903
836
  }
904
837
  stopStreamingHeartbeat() {
905
838
  // Exit global streaming mode - allows UI to render again
@@ -915,28 +848,10 @@ export class InteractiveShell {
915
848
  // Force refresh to update the input area now that streaming has ended
916
849
  this.refreshStatusLine(true);
917
850
  }
918
- buildStreamingStatus(label, elapsedSeconds) {
851
+ buildStreamingStatus(label) {
919
852
  const detail = this.describeModelDetail();
920
- const elapsedLabel = typeof elapsedSeconds === 'number' && elapsedSeconds >= 0
921
- ? theme.ui.muted(this.formatElapsedShort(elapsedSeconds))
922
- : null;
923
- const prefix = theme.info('⏺');
924
- const parts = [label];
925
- if (detail) {
926
- parts.push(theme.ui.muted('·'), detail);
927
- }
928
- if (elapsedLabel) {
929
- parts.push(theme.ui.muted('·'), elapsedLabel);
930
- }
931
- return `${prefix} ${parts.join(' ')}`.trim();
932
- }
933
- formatElapsedShort(seconds) {
934
- if (seconds < 60) {
935
- return `${seconds}s`;
936
- }
937
- const minutes = Math.floor(seconds / 60);
938
- const remaining = seconds % 60;
939
- return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
853
+ const prefix = theme.info('');
854
+ return detail ? `${prefix} ${label} ${theme.ui.muted('·')} ${detail}` : `${prefix} ${label}`;
940
855
  }
941
856
  refreshQueueIndicators() {
942
857
  if (this.isProcessing) {
@@ -1184,6 +1099,17 @@ export class InteractiveShell {
1184
1099
  case '/discover':
1185
1100
  await this.discoverModelsCommand();
1186
1101
  break;
1102
+ case '/memory':
1103
+ this.handleMemoryCommand(input);
1104
+ break;
1105
+ case '/clear':
1106
+ display.clear();
1107
+ this.cachedHistory = [];
1108
+ display.showInfo('Conversation cleared.');
1109
+ break;
1110
+ case '/help':
1111
+ this.showHelp();
1112
+ break;
1187
1113
  default:
1188
1114
  if (!(await this.tryCustomSlashCommand(command, input))) {
1189
1115
  display.showWarning(`Unknown command "${command}".`);
@@ -1492,6 +1418,99 @@ export class InteractiveShell {
1492
1418
  // Display keyboard shortcuts help (Claude Code style)
1493
1419
  display.showSystemMessage(formatShortcutsHelp());
1494
1420
  }
1421
+ handleMemoryCommand(input) {
1422
+ const tokens = input.trim().split(/\s+/).slice(1);
1423
+ const action = (tokens.shift() ?? 'show').toLowerCase();
1424
+ switch (action) {
1425
+ case '':
1426
+ case 'show':
1427
+ case 'list': {
1428
+ this.showMemoryStatus();
1429
+ break;
1430
+ }
1431
+ case 'paths': {
1432
+ this.showMemoryPaths();
1433
+ break;
1434
+ }
1435
+ case 'edit': {
1436
+ const level = (tokens[0] ?? 'project').toLowerCase();
1437
+ this.openMemoryForEdit(level);
1438
+ break;
1439
+ }
1440
+ default:
1441
+ display.showWarning('Usage: /memory [show|paths|edit <user|project>]');
1442
+ break;
1443
+ }
1444
+ }
1445
+ showMemoryStatus() {
1446
+ const memory = loadMemory(this.workingDir);
1447
+ const lines = [];
1448
+ lines.push(theme.bold('Persistent Memory'));
1449
+ lines.push('');
1450
+ if (memory.sources.length === 0) {
1451
+ lines.push(theme.secondary('No memory files found.'));
1452
+ lines.push('');
1453
+ lines.push(`Create ${theme.info('EROSOLAR.md')} in your project to add persistent context.`);
1454
+ lines.push(`Use ${theme.info('/memory edit project')} to create one.`);
1455
+ }
1456
+ else {
1457
+ for (const source of memory.sources) {
1458
+ const levelLabel = source.level === 'enterprise' ? 'Enterprise' :
1459
+ source.level === 'user' ? 'User' : 'Project';
1460
+ const preview = source.content.slice(0, 200).replace(/\n/g, ' ').trim();
1461
+ const truncated = source.content.length > 200 ? '...' : '';
1462
+ lines.push(`${theme.success('●')} ${theme.bold(levelLabel)}: ${source.path}`);
1463
+ lines.push(` ${theme.dim(preview + truncated)}`);
1464
+ lines.push('');
1465
+ }
1466
+ if (memory.importedPaths.length > memory.sources.length) {
1467
+ lines.push(theme.secondary(`Imported ${memory.importedPaths.length - memory.sources.length} additional files via @imports.`));
1468
+ }
1469
+ }
1470
+ display.showSystemMessage(lines.join('\n'));
1471
+ }
1472
+ showMemoryPaths() {
1473
+ const paths = listMemoryPaths(this.workingDir);
1474
+ const lines = [];
1475
+ lines.push(theme.bold('Memory File Locations'));
1476
+ lines.push('');
1477
+ for (const { level, path, exists } of paths) {
1478
+ const icon = exists ? theme.success('✓') : theme.dim('○');
1479
+ const levelLabel = level.charAt(0).toUpperCase() + level.slice(1);
1480
+ lines.push(`${icon} ${levelLabel}: ${path}`);
1481
+ }
1482
+ lines.push('');
1483
+ lines.push(theme.secondary('Create any of these files to add persistent memory.'));
1484
+ lines.push(theme.secondary('Use @path/to/file.md syntax to import other files.'));
1485
+ display.showSystemMessage(lines.join('\n'));
1486
+ }
1487
+ openMemoryForEdit(level) {
1488
+ let targetPath;
1489
+ if (level === 'user') {
1490
+ targetPath = getUserMemoryEditPath();
1491
+ }
1492
+ else if (level === 'project') {
1493
+ targetPath = getDefaultProjectMemoryPath(this.workingDir);
1494
+ }
1495
+ else {
1496
+ display.showWarning('Specify "user" or "project" to edit. Enterprise memory is read-only.');
1497
+ return;
1498
+ }
1499
+ display.showInfo(`Memory file: ${targetPath}`);
1500
+ display.showInfo('Create or edit this file to add persistent context for the AI.');
1501
+ display.showInfo('');
1502
+ display.showInfo('Example EROSOLAR.md content:');
1503
+ display.showInfo('');
1504
+ display.showInfo(theme.dim(`# Project Guidelines
1505
+
1506
+ When working in this codebase:
1507
+ - Follow TypeScript strict mode conventions
1508
+ - Use functional patterns where appropriate
1509
+ - Run tests before committing
1510
+
1511
+ @./docs/coding-standards.md
1512
+ `));
1513
+ }
1495
1514
  showFileChangeSummary() {
1496
1515
  const summary = this._fileChangeTracker.getSummary();
1497
1516
  const changes = this._fileChangeTracker.getAllChanges();
@@ -1534,11 +1553,11 @@ export class InteractiveShell {
1534
1553
  display.showSystemMessage(lines.join('\n'));
1535
1554
  }
1536
1555
  showAlphaZeroMetrics() {
1537
- const summary = this.alphaZeroMetrics.getPerformanceSummary();
1556
+ const summary = this.sessionMetrics.getPerformanceSummary();
1538
1557
  display.showSystemMessage(summary);
1539
1558
  }
1540
1559
  showImprovementSuggestions() {
1541
- const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
1560
+ const suggestions = this.sessionMetrics.getImprovementSuggestions();
1542
1561
  if (suggestions.length === 0) {
1543
1562
  display.showInfo('No improvement suggestions at this time. Keep using the shell to generate metrics!');
1544
1563
  return;
@@ -1584,9 +1603,7 @@ export class InteractiveShell {
1584
1603
  }
1585
1604
  }
1586
1605
  lines.push(theme.secondary('CLI Flags:'));
1587
- lines.push(' --alpha-zero Enable Alpha Zero 2 RL framework');
1588
1606
  lines.push(' --coding Enable enhanced coding tools');
1589
- lines.push(' --security Enable security research tools');
1590
1607
  lines.push(' --all-plugins Enable all optional plugins');
1591
1608
  display.showSystemMessage(lines.join('\n'));
1592
1609
  }
@@ -1830,75 +1847,6 @@ export class InteractiveShell {
1830
1847
  }
1831
1848
  return `${warning.label}: ${warning.reason}.`;
1832
1849
  }
1833
- buildLaunchCommandPalette() {
1834
- const entries = [];
1835
- const secretsSummary = this.summarizeSecretsForPalette();
1836
- const toolSummary = this.getToolSelectionSummary();
1837
- const autosaveLabel = this.autosaveEnabled ? 'on' : 'off';
1838
- for (const command of this.slashCommands) {
1839
- const entry = {
1840
- command: command.command,
1841
- description: command.description,
1842
- category: command.category ?? 'other',
1843
- };
1844
- switch (command.command) {
1845
- case '/secrets':
1846
- if (secretsSummary.text) {
1847
- entry.description = `${command.description} (${secretsSummary.text})`;
1848
- entry.tone = secretsSummary.tone;
1849
- }
1850
- break;
1851
- case '/tools':
1852
- if (toolSummary) {
1853
- entry.description = `${command.description} (${toolSummary})`;
1854
- }
1855
- break;
1856
- case '/sessions':
1857
- entry.description = `${command.description} (autosave ${autosaveLabel})`;
1858
- break;
1859
- case '/model':
1860
- entry.description = `${command.description} (current: ${this.sessionState.model})`;
1861
- break;
1862
- case '/provider':
1863
- entry.description = `${command.description} (current: ${this.providerLabel(this.sessionState.provider)})`;
1864
- break;
1865
- default:
1866
- break;
1867
- }
1868
- entries.push(entry);
1869
- }
1870
- return entries;
1871
- }
1872
- summarizeSecretsForPalette() {
1873
- const definitions = listSecretDefinitions();
1874
- if (!definitions.length) {
1875
- return { text: null };
1876
- }
1877
- const missing = definitions.filter((definition) => !getSecretValue(definition.id));
1878
- if (missing.length === 0) {
1879
- return { text: 'all configured', tone: 'success' };
1880
- }
1881
- const labels = missing.map((definition) => definition.label ?? definition.id);
1882
- return { text: `missing ${this.formatList(labels)}`, tone: 'warn' };
1883
- }
1884
- getToolSelectionSummary() {
1885
- const toolSettings = loadToolSettings();
1886
- const selection = buildEnabledToolSet(toolSettings);
1887
- const options = getToolToggleOptions();
1888
- if (!options.length) {
1889
- return null;
1890
- }
1891
- const enabledCount = options.filter((option) => selection.has(option.id)).length;
1892
- return `${enabledCount}/${options.length} enabled`;
1893
- }
1894
- formatList(values, maxItems = 3) {
1895
- if (!values.length) {
1896
- return '';
1897
- }
1898
- const shown = values.slice(0, maxItems);
1899
- const suffix = values.length > maxItems ? ', …' : '';
1900
- return `${shown.join(', ')}${suffix}`;
1901
- }
1902
1850
  buildSlashCommandList(header) {
1903
1851
  const lines = [theme.gradient.primary(header), ''];
1904
1852
  for (const command of this.slashCommands) {
@@ -2367,7 +2315,7 @@ export class InteractiveShell {
2367
2315
  this.autosaveIfEnabled();
2368
2316
  // Track metrics with Alpha Zero 2
2369
2317
  const elapsedMs = Date.now() - requestStartTime;
2370
- this.alphaZeroMetrics.recordMessage(elapsedMs);
2318
+ this.sessionMetrics.recordMessage(elapsedMs);
2371
2319
  if (!responseText?.trim()) {
2372
2320
  display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
2373
2321
  }
@@ -2385,10 +2333,14 @@ export class InteractiveShell {
2385
2333
  this.stopStreamingHeartbeat();
2386
2334
  this.isProcessing = false;
2387
2335
  this.terminalInput.setStreaming(false);
2336
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2388
2337
  this.uiAdapter.endProcessing('Ready for prompts');
2389
2338
  this.setIdleStatus();
2390
2339
  display.newLine();
2391
2340
  this.updateStatusMessage(null);
2341
+ // Claude Code style: Show unified status bar before prompt
2342
+ // This creates consistent UI between startup and post-streaming
2343
+ this.showUnifiedStatusBar();
2392
2344
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
2393
2345
  // CRITICAL: Ensure readline prompt is active for user input
2394
2346
  // Claude Code style: New prompt naturally appears at bottom
@@ -2465,14 +2417,13 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
2465
2417
  try {
2466
2418
  // Send the request and capture the response (streaming disabled)
2467
2419
  display.showThinking('Responding...');
2468
- this.refreshStatusLine(true);
2469
2420
  const response = await agent.send(currentPrompt, true);
2470
2421
  await this.awaitPendingCleanup();
2471
2422
  this.captureHistorySnapshot();
2472
2423
  this.autosaveIfEnabled();
2473
2424
  // Track metrics
2474
2425
  const elapsedMs = Date.now() - overallStartTime;
2475
- this.alphaZeroMetrics.recordMessage(elapsedMs);
2426
+ this.sessionMetrics.recordMessage(elapsedMs);
2476
2427
  if (!response?.trim()) {
2477
2428
  display.showWarning('Model returned an empty response. Retrying this iteration...');
2478
2429
  consecutiveNoProgress++;
@@ -2609,6 +2560,7 @@ What's the next action?`;
2609
2560
  this.stopStreamingHeartbeat();
2610
2561
  this.isProcessing = false;
2611
2562
  this.terminalInput.setStreaming(false);
2563
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2612
2564
  this.uiAdapter.endProcessing('Ready for prompts');
2613
2565
  this.setIdleStatus();
2614
2566
  this.updateStatusMessage(null);
@@ -2965,10 +2917,8 @@ What's the next action?`;
2965
2917
  try {
2966
2918
  // Send the error to the agent for fixing
2967
2919
  display.showThinking('Analyzing build errors');
2968
- this.refreshStatusLine(true);
2969
2920
  const response = await this.agent.send(prompt, true);
2970
2921
  display.stopThinking();
2971
- this.refreshStatusLine(true);
2972
2922
  if (response) {
2973
2923
  display.showAssistantMessage(response, { isFinal: true });
2974
2924
  }
@@ -2995,8 +2945,8 @@ What's the next action?`;
2995
2945
  };
2996
2946
  this.agent = this.runtimeSession.createAgent(selection, {
2997
2947
  onStreamChunk: (chunk) => {
2998
- // Stream output using clean streamContent() - chat box floats below
2999
- this.terminalInput.streamContent(chunk);
2948
+ // Stream output directly - no spinner during streaming to avoid race conditions
2949
+ display.stream(chunk);
3000
2950
  },
3001
2951
  onStreamFallback: (info) => this.handleStreamingFallback(info),
3002
2952
  onAssistantMessage: (content, metadata) => {
@@ -3018,16 +2968,18 @@ What's the next action?`;
3018
2968
  display.showAssistantMessage(finalContent, enriched);
3019
2969
  }
3020
2970
  }
3021
- // Status shown in mode controls bar - no separate status line needed
2971
+ // Show status line at end (Claude Code style: "• Context X% used • Ready for prompts (2s)")
3022
2972
  display.stopThinking();
3023
- // Update context usage for mode controls display
2973
+ // Calculate context usage
2974
+ let contextInfo;
3024
2975
  if (enriched.contextWindowTokens && metadata.usage) {
3025
2976
  const total = this.totalTokens(metadata.usage);
3026
2977
  if (total && total > 0) {
3027
2978
  const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
3028
- this.updateContextUsage(percentage);
2979
+ contextInfo = { percentage, tokens: total };
3029
2980
  }
3030
2981
  }
2982
+ display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
3031
2983
  // Auto-verify changes: build first (catches type errors), then tests
3032
2984
  void this.enforceAutoBuild('final-response');
3033
2985
  void this.enforceAutoTests('final-response');
@@ -3097,6 +3049,7 @@ What's the next action?`;
3097
3049
  this.stopStreamingHeartbeat();
3098
3050
  this.updateStatusMessage(null);
3099
3051
  this.terminalInput.setStreaming(false);
3052
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3100
3053
  this.terminalInput.render();
3101
3054
  },
3102
3055
  onVerificationNeeded: () => {
@@ -3133,6 +3086,7 @@ What's the next action?`;
3133
3086
  resetChatBoxAfterModelSwap() {
3134
3087
  this.updateStatusMessage(null);
3135
3088
  this.terminalInput.setStreaming(false);
3089
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3136
3090
  this.terminalInput.render();
3137
3091
  this.ensureReadlineReady();
3138
3092
  }
@@ -3197,14 +3151,9 @@ What's the next action?`;
3197
3151
  return null;
3198
3152
  }
3199
3153
  const usageRatio = total / windowTokens;
3200
- this.latestTokenUsage = {
3201
- used: total,
3202
- limit: windowTokens,
3203
- };
3204
3154
  // Always update context usage in the UI
3205
3155
  const percentUsed = Math.round(usageRatio * 100);
3206
3156
  this.updateContextUsage(percentUsed);
3207
- this.refreshStatusLine(true);
3208
3157
  if (usageRatio < CONTEXT_USAGE_THRESHOLD) {
3209
3158
  return null;
3210
3159
  }
@@ -3471,22 +3420,6 @@ What's the next action?`;
3471
3420
  this.sessionState.reasoningEffort = preset.reasoningEffort;
3472
3421
  }
3473
3422
  }
3474
- /**
3475
- * Build the session banner.
3476
- */
3477
- buildBanner() {
3478
- const terminalWidth = output.columns ?? 100;
3479
- const width = Math.min(terminalWidth - 4, 110);
3480
- return renderSessionFrame({
3481
- profileLabel: this.profileLabel,
3482
- profileName: this.profile,
3483
- model: this.sessionState.model,
3484
- provider: this.sessionState.provider,
3485
- workspace: this.workingDir,
3486
- version: this.version,
3487
- width,
3488
- });
3489
- }
3490
3423
  refreshBannerSessionInfo() {
3491
3424
  const nextState = {
3492
3425
  model: this.sessionState.model,
@@ -3497,11 +3430,13 @@ What's the next action?`;
3497
3430
  return;
3498
3431
  }
3499
3432
  this.refreshContextGauge();
3500
- // Banner is no longer stored in display - it was streamed as content
3501
- // Model/provider changes are visible in the control bar
3433
+ display.updateSessionInfo(nextState.model, nextState.provider);
3434
+ // Update the persistent model info display in terminal input
3435
+ this.terminalInput.setModelInfo(this.describeModelDetail());
3502
3436
  if (!this.isProcessing) {
3503
3437
  this.setIdleStatus();
3504
3438
  }
3439
+ // Pinned header rows are disabled; scroll region handles all output.
3505
3440
  this.bannerSessionState = nextState;
3506
3441
  }
3507
3442
  providerLabel(id) {