erosolar-cli 1.7.344 → 1.7.346

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/README.md +148 -24
  2. package/dist/alpha-zero/agentWrapper.d.ts +84 -0
  3. package/dist/alpha-zero/agentWrapper.d.ts.map +1 -0
  4. package/dist/alpha-zero/agentWrapper.js +171 -0
  5. package/dist/alpha-zero/agentWrapper.js.map +1 -0
  6. package/dist/alpha-zero/codeEvaluator.d.ts +25 -0
  7. package/dist/alpha-zero/codeEvaluator.d.ts.map +1 -0
  8. package/dist/alpha-zero/codeEvaluator.js +273 -0
  9. package/dist/alpha-zero/codeEvaluator.js.map +1 -0
  10. package/dist/alpha-zero/competitiveRunner.d.ts +66 -0
  11. package/dist/alpha-zero/competitiveRunner.d.ts.map +1 -0
  12. package/dist/alpha-zero/competitiveRunner.js +224 -0
  13. package/dist/alpha-zero/competitiveRunner.js.map +1 -0
  14. package/dist/alpha-zero/index.d.ts +67 -0
  15. package/dist/alpha-zero/index.d.ts.map +1 -0
  16. package/dist/alpha-zero/index.js +99 -0
  17. package/dist/alpha-zero/index.js.map +1 -0
  18. package/dist/alpha-zero/introspection.d.ts +128 -0
  19. package/dist/alpha-zero/introspection.d.ts.map +1 -0
  20. package/dist/alpha-zero/introspection.js +300 -0
  21. package/dist/alpha-zero/introspection.js.map +1 -0
  22. package/dist/alpha-zero/metricsTracker.d.ts +71 -0
  23. package/dist/alpha-zero/metricsTracker.d.ts.map +1 -0
  24. package/dist/{core → alpha-zero}/metricsTracker.js +5 -2
  25. package/dist/alpha-zero/metricsTracker.js.map +1 -0
  26. package/dist/alpha-zero/security/core.d.ts +125 -0
  27. package/dist/alpha-zero/security/core.d.ts.map +1 -0
  28. package/dist/alpha-zero/security/core.js +271 -0
  29. package/dist/alpha-zero/security/core.js.map +1 -0
  30. package/dist/alpha-zero/security/google.d.ts +125 -0
  31. package/dist/alpha-zero/security/google.d.ts.map +1 -0
  32. package/dist/alpha-zero/security/google.js +311 -0
  33. package/dist/alpha-zero/security/google.js.map +1 -0
  34. package/dist/alpha-zero/security/googleLoader.d.ts +17 -0
  35. package/dist/alpha-zero/security/googleLoader.d.ts.map +1 -0
  36. package/dist/alpha-zero/security/googleLoader.js +41 -0
  37. package/dist/alpha-zero/security/googleLoader.js.map +1 -0
  38. package/dist/alpha-zero/security/index.d.ts +29 -0
  39. package/dist/alpha-zero/security/index.d.ts.map +1 -0
  40. package/dist/alpha-zero/security/index.js +32 -0
  41. package/dist/alpha-zero/security/index.js.map +1 -0
  42. package/dist/alpha-zero/security/simulation.d.ts +124 -0
  43. package/dist/alpha-zero/security/simulation.d.ts.map +1 -0
  44. package/dist/alpha-zero/security/simulation.js +277 -0
  45. package/dist/alpha-zero/security/simulation.js.map +1 -0
  46. package/dist/alpha-zero/selfModification.d.ts +109 -0
  47. package/dist/alpha-zero/selfModification.d.ts.map +1 -0
  48. package/dist/alpha-zero/selfModification.js +233 -0
  49. package/dist/alpha-zero/selfModification.js.map +1 -0
  50. package/dist/alpha-zero/types.d.ts +170 -0
  51. package/dist/alpha-zero/types.d.ts.map +1 -0
  52. package/dist/alpha-zero/types.js +31 -0
  53. package/dist/alpha-zero/types.js.map +1 -0
  54. package/dist/bin/erosolar.js +21 -5
  55. package/dist/bin/erosolar.js.map +1 -1
  56. package/dist/capabilities/agentSpawningCapability.d.ts.map +1 -1
  57. package/dist/capabilities/agentSpawningCapability.js +31 -56
  58. package/dist/capabilities/agentSpawningCapability.js.map +1 -1
  59. package/dist/capabilities/securityTestingCapability.d.ts +13 -0
  60. package/dist/capabilities/securityTestingCapability.d.ts.map +1 -0
  61. package/dist/capabilities/securityTestingCapability.js +25 -0
  62. package/dist/capabilities/securityTestingCapability.js.map +1 -0
  63. package/dist/contracts/agent-schemas.json +15 -0
  64. package/dist/contracts/tools.schema.json +9 -0
  65. package/dist/core/agent.d.ts +2 -2
  66. package/dist/core/agent.d.ts.map +1 -1
  67. package/dist/core/agent.js.map +1 -1
  68. package/dist/core/aiFlowOptimizer.d.ts +26 -0
  69. package/dist/core/aiFlowOptimizer.d.ts.map +1 -0
  70. package/dist/core/aiFlowOptimizer.js +31 -0
  71. package/dist/core/aiFlowOptimizer.js.map +1 -0
  72. package/dist/core/aiOptimizationEngine.d.ts +158 -0
  73. package/dist/core/aiOptimizationEngine.d.ts.map +1 -0
  74. package/dist/core/aiOptimizationEngine.js +428 -0
  75. package/dist/core/aiOptimizationEngine.js.map +1 -0
  76. package/dist/core/aiOptimizationIntegration.d.ts +93 -0
  77. package/dist/core/aiOptimizationIntegration.d.ts.map +1 -0
  78. package/dist/core/aiOptimizationIntegration.js +250 -0
  79. package/dist/core/aiOptimizationIntegration.js.map +1 -0
  80. package/dist/core/customCommands.d.ts +0 -1
  81. package/dist/core/customCommands.d.ts.map +1 -1
  82. package/dist/core/customCommands.js +0 -3
  83. package/dist/core/customCommands.js.map +1 -1
  84. package/dist/core/enhancedErrorRecovery.d.ts +100 -0
  85. package/dist/core/enhancedErrorRecovery.d.ts.map +1 -0
  86. package/dist/core/enhancedErrorRecovery.js +345 -0
  87. package/dist/core/enhancedErrorRecovery.js.map +1 -0
  88. package/dist/core/hooksSystem.d.ts +65 -0
  89. package/dist/core/hooksSystem.d.ts.map +1 -0
  90. package/dist/core/hooksSystem.js +273 -0
  91. package/dist/core/hooksSystem.js.map +1 -0
  92. package/dist/core/memorySystem.d.ts +48 -0
  93. package/dist/core/memorySystem.d.ts.map +1 -0
  94. package/dist/core/memorySystem.js +271 -0
  95. package/dist/core/memorySystem.js.map +1 -0
  96. package/dist/core/sessionStore.d.ts +0 -2
  97. package/dist/core/sessionStore.d.ts.map +1 -1
  98. package/dist/core/sessionStore.js +0 -1
  99. package/dist/core/sessionStore.js.map +1 -1
  100. package/dist/core/toolPreconditions.d.ts.map +1 -1
  101. package/dist/core/toolPreconditions.js +14 -0
  102. package/dist/core/toolPreconditions.js.map +1 -1
  103. package/dist/core/toolRuntime.d.ts +1 -22
  104. package/dist/core/toolRuntime.d.ts.map +1 -1
  105. package/dist/core/toolRuntime.js +5 -0
  106. package/dist/core/toolRuntime.js.map +1 -1
  107. package/dist/core/toolValidation.d.ts.map +1 -1
  108. package/dist/core/toolValidation.js +3 -14
  109. package/dist/core/toolValidation.js.map +1 -1
  110. package/dist/core/unified/errors.d.ts +189 -0
  111. package/dist/core/unified/errors.d.ts.map +1 -0
  112. package/dist/core/unified/errors.js +497 -0
  113. package/dist/core/unified/errors.js.map +1 -0
  114. package/dist/core/unified/index.d.ts +19 -0
  115. package/dist/core/unified/index.d.ts.map +1 -0
  116. package/dist/core/unified/index.js +68 -0
  117. package/dist/core/unified/index.js.map +1 -0
  118. package/dist/core/unified/schema.d.ts +101 -0
  119. package/dist/core/unified/schema.d.ts.map +1 -0
  120. package/dist/core/unified/schema.js +350 -0
  121. package/dist/core/unified/schema.js.map +1 -0
  122. package/dist/core/unified/toolRuntime.d.ts +179 -0
  123. package/dist/core/unified/toolRuntime.d.ts.map +1 -0
  124. package/dist/core/unified/toolRuntime.js +517 -0
  125. package/dist/core/unified/toolRuntime.js.map +1 -0
  126. package/dist/core/unified/tools.d.ts +127 -0
  127. package/dist/core/unified/tools.d.ts.map +1 -0
  128. package/dist/core/unified/tools.js +1333 -0
  129. package/dist/core/unified/tools.js.map +1 -0
  130. package/dist/core/unified/types.d.ts +352 -0
  131. package/dist/core/unified/types.d.ts.map +1 -0
  132. package/dist/core/unified/types.js +12 -0
  133. package/dist/core/unified/types.js.map +1 -0
  134. package/dist/core/unified/version.d.ts +209 -0
  135. package/dist/core/unified/version.d.ts.map +1 -0
  136. package/dist/core/unified/version.js +454 -0
  137. package/dist/core/unified/version.js.map +1 -0
  138. package/dist/core/validationRunner.d.ts +3 -1
  139. package/dist/core/validationRunner.d.ts.map +1 -1
  140. package/dist/core/validationRunner.js.map +1 -1
  141. package/dist/headless/headlessApp.d.ts.map +1 -1
  142. package/dist/headless/headlessApp.js +0 -21
  143. package/dist/headless/headlessApp.js.map +1 -1
  144. package/dist/mcp/sseClient.d.ts.map +1 -1
  145. package/dist/mcp/sseClient.js +18 -9
  146. package/dist/mcp/sseClient.js.map +1 -1
  147. package/dist/plugins/tools/build/buildPlugin.d.ts +6 -0
  148. package/dist/plugins/tools/build/buildPlugin.d.ts.map +1 -1
  149. package/dist/plugins/tools/build/buildPlugin.js +10 -4
  150. package/dist/plugins/tools/build/buildPlugin.js.map +1 -1
  151. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
  152. package/dist/plugins/tools/nodeDefaults.js +2 -0
  153. package/dist/plugins/tools/nodeDefaults.js.map +1 -1
  154. package/dist/plugins/tools/security/securityPlugin.d.ts +3 -0
  155. package/dist/plugins/tools/security/securityPlugin.d.ts.map +1 -0
  156. package/dist/plugins/tools/security/securityPlugin.js +12 -0
  157. package/dist/plugins/tools/security/securityPlugin.js.map +1 -0
  158. package/dist/runtime/agentSession.d.ts +2 -2
  159. package/dist/runtime/agentSession.d.ts.map +1 -1
  160. package/dist/runtime/agentSession.js +2 -2
  161. package/dist/runtime/agentSession.js.map +1 -1
  162. package/dist/security/active-stack-security.d.ts +112 -0
  163. package/dist/security/active-stack-security.d.ts.map +1 -0
  164. package/dist/security/active-stack-security.js +296 -0
  165. package/dist/security/active-stack-security.js.map +1 -0
  166. package/dist/security/advanced-persistence-research.d.ts +92 -0
  167. package/dist/security/advanced-persistence-research.d.ts.map +1 -0
  168. package/dist/security/advanced-persistence-research.js +195 -0
  169. package/dist/security/advanced-persistence-research.js.map +1 -0
  170. package/dist/security/advanced-targeting.d.ts +119 -0
  171. package/dist/security/advanced-targeting.d.ts.map +1 -0
  172. package/dist/security/advanced-targeting.js +233 -0
  173. package/dist/security/advanced-targeting.js.map +1 -0
  174. package/dist/security/assessment/vulnerabilityAssessment.d.ts +104 -0
  175. package/dist/security/assessment/vulnerabilityAssessment.d.ts.map +1 -0
  176. package/dist/security/assessment/vulnerabilityAssessment.js +315 -0
  177. package/dist/security/assessment/vulnerabilityAssessment.js.map +1 -0
  178. package/dist/security/authorization/securityAuthorization.d.ts +88 -0
  179. package/dist/security/authorization/securityAuthorization.d.ts.map +1 -0
  180. package/dist/security/authorization/securityAuthorization.js +172 -0
  181. package/dist/security/authorization/securityAuthorization.js.map +1 -0
  182. package/dist/security/comprehensive-targeting.d.ts +85 -0
  183. package/dist/security/comprehensive-targeting.d.ts.map +1 -0
  184. package/dist/security/comprehensive-targeting.js +438 -0
  185. package/dist/security/comprehensive-targeting.js.map +1 -0
  186. package/dist/security/global-security-integration.d.ts +91 -0
  187. package/dist/security/global-security-integration.d.ts.map +1 -0
  188. package/dist/security/global-security-integration.js +218 -0
  189. package/dist/security/global-security-integration.js.map +1 -0
  190. package/dist/security/index.d.ts +38 -0
  191. package/dist/security/index.d.ts.map +1 -0
  192. package/dist/security/index.js +47 -0
  193. package/dist/security/index.js.map +1 -0
  194. package/dist/security/persistence-analyzer.d.ts +56 -0
  195. package/dist/security/persistence-analyzer.d.ts.map +1 -0
  196. package/dist/security/persistence-analyzer.js +187 -0
  197. package/dist/security/persistence-analyzer.js.map +1 -0
  198. package/dist/security/persistence-cli.d.ts +36 -0
  199. package/dist/security/persistence-cli.d.ts.map +1 -0
  200. package/dist/security/persistence-cli.js +160 -0
  201. package/dist/security/persistence-cli.js.map +1 -0
  202. package/dist/security/persistence-research.d.ts +92 -0
  203. package/dist/security/persistence-research.d.ts.map +1 -0
  204. package/dist/security/persistence-research.js +364 -0
  205. package/dist/security/persistence-research.js.map +1 -0
  206. package/dist/security/research/persistenceResearch.d.ts +97 -0
  207. package/dist/security/research/persistenceResearch.d.ts.map +1 -0
  208. package/dist/security/research/persistenceResearch.js +282 -0
  209. package/dist/security/research/persistenceResearch.js.map +1 -0
  210. package/dist/security/security-integration.d.ts +74 -0
  211. package/dist/security/security-integration.d.ts.map +1 -0
  212. package/dist/security/security-integration.js +137 -0
  213. package/dist/security/security-integration.js.map +1 -0
  214. package/dist/security/security-testing-framework.d.ts +112 -0
  215. package/dist/security/security-testing-framework.d.ts.map +1 -0
  216. package/dist/security/security-testing-framework.js +364 -0
  217. package/dist/security/security-testing-framework.js.map +1 -0
  218. package/dist/security/simulation/attackSimulation.d.ts +93 -0
  219. package/dist/security/simulation/attackSimulation.d.ts.map +1 -0
  220. package/dist/security/simulation/attackSimulation.js +341 -0
  221. package/dist/security/simulation/attackSimulation.js.map +1 -0
  222. package/dist/security/strategic-operations.d.ts +100 -0
  223. package/dist/security/strategic-operations.d.ts.map +1 -0
  224. package/dist/security/strategic-operations.js +276 -0
  225. package/dist/security/strategic-operations.js.map +1 -0
  226. package/dist/security/tool-security-wrapper.d.ts +58 -0
  227. package/dist/security/tool-security-wrapper.d.ts.map +1 -0
  228. package/dist/security/tool-security-wrapper.js +156 -0
  229. package/dist/security/tool-security-wrapper.js.map +1 -0
  230. package/dist/shell/claudeCodeStreamHandler.d.ts +145 -0
  231. package/dist/shell/claudeCodeStreamHandler.d.ts.map +1 -0
  232. package/dist/shell/claudeCodeStreamHandler.js +322 -0
  233. package/dist/shell/claudeCodeStreamHandler.js.map +1 -0
  234. package/dist/shell/inputQueueManager.d.ts +144 -0
  235. package/dist/shell/inputQueueManager.d.ts.map +1 -0
  236. package/dist/shell/inputQueueManager.js +290 -0
  237. package/dist/shell/inputQueueManager.js.map +1 -0
  238. package/dist/shell/interactiveShell.d.ts +7 -19
  239. package/dist/shell/interactiveShell.d.ts.map +1 -1
  240. package/dist/shell/interactiveShell.js +166 -271
  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 +120 -218
  258. package/dist/shell/terminalInput.d.ts.map +1 -1
  259. package/dist/shell/terminalInput.js +537 -932
  260. package/dist/shell/terminalInput.js.map +1 -1
  261. package/dist/shell/terminalInputAdapter.d.ts +21 -99
  262. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  263. package/dist/shell/terminalInputAdapter.js +30 -135
  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/securityTools.d.ts +22 -0
  270. package/dist/tools/securityTools.d.ts.map +1 -0
  271. package/dist/tools/securityTools.js +448 -0
  272. package/dist/tools/securityTools.js.map +1 -0
  273. package/dist/ui/ShellUIAdapter.d.ts +1 -7
  274. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  275. package/dist/ui/ShellUIAdapter.js +18 -42
  276. package/dist/ui/ShellUIAdapter.js.map +1 -1
  277. package/dist/ui/display.d.ts +45 -24
  278. package/dist/ui/display.d.ts.map +1 -1
  279. package/dist/ui/display.js +259 -140
  280. package/dist/ui/display.js.map +1 -1
  281. package/dist/ui/persistentPrompt.d.ts +50 -0
  282. package/dist/ui/persistentPrompt.d.ts.map +1 -0
  283. package/dist/ui/persistentPrompt.js +92 -0
  284. package/dist/ui/persistentPrompt.js.map +1 -0
  285. package/dist/ui/terminalUISchema.d.ts +195 -0
  286. package/dist/ui/terminalUISchema.d.ts.map +1 -0
  287. package/dist/ui/terminalUISchema.js +113 -0
  288. package/dist/ui/terminalUISchema.js.map +1 -0
  289. package/dist/ui/theme.d.ts.map +1 -1
  290. package/dist/ui/theme.js +8 -6
  291. package/dist/ui/theme.js.map +1 -1
  292. package/dist/ui/toolDisplay.d.ts +158 -0
  293. package/dist/ui/toolDisplay.d.ts.map +1 -1
  294. package/dist/ui/toolDisplay.js +348 -0
  295. package/dist/ui/toolDisplay.js.map +1 -1
  296. package/dist/ui/unified/layout.d.ts +0 -20
  297. package/dist/ui/unified/layout.d.ts.map +1 -1
  298. package/dist/ui/unified/layout.js +216 -29
  299. package/dist/ui/unified/layout.js.map +1 -1
  300. package/package.json +4 -4
  301. package/scripts/deploy-security-capabilities.js +178 -0
  302. package/dist/core/hooks.d.ts +0 -113
  303. package/dist/core/hooks.d.ts.map +0 -1
  304. package/dist/core/hooks.js +0 -267
  305. package/dist/core/hooks.js.map +0 -1
  306. package/dist/core/metricsTracker.d.ts +0 -122
  307. package/dist/core/metricsTracker.d.ts.map +0 -1
  308. package/dist/core/metricsTracker.js.map +0 -1
  309. package/dist/core/securityAssessment.d.ts +0 -91
  310. package/dist/core/securityAssessment.d.ts.map +0 -1
  311. package/dist/core/securityAssessment.js +0 -580
  312. package/dist/core/securityAssessment.js.map +0 -1
  313. package/dist/core/verification.d.ts +0 -137
  314. package/dist/core/verification.d.ts.map +0 -1
  315. package/dist/core/verification.js +0 -323
  316. package/dist/core/verification.js.map +0 -1
  317. package/dist/subagents/agentConfig.d.ts +0 -27
  318. package/dist/subagents/agentConfig.d.ts.map +0 -1
  319. package/dist/subagents/agentConfig.js +0 -89
  320. package/dist/subagents/agentConfig.js.map +0 -1
  321. package/dist/subagents/agentRegistry.d.ts +0 -33
  322. package/dist/subagents/agentRegistry.d.ts.map +0 -1
  323. package/dist/subagents/agentRegistry.js +0 -162
  324. package/dist/subagents/agentRegistry.js.map +0 -1
  325. package/dist/utils/frontmatter.d.ts +0 -10
  326. package/dist/utils/frontmatter.d.ts.map +0 -1
  327. package/dist/utils/frontmatter.js +0 -78
  328. package/dist/utils/frontmatter.js.map +0 -1
@@ -2,7 +2,7 @@ import { stdin as input, stdout as output, exit } from 'node:process';
2
2
  import { exec } from 'node:child_process';
3
3
  import { promisify } from 'node:util';
4
4
  import { display } from '../ui/display.js';
5
- import { theme } from '../ui/theme.js';
5
+ import { theme, formatUserPrompt } from '../ui/theme.js';
6
6
  import { getContextWindowTokens } from '../core/contextWindow.js';
7
7
  import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
8
8
  import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, } from '../core/preferences.js';
@@ -19,11 +19,11 @@ import { SkillRepository } from '../skills/skillRepository.js';
19
19
  import { createSkillTools } from '../tools/skillTools.js';
20
20
  import { FileChangeTracker } from './fileChangeTracker.js';
21
21
  import { formatShortcutsHelp } from '../ui/shortcutsHelp.js';
22
- import { MetricsTracker } from '../core/metricsTracker.js';
22
+ import { MetricsTracker } from './metricsTracker.js';
23
23
  import { listAvailablePlugins } from '../plugins/index.js';
24
+ import { loadMemory, listMemoryPaths, getDefaultProjectMemoryPath, getUserMemoryEditPath, } from '../core/memorySystem.js';
24
25
  import { TerminalInputAdapter } from './terminalInputAdapter.js';
25
- import { renderSessionFrame } from '../ui/unified/layout.js';
26
- import { isUpdateInProgress, maybeOfferCliUpdate } from './updateManager.js';
26
+ import { isUpdateInProgress } from './updateManager.js';
27
27
  import { writeLock } from '../ui/writeLock.js';
28
28
  import { enterStreamingMode, exitStreamingMode } from '../ui/globalWriteLock.js';
29
29
  const execAsync = promisify(exec);
@@ -35,7 +35,6 @@ const DROPDOWN_COLORS = [
35
35
  theme.success,
36
36
  theme.warning,
37
37
  ];
38
- const STREAMING_SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
39
38
  // Load MODEL_PRESETS from centralized schema
40
39
  const MODEL_PRESETS = getModels().map((model) => ({
41
40
  id: model.id,
@@ -50,13 +49,11 @@ const MODEL_PRESETS = getModels().map((model) => ({
50
49
  const BASE_SLASH_COMMANDS = getSlashCommands().map((cmd) => ({
51
50
  command: cmd.command,
52
51
  description: cmd.description,
53
- category: cmd.category,
54
52
  }));
55
53
  // Load PROVIDER_LABELS from centralized schema
56
54
  const PROVIDER_LABELS = Object.fromEntries(getProviders().map((provider) => [provider.id, provider.label]));
57
55
  // Allow enough time for paste detection to kick in before flushing buffered lines
58
56
  const CONTEXT_USAGE_THRESHOLD = 0.9;
59
- const CONTEXT_AUTOCOMPACT_PERCENT = Math.round(CONTEXT_USAGE_THRESHOLD * 100);
60
57
  const CONTEXT_RECENT_MESSAGE_COUNT = 12;
61
58
  const CONTEXT_CLEANUP_CHARS_PER_CHUNK = 6000;
62
59
  const CONTEXT_CLEANUP_MAX_OUTPUT_TOKENS = 800;
@@ -97,12 +94,11 @@ export class InteractiveShell {
97
94
  uiAdapter;
98
95
  uiUpdates;
99
96
  _fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
100
- alphaZeroMetrics; // Alpha Zero 2 performance tracking
97
+ sessionMetrics; // Session performance tracking
101
98
  statusSubscription = null;
102
99
  followUpQueue = [];
103
100
  isDrainingQueue = false;
104
101
  activeContextWindowTokens = null;
105
- latestTokenUsage = { used: null, limit: null };
106
102
  sessionPreferences;
107
103
  autosaveEnabled;
108
104
  autoContinueEnabled;
@@ -133,9 +129,6 @@ export class InteractiveShell {
133
129
  statusLineState = null;
134
130
  statusMessageOverride = null;
135
131
  promptRefreshTimer = null;
136
- launchPaletteShown = false;
137
- version;
138
- alternateScreenEnabled;
139
132
  constructor(config) {
140
133
  this.profile = config.profile;
141
134
  this.profileLabel = config.profileLabel;
@@ -149,8 +142,6 @@ export class InteractiveShell {
149
142
  this.autoContinueEnabled = this.sessionPreferences.autoContinue;
150
143
  this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
151
144
  this._enabledPlugins = config.enabledPlugins ?? [];
152
- this.version = config.version ?? '0.0.0';
153
- this.alternateScreenEnabled = config.alternateScreen ?? true;
154
145
  this.initializeSessionHistory();
155
146
  this.sessionState = {
156
147
  provider: config.initialModel.provider,
@@ -171,7 +162,6 @@ export class InteractiveShell {
171
162
  this.slashCommands.push({
172
163
  command: '/agents',
173
164
  description: 'Select the default agent profile (applies on next launch)',
174
- category: 'configuration',
175
165
  });
176
166
  }
177
167
  this.customCommands = loadCustomSlashCommands();
@@ -180,21 +170,18 @@ export class InteractiveShell {
180
170
  this.slashCommands.push({
181
171
  command: custom.command,
182
172
  description: `${custom.description} (custom)`,
183
- category: custom.category ?? 'other',
184
173
  });
185
174
  }
186
175
  if (!this.slashCommands.some((cmd) => cmd.command === '/exit')) {
187
176
  this.slashCommands.push({
188
177
  command: '/exit',
189
178
  description: 'Quit the CLI immediately',
190
- category: 'other',
191
179
  });
192
180
  }
193
181
  // Add /plugins command
194
182
  this.slashCommands.push({
195
183
  command: '/plugins',
196
184
  description: 'Show available and loaded plugins',
197
- category: 'configuration',
198
185
  });
199
186
  this.statusTracker = config.statusTracker;
200
187
  this.ui = config.ui;
@@ -227,25 +214,19 @@ export class InteractiveShell {
227
214
  onToggleVerify: () => this.toggleVerificationMode(),
228
215
  onToggleAutoContinue: () => this.toggleAutoContinueMode(),
229
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();
230
224
  // Initialize Alpha Zero 2 metrics tracking
231
- this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
225
+ this.sessionMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
232
226
  this.setupStatusTracking();
233
227
  this.refreshContextGauge();
234
- // Start terminal input (sets up handlers)
235
228
  this.terminalInput.start();
236
- // Enter alternate screen buffer when enabled, otherwise clear the main screen for layout
237
- if (this.alternateScreenEnabled) {
238
- this.terminalInput.enterAlternateScreen();
239
- }
240
- else if (output.isTTY) {
241
- this.terminalInput.clearScreen();
242
- }
243
- // Stream banner first - this sets up scroll region dynamically
244
- const banner = this.buildBanner();
245
- this.terminalInput.streamContent(banner + '\n\n');
246
- // Render chat box after banner is streamed
247
229
  this.refreshControlBar();
248
- this.terminalInput.forceRender();
249
230
  this.rebuildAgent();
250
231
  this.setupHandlers();
251
232
  this.refreshBannerSessionInfo();
@@ -265,10 +246,6 @@ export class InteractiveShell {
265
246
  this.activeSessionId = stored.id;
266
247
  this.activeSessionTitle = stored.title;
267
248
  this.sessionResumeNotice = `Resumed session "${stored.title}".`;
268
- // Restore scrollback buffer if available
269
- if (stored.scrollbackBuffer && Array.isArray(stored.scrollbackBuffer)) {
270
- this.terminalInput.loadScrollbackBuffer(stored.scrollbackBuffer);
271
- }
272
249
  return;
273
250
  }
274
251
  display.showWarning(`Session "${this.sessionRestoreConfig.sessionId}" not found. Starting fresh session.`);
@@ -282,10 +259,6 @@ export class InteractiveShell {
282
259
  this.activeSessionId = null;
283
260
  this.activeSessionTitle = autosave.title;
284
261
  this.sessionResumeNotice = 'Restored last autosaved session.';
285
- // Restore scrollback buffer if available
286
- if (autosave.scrollbackBuffer && Array.isArray(autosave.scrollbackBuffer)) {
287
- this.terminalInput.loadScrollbackBuffer(autosave.scrollbackBuffer);
288
- }
289
262
  return;
290
263
  }
291
264
  display.showWarning('No autosaved session found. Starting fresh session.');
@@ -300,24 +273,14 @@ export class InteractiveShell {
300
273
  this.sessionResumeNotice = null;
301
274
  }
302
275
  async start(initialPrompt) {
303
- // Check for updates in background (non-blocking)
304
- if (this.version) {
305
- void maybeOfferCliUpdate(this.version);
306
- }
307
276
  if (initialPrompt) {
308
277
  this.logUserPrompt(initialPrompt);
309
278
  await this.processInputBlock(initialPrompt);
310
279
  return;
311
280
  }
312
- this.showLaunchCommandPalette();
313
281
  // Ensure the terminal input is visible
314
282
  this.terminalInput.render();
315
283
  }
316
- showLaunchCommandPalette() {
317
- // Disabled: Quick commands palette takes up too much space
318
- // Users can type /help to see available commands
319
- this.launchPaletteShown = true;
320
- }
321
284
  /**
322
285
  * TerminalInputAdapter submit handler
323
286
  */
@@ -331,8 +294,9 @@ export class InteractiveShell {
331
294
  this.handleInputChange('');
332
295
  return;
333
296
  }
334
- // DON'T clear the input here - keep it visible while streaming.
335
- // 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);
336
300
  this.logUserPrompt(approved);
337
301
  void this.processInputBlock(approved).catch((err) => {
338
302
  display.showError(err instanceof Error ? err.message : String(err), err);
@@ -521,10 +485,6 @@ export class InteractiveShell {
521
485
  return;
522
486
  }
523
487
  this.shuttingDown = true;
524
- const shouldRestoreScrollback = this.alternateScreenEnabled && this.terminalInput.isAlternateScreenActive();
525
- const scrollbackSnapshot = shouldRestoreScrollback
526
- ? this.terminalInput.getScrollbackSnapshot()
527
- : null;
528
488
  // Stop any active spinner to prevent process hang
529
489
  display.stopThinking(false);
530
490
  this.stopStreamingHeartbeat();
@@ -533,34 +493,18 @@ export class InteractiveShell {
533
493
  this.teardownStatusTracking();
534
494
  // Clear any pending cleanup to prevent hanging
535
495
  this.pendingCleanup = null;
536
- // Reset terminal state before disposing adapters
537
- this.terminalInput.exitStreamingScrollRegion();
538
- if (this.alternateScreenEnabled) {
539
- this.terminalInput.exitAlternateScreen();
540
- }
541
496
  // Dispose terminal input handler
542
497
  this.terminalInput.dispose();
543
498
  // Dispose unified UI adapter
544
499
  this.uiAdapter.dispose();
545
- if (scrollbackSnapshot && scrollbackSnapshot.length > 0) {
546
- this.restoreScrollbackSnapshot(scrollbackSnapshot);
547
- }
548
500
  display.newLine();
549
- console.log(theme.ui.muted(''.repeat(44)));
550
- console.log(theme.ui.muted(' Goodbye! · support@ero.solar'));
551
- 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)));
552
506
  exit(0);
553
507
  }
554
- restoreScrollbackSnapshot(lines) {
555
- if (!lines.length) {
556
- return;
557
- }
558
- const transcript = lines.join('\n');
559
- const separator = theme.ui.muted('─'.repeat(44));
560
- const header = theme.ui.muted('Restored scrollback from this session:');
561
- // Write directly to stdout after exiting alternate screen to preserve the transcript
562
- process.stdout.write(`\n${separator}\n${header}\n${transcript}\n${separator}\n`);
563
- }
564
508
  /**
565
509
  * Update status bar message
566
510
  */
@@ -728,14 +672,13 @@ export class InteractiveShell {
728
672
  });
729
673
  }
730
674
  setProcessingStatus(detail) {
731
- this.latestTokenUsage = { used: null, limit: this.latestTokenUsage.limit };
732
675
  this.statusTracker.setBase('Working on your request', {
733
676
  detail: this.describeStatusDetail(detail),
734
677
  tone: 'info',
735
678
  });
736
679
  }
737
680
  describeStatusDetail(detail) {
738
- const parts = detail?.trim() ? [detail.trim()] : [];
681
+ const parts = [detail?.trim() || this.describeModelDetail()];
739
682
  const queued = this.followUpQueue.length;
740
683
  if (queued > 0) {
741
684
  parts.push(`${queued} follow-up${queued === 1 ? '' : 's'} queued`);
@@ -748,18 +691,12 @@ export class InteractiveShell {
748
691
  }
749
692
  refreshContextGauge() {
750
693
  const tokens = getContextWindowTokens(this.sessionState.model);
751
- const normalizedTokens = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
752
- this.activeContextWindowTokens = normalizedTokens;
753
- if (normalizedTokens !== null) {
754
- this.latestTokenUsage = {
755
- used: this.latestTokenUsage.used,
756
- limit: normalizedTokens,
757
- };
758
- }
694
+ this.activeContextWindowTokens =
695
+ typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
759
696
  }
760
697
  updateContextUsage(percentage) {
761
698
  this.uiAdapter.updateContextUsage(percentage);
762
- this.terminalInput.setContextUsage(percentage, CONTEXT_AUTOCOMPACT_PERCENT);
699
+ this.terminalInput.setContextUsage(percentage);
763
700
  }
764
701
  refreshControlBar() {
765
702
  this.terminalInput.setModeToggles({
@@ -767,9 +704,9 @@ export class InteractiveShell {
767
704
  autoContinueEnabled: this.autoContinueEnabled,
768
705
  verificationHotkey: 'alt+v',
769
706
  autoContinueHotkey: 'alt+c',
770
- thinkingModeLabel: this.thinkingMode,
771
- thinkingHotkey: '/thinking',
772
707
  });
708
+ // Update persistent model info display
709
+ this.terminalInput.setModelInfo(this.describeModelDetail());
773
710
  this.refreshStatusLine();
774
711
  this.terminalInput.render();
775
712
  }
@@ -800,25 +737,6 @@ export class InteractiveShell {
800
737
  // Set main status (tool execution, etc.) - shown when not overridden
801
738
  const statusText = this.formatStatusLine(this.statusLineState);
802
739
  this.terminalInput.setStatusMessage(statusText);
803
- // Surface meta header (elapsed + context usage) above the divider
804
- const elapsedSeconds = this.statusLineState
805
- ? Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000))
806
- : null;
807
- const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
808
- const tokensUsed = this.latestTokenUsage.used;
809
- const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
810
- this.terminalInput.setMetaStatus({
811
- elapsedSeconds,
812
- tokensUsed,
813
- tokenLimit,
814
- thinkingMs,
815
- thinkingHasContent: display.isSpinnerActive(),
816
- });
817
- // Keep model/provider visible in the controls bar
818
- this.terminalInput.setModelContext({
819
- model: this.sessionState.model,
820
- provider: this.providerLabel(this.sessionState.provider),
821
- });
822
740
  if (forceRender) {
823
741
  this.terminalInput.render();
824
742
  }
@@ -878,14 +796,15 @@ export class InteractiveShell {
878
796
  this.terminalInput.render();
879
797
  }
880
798
  /**
881
- * 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.
882
801
  */
883
802
  logUserPrompt(text) {
884
- if (!text.trim())
885
- return;
886
- // Format with user prompt prefix and write to scroll region
887
- const formatted = `${theme.user('>')} ${text}\n`;
888
- 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());
889
808
  }
890
809
  requestPromptRefresh(force = false) {
891
810
  if (force) {
@@ -910,40 +829,16 @@ export class InteractiveShell {
910
829
  this.stopStreamingHeartbeat();
911
830
  // Enter global streaming mode - blocks all non-streaming UI output
912
831
  enterStreamingMode();
913
- // Set up scroll region for streaming content
914
- this.terminalInput.enterStreamingScrollRegion();
915
832
  this.uiUpdates.setMode('streaming');
916
833
  this.streamingHeartbeatStart = Date.now();
917
834
  this.streamingHeartbeatFrame = 0;
918
- const initialFrame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
919
- this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
920
- display.updateStreamingStatus(this.streamingStatusLabel);
921
- this.refreshStatusLine(true);
922
- // Periodically refresh the pinned input/status region while streaming so
923
- // elapsed time remains visible without interrupting the scroll region.
924
- this.uiUpdates.startHeartbeat('streaming', {
925
- intervalMs: 1000,
926
- lane: 'heartbeat',
927
- mode: ['streaming', 'processing'],
928
- coalesceKey: 'streaming:heartbeat',
929
- run: () => {
930
- const elapsedSeconds = this.streamingHeartbeatStart
931
- ? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
932
- : 0;
933
- this.streamingHeartbeatFrame =
934
- (this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
935
- const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
936
- this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
937
- display.updateStreamingStatus(this.streamingStatusLabel);
938
- this.refreshStatusLine(true);
939
- },
940
- });
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.
941
838
  }
942
839
  stopStreamingHeartbeat() {
943
840
  // Exit global streaming mode - allows UI to render again
944
841
  exitStreamingMode();
945
- // Exit scroll region mode
946
- this.terminalInput.exitStreamingScrollRegion();
947
842
  this.uiUpdates.stopHeartbeat('streaming');
948
843
  this.streamingHeartbeatStart = null;
949
844
  this.streamingHeartbeatFrame = 0;
@@ -955,28 +850,10 @@ export class InteractiveShell {
955
850
  // Force refresh to update the input area now that streaming has ended
956
851
  this.refreshStatusLine(true);
957
852
  }
958
- buildStreamingStatus(label, elapsedSeconds) {
853
+ buildStreamingStatus(label) {
959
854
  const detail = this.describeModelDetail();
960
- const elapsedLabel = typeof elapsedSeconds === 'number' && elapsedSeconds >= 0
961
- ? theme.ui.muted(this.formatElapsedShort(elapsedSeconds))
962
- : null;
963
- const prefix = theme.info('⏺');
964
- const parts = [label];
965
- if (detail) {
966
- parts.push(theme.ui.muted('·'), detail);
967
- }
968
- if (elapsedLabel) {
969
- parts.push(theme.ui.muted('·'), elapsedLabel);
970
- }
971
- return `${prefix} ${parts.join(' ')}`.trim();
972
- }
973
- formatElapsedShort(seconds) {
974
- if (seconds < 60) {
975
- return `${seconds}s`;
976
- }
977
- const minutes = Math.floor(seconds / 60);
978
- const remaining = seconds % 60;
979
- 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}`;
980
857
  }
981
858
  refreshQueueIndicators() {
982
859
  if (this.isProcessing) {
@@ -1224,6 +1101,17 @@ export class InteractiveShell {
1224
1101
  case '/discover':
1225
1102
  await this.discoverModelsCommand();
1226
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;
1227
1115
  default:
1228
1116
  if (!(await this.tryCustomSlashCommand(command, input))) {
1229
1117
  display.showWarning(`Unknown command "${command}".`);
@@ -1532,6 +1420,99 @@ export class InteractiveShell {
1532
1420
  // Display keyboard shortcuts help (Claude Code style)
1533
1421
  display.showSystemMessage(formatShortcutsHelp());
1534
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
+ }
1535
1516
  showFileChangeSummary() {
1536
1517
  const summary = this._fileChangeTracker.getSummary();
1537
1518
  const changes = this._fileChangeTracker.getAllChanges();
@@ -1574,11 +1555,11 @@ export class InteractiveShell {
1574
1555
  display.showSystemMessage(lines.join('\n'));
1575
1556
  }
1576
1557
  showAlphaZeroMetrics() {
1577
- const summary = this.alphaZeroMetrics.getPerformanceSummary();
1558
+ const summary = this.sessionMetrics.getPerformanceSummary();
1578
1559
  display.showSystemMessage(summary);
1579
1560
  }
1580
1561
  showImprovementSuggestions() {
1581
- const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
1562
+ const suggestions = this.sessionMetrics.getImprovementSuggestions();
1582
1563
  if (suggestions.length === 0) {
1583
1564
  display.showInfo('No improvement suggestions at this time. Keep using the shell to generate metrics!');
1584
1565
  return;
@@ -1624,9 +1605,7 @@ export class InteractiveShell {
1624
1605
  }
1625
1606
  }
1626
1607
  lines.push(theme.secondary('CLI Flags:'));
1627
- lines.push(' --alpha-zero Enable Alpha Zero 2 RL framework');
1628
1608
  lines.push(' --coding Enable enhanced coding tools');
1629
- lines.push(' --security Enable security research tools');
1630
1609
  lines.push(' --all-plugins Enable all optional plugins');
1631
1610
  display.showSystemMessage(lines.join('\n'));
1632
1611
  }
@@ -1675,7 +1654,6 @@ export class InteractiveShell {
1675
1654
  model: this.sessionState.model,
1676
1655
  workspaceRoot: this.workingDir,
1677
1656
  messages: history,
1678
- scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
1679
1657
  });
1680
1658
  this.cachedHistory = history;
1681
1659
  this.updateActiveSession(summary, true);
@@ -1854,7 +1832,6 @@ export class InteractiveShell {
1854
1832
  workspaceRoot: this.workingDir,
1855
1833
  title: this.activeSessionTitle,
1856
1834
  messages: this.cachedHistory,
1857
- scrollbackBuffer: this.terminalInput.getScrollbackBuffer(),
1858
1835
  });
1859
1836
  }
1860
1837
  describeWorkspaceOptions() {
@@ -1872,75 +1849,6 @@ export class InteractiveShell {
1872
1849
  }
1873
1850
  return `${warning.label}: ${warning.reason}.`;
1874
1851
  }
1875
- buildLaunchCommandPalette() {
1876
- const entries = [];
1877
- const secretsSummary = this.summarizeSecretsForPalette();
1878
- const toolSummary = this.getToolSelectionSummary();
1879
- const autosaveLabel = this.autosaveEnabled ? 'on' : 'off';
1880
- for (const command of this.slashCommands) {
1881
- const entry = {
1882
- command: command.command,
1883
- description: command.description,
1884
- category: command.category ?? 'other',
1885
- };
1886
- switch (command.command) {
1887
- case '/secrets':
1888
- if (secretsSummary.text) {
1889
- entry.description = `${command.description} (${secretsSummary.text})`;
1890
- entry.tone = secretsSummary.tone;
1891
- }
1892
- break;
1893
- case '/tools':
1894
- if (toolSummary) {
1895
- entry.description = `${command.description} (${toolSummary})`;
1896
- }
1897
- break;
1898
- case '/sessions':
1899
- entry.description = `${command.description} (autosave ${autosaveLabel})`;
1900
- break;
1901
- case '/model':
1902
- entry.description = `${command.description} (current: ${this.sessionState.model})`;
1903
- break;
1904
- case '/provider':
1905
- entry.description = `${command.description} (current: ${this.providerLabel(this.sessionState.provider)})`;
1906
- break;
1907
- default:
1908
- break;
1909
- }
1910
- entries.push(entry);
1911
- }
1912
- return entries;
1913
- }
1914
- summarizeSecretsForPalette() {
1915
- const definitions = listSecretDefinitions();
1916
- if (!definitions.length) {
1917
- return { text: null };
1918
- }
1919
- const missing = definitions.filter((definition) => !getSecretValue(definition.id));
1920
- if (missing.length === 0) {
1921
- return { text: 'all configured', tone: 'success' };
1922
- }
1923
- const labels = missing.map((definition) => definition.label ?? definition.id);
1924
- return { text: `missing ${this.formatList(labels)}`, tone: 'warn' };
1925
- }
1926
- getToolSelectionSummary() {
1927
- const toolSettings = loadToolSettings();
1928
- const selection = buildEnabledToolSet(toolSettings);
1929
- const options = getToolToggleOptions();
1930
- if (!options.length) {
1931
- return null;
1932
- }
1933
- const enabledCount = options.filter((option) => selection.has(option.id)).length;
1934
- return `${enabledCount}/${options.length} enabled`;
1935
- }
1936
- formatList(values, maxItems = 3) {
1937
- if (!values.length) {
1938
- return '';
1939
- }
1940
- const shown = values.slice(0, maxItems);
1941
- const suffix = values.length > maxItems ? ', …' : '';
1942
- return `${shown.join(', ')}${suffix}`;
1943
- }
1944
1852
  buildSlashCommandList(header) {
1945
1853
  const lines = [theme.gradient.primary(header), ''];
1946
1854
  for (const command of this.slashCommands) {
@@ -2409,7 +2317,7 @@ export class InteractiveShell {
2409
2317
  this.autosaveIfEnabled();
2410
2318
  // Track metrics with Alpha Zero 2
2411
2319
  const elapsedMs = Date.now() - requestStartTime;
2412
- this.alphaZeroMetrics.recordMessage(elapsedMs);
2320
+ this.sessionMetrics.recordMessage(elapsedMs);
2413
2321
  if (!responseText?.trim()) {
2414
2322
  display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
2415
2323
  }
@@ -2427,10 +2335,14 @@ export class InteractiveShell {
2427
2335
  this.stopStreamingHeartbeat();
2428
2336
  this.isProcessing = false;
2429
2337
  this.terminalInput.setStreaming(false);
2338
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2430
2339
  this.uiAdapter.endProcessing('Ready for prompts');
2431
2340
  this.setIdleStatus();
2432
2341
  display.newLine();
2433
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();
2434
2346
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
2435
2347
  // CRITICAL: Ensure readline prompt is active for user input
2436
2348
  // Claude Code style: New prompt naturally appears at bottom
@@ -2507,14 +2419,13 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
2507
2419
  try {
2508
2420
  // Send the request and capture the response (streaming disabled)
2509
2421
  display.showThinking('Responding...');
2510
- this.refreshStatusLine(true);
2511
2422
  const response = await agent.send(currentPrompt, true);
2512
2423
  await this.awaitPendingCleanup();
2513
2424
  this.captureHistorySnapshot();
2514
2425
  this.autosaveIfEnabled();
2515
2426
  // Track metrics
2516
2427
  const elapsedMs = Date.now() - overallStartTime;
2517
- this.alphaZeroMetrics.recordMessage(elapsedMs);
2428
+ this.sessionMetrics.recordMessage(elapsedMs);
2518
2429
  if (!response?.trim()) {
2519
2430
  display.showWarning('Model returned an empty response. Retrying this iteration...');
2520
2431
  consecutiveNoProgress++;
@@ -2651,6 +2562,7 @@ What's the next action?`;
2651
2562
  this.stopStreamingHeartbeat();
2652
2563
  this.isProcessing = false;
2653
2564
  this.terminalInput.setStreaming(false);
2565
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
2654
2566
  this.uiAdapter.endProcessing('Ready for prompts');
2655
2567
  this.setIdleStatus();
2656
2568
  this.updateStatusMessage(null);
@@ -3007,10 +2919,8 @@ What's the next action?`;
3007
2919
  try {
3008
2920
  // Send the error to the agent for fixing
3009
2921
  display.showThinking('Analyzing build errors');
3010
- this.refreshStatusLine(true);
3011
2922
  const response = await this.agent.send(prompt, true);
3012
2923
  display.stopThinking();
3013
- this.refreshStatusLine(true);
3014
2924
  if (response) {
3015
2925
  display.showAssistantMessage(response, { isFinal: true });
3016
2926
  }
@@ -3037,8 +2947,8 @@ What's the next action?`;
3037
2947
  };
3038
2948
  this.agent = this.runtimeSession.createAgent(selection, {
3039
2949
  onStreamChunk: (chunk) => {
3040
- // Stream output using clean streamContent() - chat box floats below
3041
- this.terminalInput.streamContent(chunk);
2950
+ // Stream output directly - no spinner during streaming to avoid race conditions
2951
+ display.stream(chunk);
3042
2952
  },
3043
2953
  onStreamFallback: (info) => this.handleStreamingFallback(info),
3044
2954
  onAssistantMessage: (content, metadata) => {
@@ -3060,16 +2970,18 @@ What's the next action?`;
3060
2970
  display.showAssistantMessage(finalContent, enriched);
3061
2971
  }
3062
2972
  }
3063
- // 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)")
3064
2974
  display.stopThinking();
3065
- // Update context usage for mode controls display
2975
+ // Calculate context usage
2976
+ let contextInfo;
3066
2977
  if (enriched.contextWindowTokens && metadata.usage) {
3067
2978
  const total = this.totalTokens(metadata.usage);
3068
2979
  if (total && total > 0) {
3069
2980
  const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
3070
- this.updateContextUsage(percentage);
2981
+ contextInfo = { percentage, tokens: total };
3071
2982
  }
3072
2983
  }
2984
+ display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
3073
2985
  // Auto-verify changes: build first (catches type errors), then tests
3074
2986
  void this.enforceAutoBuild('final-response');
3075
2987
  void this.enforceAutoTests('final-response');
@@ -3139,6 +3051,7 @@ What's the next action?`;
3139
3051
  this.stopStreamingHeartbeat();
3140
3052
  this.updateStatusMessage(null);
3141
3053
  this.terminalInput.setStreaming(false);
3054
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3142
3055
  this.terminalInput.render();
3143
3056
  },
3144
3057
  onVerificationNeeded: () => {
@@ -3175,6 +3088,7 @@ What's the next action?`;
3175
3088
  resetChatBoxAfterModelSwap() {
3176
3089
  this.updateStatusMessage(null);
3177
3090
  this.terminalInput.setStreaming(false);
3091
+ this.terminalInput.setContentEndRow(display.getTotalWrittenLines());
3178
3092
  this.terminalInput.render();
3179
3093
  this.ensureReadlineReady();
3180
3094
  }
@@ -3239,14 +3153,9 @@ What's the next action?`;
3239
3153
  return null;
3240
3154
  }
3241
3155
  const usageRatio = total / windowTokens;
3242
- this.latestTokenUsage = {
3243
- used: total,
3244
- limit: windowTokens,
3245
- };
3246
3156
  // Always update context usage in the UI
3247
3157
  const percentUsed = Math.round(usageRatio * 100);
3248
3158
  this.updateContextUsage(percentUsed);
3249
- this.refreshStatusLine(true);
3250
3159
  if (usageRatio < CONTEXT_USAGE_THRESHOLD) {
3251
3160
  return null;
3252
3161
  }
@@ -3513,22 +3422,6 @@ What's the next action?`;
3513
3422
  this.sessionState.reasoningEffort = preset.reasoningEffort;
3514
3423
  }
3515
3424
  }
3516
- /**
3517
- * Build the session banner.
3518
- */
3519
- buildBanner() {
3520
- const terminalWidth = output.columns ?? 100;
3521
- const width = Math.min(terminalWidth - 4, 110);
3522
- return renderSessionFrame({
3523
- profileLabel: this.profileLabel,
3524
- profileName: this.profile,
3525
- model: this.sessionState.model,
3526
- provider: this.sessionState.provider,
3527
- workspace: this.workingDir,
3528
- version: this.version,
3529
- width,
3530
- });
3531
- }
3532
3425
  refreshBannerSessionInfo() {
3533
3426
  const nextState = {
3534
3427
  model: this.sessionState.model,
@@ -3539,11 +3432,13 @@ What's the next action?`;
3539
3432
  return;
3540
3433
  }
3541
3434
  this.refreshContextGauge();
3542
- // Banner is no longer stored in display - it was streamed as content
3543
- // 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());
3544
3438
  if (!this.isProcessing) {
3545
3439
  this.setIdleStatus();
3546
3440
  }
3441
+ // Pinned header rows are disabled; scroll region handles all output.
3547
3442
  this.bannerSessionState = nextState;
3548
3443
  }
3549
3444
  providerLabel(id) {