erosolar-cli 1.0.2 → 1.0.5

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 (437) hide show
  1. package/README.md +80 -44
  2. package/agents/erosolar-code.rules.json +167 -0
  3. package/agents/general.rules.json +188 -0
  4. package/dist/adapters/browser/index.d.ts +12 -0
  5. package/dist/adapters/browser/index.d.ts.map +1 -0
  6. package/dist/adapters/browser/index.js +1 -0
  7. package/dist/adapters/browser/index.js.map +1 -0
  8. package/dist/adapters/node/index.d.ts +17 -0
  9. package/dist/adapters/node/index.d.ts.map +1 -0
  10. package/dist/adapters/node/index.js +1 -0
  11. package/dist/adapters/node/index.js.map +1 -0
  12. package/dist/adapters/remote/index.d.ts +13 -0
  13. package/dist/adapters/remote/index.d.ts.map +1 -0
  14. package/dist/adapters/remote/index.js +1 -0
  15. package/dist/adapters/remote/index.js.map +1 -0
  16. package/dist/adapters/types.d.ts +14 -0
  17. package/dist/adapters/types.d.ts.map +1 -0
  18. package/dist/adapters/types.js +1 -0
  19. package/dist/adapters/types.js.map +1 -0
  20. package/dist/bin/erosolar.d.ts +3 -0
  21. package/dist/bin/erosolar.d.ts.map +1 -0
  22. package/dist/bin/erosolar.js +1 -0
  23. package/dist/bin/erosolar.js.map +1 -0
  24. package/dist/capabilities/bashCapability.d.ts +13 -0
  25. package/dist/capabilities/bashCapability.d.ts.map +1 -0
  26. package/dist/capabilities/bashCapability.js +1 -0
  27. package/dist/capabilities/bashCapability.js.map +1 -0
  28. package/dist/capabilities/codeAnalysisCapability.d.ts +13 -0
  29. package/dist/capabilities/codeAnalysisCapability.d.ts.map +1 -0
  30. package/dist/capabilities/codeAnalysisCapability.js +24 -0
  31. package/dist/capabilities/codeAnalysisCapability.js.map +1 -0
  32. package/dist/capabilities/codeGenerationCapability.d.ts +13 -0
  33. package/dist/capabilities/codeGenerationCapability.d.ts.map +1 -0
  34. package/dist/capabilities/codeGenerationCapability.js +25 -0
  35. package/dist/capabilities/codeGenerationCapability.js.map +1 -0
  36. package/dist/capabilities/codeQualityCapability.d.ts +13 -0
  37. package/dist/capabilities/codeQualityCapability.d.ts.map +1 -0
  38. package/dist/capabilities/codeQualityCapability.js +25 -0
  39. package/dist/capabilities/codeQualityCapability.js.map +1 -0
  40. package/dist/capabilities/dependencySecurityCapability.d.ts +13 -0
  41. package/dist/capabilities/dependencySecurityCapability.d.ts.map +1 -0
  42. package/dist/capabilities/dependencySecurityCapability.js +24 -0
  43. package/dist/capabilities/dependencySecurityCapability.js.map +1 -0
  44. package/dist/capabilities/devCapability.d.ts +13 -0
  45. package/dist/capabilities/devCapability.d.ts.map +1 -0
  46. package/dist/capabilities/devCapability.js +24 -0
  47. package/dist/capabilities/devCapability.js.map +1 -0
  48. package/dist/capabilities/filesystemCapability.d.ts +13 -0
  49. package/dist/capabilities/filesystemCapability.d.ts.map +1 -0
  50. package/dist/capabilities/filesystemCapability.js +1 -0
  51. package/dist/capabilities/filesystemCapability.js.map +1 -0
  52. package/dist/capabilities/index.d.ts +10 -0
  53. package/dist/capabilities/index.d.ts.map +1 -0
  54. package/dist/capabilities/index.js +7 -0
  55. package/dist/capabilities/index.js.map +1 -0
  56. package/dist/capabilities/refactoringCapability.d.ts +13 -0
  57. package/dist/capabilities/refactoringCapability.d.ts.map +1 -0
  58. package/dist/capabilities/refactoringCapability.js +25 -0
  59. package/dist/capabilities/refactoringCapability.js.map +1 -0
  60. package/dist/capabilities/repoChecksCapability.d.ts +10 -0
  61. package/dist/capabilities/repoChecksCapability.d.ts.map +1 -0
  62. package/dist/capabilities/repoChecksCapability.js +24 -0
  63. package/dist/capabilities/repoChecksCapability.js.map +1 -0
  64. package/dist/capabilities/searchCapability.d.ts +13 -0
  65. package/dist/capabilities/searchCapability.d.ts.map +1 -0
  66. package/dist/capabilities/searchCapability.js +1 -0
  67. package/dist/capabilities/searchCapability.js.map +1 -0
  68. package/dist/capabilities/testingCapability.d.ts +13 -0
  69. package/dist/capabilities/testingCapability.d.ts.map +1 -0
  70. package/dist/capabilities/testingCapability.js +25 -0
  71. package/dist/capabilities/testingCapability.js.map +1 -0
  72. package/dist/capabilities/toolManifest.d.ts +3 -0
  73. package/dist/capabilities/toolManifest.d.ts.map +1 -0
  74. package/dist/capabilities/toolManifest.js +160 -0
  75. package/dist/capabilities/toolManifest.js.map +1 -0
  76. package/dist/capabilities/toolRegistry.d.ts +22 -0
  77. package/dist/capabilities/toolRegistry.d.ts.map +1 -0
  78. package/dist/capabilities/toolRegistry.js +49 -32
  79. package/dist/capabilities/toolRegistry.js.map +1 -0
  80. package/dist/config.d.ts +25 -0
  81. package/dist/config.d.ts.map +1 -0
  82. package/dist/config.js +103 -23
  83. package/dist/config.js.map +1 -0
  84. package/dist/contracts/agent-profiles.schema.json +43 -0
  85. package/dist/contracts/agent-schemas.json +293 -0
  86. package/dist/contracts/schemas/agent-profile.schema.json +157 -0
  87. package/dist/contracts/schemas/agent-rules.schema.json +238 -0
  88. package/dist/contracts/schemas/agent-schemas.schema.json +528 -0
  89. package/dist/contracts/schemas/agent.schema.json +89 -0
  90. package/dist/contracts/schemas/tool-selection.schema.json +174 -0
  91. package/dist/contracts/tools.schema.json +100 -0
  92. package/dist/contracts/v1/agent.d.ts +147 -0
  93. package/dist/contracts/v1/agent.d.ts.map +1 -0
  94. package/dist/contracts/v1/agent.js +1 -0
  95. package/dist/contracts/v1/agent.js.map +1 -0
  96. package/dist/contracts/v1/agentProfileManifest.d.ts +60 -0
  97. package/dist/contracts/v1/agentProfileManifest.d.ts.map +1 -0
  98. package/dist/contracts/v1/agentProfileManifest.js +9 -0
  99. package/dist/contracts/v1/agentProfileManifest.js.map +1 -0
  100. package/dist/contracts/v1/agentRules.d.ts +60 -0
  101. package/dist/contracts/v1/agentRules.d.ts.map +1 -0
  102. package/dist/contracts/v1/agentRules.js +10 -0
  103. package/dist/contracts/v1/agentRules.js.map +1 -0
  104. package/dist/contracts/v1/provider.d.ts +149 -0
  105. package/dist/contracts/v1/provider.d.ts.map +1 -0
  106. package/dist/contracts/v1/provider.js +1 -0
  107. package/dist/contracts/v1/provider.js.map +1 -0
  108. package/dist/contracts/v1/tool.d.ts +136 -0
  109. package/dist/contracts/v1/tool.d.ts.map +1 -0
  110. package/dist/contracts/v1/tool.js +1 -0
  111. package/dist/contracts/v1/tool.js.map +1 -0
  112. package/dist/contracts/v1/toolAccess.d.ts +43 -0
  113. package/dist/contracts/v1/toolAccess.d.ts.map +1 -0
  114. package/dist/contracts/v1/toolAccess.js +9 -0
  115. package/dist/contracts/v1/toolAccess.js.map +1 -0
  116. package/dist/core/agent.d.ts +35 -0
  117. package/dist/core/agent.d.ts.map +1 -0
  118. package/dist/core/agent.js +17 -9
  119. package/dist/core/agent.js.map +1 -0
  120. package/dist/core/agentProfileManifest.d.ts +3 -0
  121. package/dist/core/agentProfileManifest.d.ts.map +1 -0
  122. package/dist/core/agentProfileManifest.js +188 -0
  123. package/dist/core/agentProfileManifest.js.map +1 -0
  124. package/dist/core/agentProfiles.d.ts +22 -0
  125. package/dist/core/agentProfiles.d.ts.map +1 -0
  126. package/dist/core/agentProfiles.js +1 -0
  127. package/dist/core/agentProfiles.js.map +1 -0
  128. package/dist/core/agentRulebook.d.ts +11 -0
  129. package/dist/core/agentRulebook.d.ts.map +1 -0
  130. package/dist/core/agentRulebook.js +136 -0
  131. package/dist/core/agentRulebook.js.map +1 -0
  132. package/dist/core/agentSchemaLoader.d.ts +131 -0
  133. package/dist/core/agentSchemaLoader.d.ts.map +1 -0
  134. package/dist/core/agentSchemaLoader.js +221 -0
  135. package/dist/core/agentSchemaLoader.js.map +1 -0
  136. package/dist/core/contextWindow.d.ts +6 -0
  137. package/dist/core/contextWindow.d.ts.map +1 -0
  138. package/dist/core/contextWindow.js +1 -0
  139. package/dist/core/contextWindow.js.map +1 -0
  140. package/dist/core/errors/apiKeyErrors.d.ts +11 -0
  141. package/dist/core/errors/apiKeyErrors.d.ts.map +1 -0
  142. package/dist/core/errors/apiKeyErrors.js +1 -0
  143. package/dist/core/errors/apiKeyErrors.js.map +1 -0
  144. package/dist/core/errors.d.ts +4 -0
  145. package/dist/core/errors.d.ts.map +1 -0
  146. package/dist/core/errors.js +33 -0
  147. package/dist/core/errors.js.map +1 -0
  148. package/dist/core/preferences.d.ts +21 -0
  149. package/dist/core/preferences.d.ts.map +1 -0
  150. package/dist/core/preferences.js +13 -7
  151. package/dist/core/preferences.js.map +1 -0
  152. package/dist/core/schemaValidator.d.ts +6 -0
  153. package/dist/core/schemaValidator.d.ts.map +1 -0
  154. package/dist/core/schemaValidator.js +93 -0
  155. package/dist/core/schemaValidator.js.map +1 -0
  156. package/dist/core/secretStore.d.ts +20 -0
  157. package/dist/core/secretStore.d.ts.map +1 -0
  158. package/dist/core/secretStore.js +3 -8
  159. package/dist/core/secretStore.js.map +1 -0
  160. package/dist/core/toolRuntime.d.ts +42 -0
  161. package/dist/core/toolRuntime.d.ts.map +1 -0
  162. package/dist/core/toolRuntime.js +25 -10
  163. package/dist/core/toolRuntime.js.map +1 -0
  164. package/dist/core/types.d.ts +86 -0
  165. package/dist/core/types.d.ts.map +1 -0
  166. package/dist/core/types.js +1 -0
  167. package/dist/core/types.js.map +1 -0
  168. package/dist/plugins/providers/anthropic/index.d.ts +2 -0
  169. package/dist/plugins/providers/anthropic/index.d.ts.map +1 -0
  170. package/dist/plugins/providers/anthropic/index.js +6 -4
  171. package/dist/plugins/providers/anthropic/index.js.map +1 -0
  172. package/dist/plugins/providers/deepseek/index.d.ts +2 -0
  173. package/dist/plugins/providers/deepseek/index.d.ts.map +1 -0
  174. package/dist/plugins/providers/deepseek/index.js +1 -0
  175. package/dist/plugins/providers/deepseek/index.js.map +1 -0
  176. package/dist/plugins/providers/google/index.d.ts +2 -0
  177. package/dist/plugins/providers/google/index.d.ts.map +1 -0
  178. package/dist/plugins/providers/google/index.js +6 -4
  179. package/dist/plugins/providers/google/index.js.map +1 -0
  180. package/dist/plugins/providers/index.d.ts +2 -0
  181. package/dist/plugins/providers/index.d.ts.map +1 -0
  182. package/dist/plugins/providers/index.js +1 -0
  183. package/dist/plugins/providers/index.js.map +1 -0
  184. package/dist/plugins/providers/openai/index.d.ts +3 -0
  185. package/dist/plugins/providers/openai/index.d.ts.map +1 -0
  186. package/dist/plugins/providers/openai/index.js +6 -4
  187. package/dist/plugins/providers/openai/index.js.map +1 -0
  188. package/dist/plugins/providers/xai/index.d.ts +2 -0
  189. package/dist/plugins/providers/xai/index.d.ts.map +1 -0
  190. package/dist/plugins/providers/xai/index.js +1 -0
  191. package/dist/plugins/providers/xai/index.js.map +1 -0
  192. package/dist/plugins/tools/bash/localBashPlugin.d.ts +3 -0
  193. package/dist/plugins/tools/bash/localBashPlugin.d.ts.map +1 -0
  194. package/dist/plugins/tools/bash/localBashPlugin.js +1 -0
  195. package/dist/plugins/tools/bash/localBashPlugin.js.map +1 -0
  196. package/dist/plugins/tools/checks/localRepoChecksPlugin.d.ts +3 -0
  197. package/dist/plugins/tools/checks/localRepoChecksPlugin.d.ts.map +1 -0
  198. package/dist/plugins/tools/checks/localRepoChecksPlugin.js +14 -0
  199. package/dist/plugins/tools/checks/localRepoChecksPlugin.js.map +1 -0
  200. package/dist/plugins/tools/codeAnalysis/codeAnalysisPlugin.d.ts +3 -0
  201. package/dist/plugins/tools/codeAnalysis/codeAnalysisPlugin.d.ts.map +1 -0
  202. package/dist/plugins/tools/codeAnalysis/codeAnalysisPlugin.js +14 -0
  203. package/dist/plugins/tools/codeAnalysis/codeAnalysisPlugin.js.map +1 -0
  204. package/dist/plugins/tools/codeQuality/codeQualityPlugin.d.ts +3 -0
  205. package/dist/plugins/tools/codeQuality/codeQualityPlugin.d.ts.map +1 -0
  206. package/dist/plugins/tools/codeQuality/codeQualityPlugin.js +14 -0
  207. package/dist/plugins/tools/codeQuality/codeQualityPlugin.js.map +1 -0
  208. package/dist/plugins/tools/dependency/dependencyPlugin.d.ts +3 -0
  209. package/dist/plugins/tools/dependency/dependencyPlugin.d.ts.map +1 -0
  210. package/dist/plugins/tools/dependency/dependencyPlugin.js +12 -0
  211. package/dist/plugins/tools/dependency/dependencyPlugin.js.map +1 -0
  212. package/dist/plugins/tools/development/devPlugin.d.ts +3 -0
  213. package/dist/plugins/tools/development/devPlugin.d.ts.map +1 -0
  214. package/dist/plugins/tools/development/devPlugin.js +14 -0
  215. package/dist/plugins/tools/development/devPlugin.js.map +1 -0
  216. package/dist/plugins/tools/filesystem/localFilesystemPlugin.d.ts +3 -0
  217. package/dist/plugins/tools/filesystem/localFilesystemPlugin.d.ts.map +1 -0
  218. package/dist/plugins/tools/filesystem/localFilesystemPlugin.js +1 -0
  219. package/dist/plugins/tools/filesystem/localFilesystemPlugin.js.map +1 -0
  220. package/dist/plugins/tools/index.d.ts +3 -0
  221. package/dist/plugins/tools/index.d.ts.map +1 -0
  222. package/dist/plugins/tools/index.js +1 -0
  223. package/dist/plugins/tools/index.js.map +1 -0
  224. package/dist/plugins/tools/nodeDefaults.d.ts +2 -0
  225. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -0
  226. package/dist/plugins/tools/nodeDefaults.js +15 -2
  227. package/dist/plugins/tools/nodeDefaults.js.map +1 -0
  228. package/dist/plugins/tools/refactoring/refactoringPlugin.d.ts +3 -0
  229. package/dist/plugins/tools/refactoring/refactoringPlugin.d.ts.map +1 -0
  230. package/dist/plugins/tools/refactoring/refactoringPlugin.js +12 -0
  231. package/dist/plugins/tools/refactoring/refactoringPlugin.js.map +1 -0
  232. package/dist/plugins/tools/registry.d.ts +22 -0
  233. package/dist/plugins/tools/registry.d.ts.map +1 -0
  234. package/dist/plugins/tools/registry.js +1 -0
  235. package/dist/plugins/tools/registry.js.map +1 -0
  236. package/dist/plugins/tools/search/localSearchPlugin.d.ts +3 -0
  237. package/dist/plugins/tools/search/localSearchPlugin.d.ts.map +1 -0
  238. package/dist/plugins/tools/search/localSearchPlugin.js +1 -0
  239. package/dist/plugins/tools/search/localSearchPlugin.js.map +1 -0
  240. package/dist/plugins/tools/testing/testingPlugin.d.ts +3 -0
  241. package/dist/plugins/tools/testing/testingPlugin.d.ts.map +1 -0
  242. package/dist/plugins/tools/testing/testingPlugin.js +12 -0
  243. package/dist/plugins/tools/testing/testingPlugin.js.map +1 -0
  244. package/dist/providers/anthropicProvider.d.ts +23 -0
  245. package/dist/providers/anthropicProvider.d.ts.map +1 -0
  246. package/dist/providers/anthropicProvider.js +3 -2
  247. package/dist/providers/anthropicProvider.js.map +1 -0
  248. package/dist/providers/googleProvider.d.ts +19 -0
  249. package/dist/providers/googleProvider.d.ts.map +1 -0
  250. package/dist/providers/googleProvider.js +1 -1
  251. package/dist/providers/googleProvider.js.map +1 -0
  252. package/dist/providers/openaiChatCompletionsProvider.d.ts +16 -0
  253. package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -0
  254. package/dist/providers/openaiChatCompletionsProvider.js +1 -0
  255. package/dist/providers/openaiChatCompletionsProvider.js.map +1 -0
  256. package/dist/providers/openaiResponsesProvider.d.ts +20 -0
  257. package/dist/providers/openaiResponsesProvider.d.ts.map +1 -0
  258. package/dist/providers/openaiResponsesProvider.js +1 -0
  259. package/dist/providers/openaiResponsesProvider.js.map +1 -0
  260. package/dist/providers/providerFactory.d.ts +18 -0
  261. package/dist/providers/providerFactory.d.ts.map +1 -0
  262. package/dist/providers/providerFactory.js +5 -1
  263. package/dist/providers/providerFactory.js.map +1 -0
  264. package/dist/runtime/agentHost.d.ts +61 -0
  265. package/dist/runtime/agentHost.d.ts.map +1 -0
  266. package/dist/runtime/agentHost.js +5 -1
  267. package/dist/runtime/agentHost.js.map +1 -0
  268. package/dist/runtime/agentSession.d.ts +32 -0
  269. package/dist/runtime/agentSession.d.ts.map +1 -0
  270. package/dist/runtime/agentSession.js +17 -1
  271. package/dist/runtime/agentSession.js.map +1 -0
  272. package/dist/runtime/browser.d.ts +7 -0
  273. package/dist/runtime/browser.d.ts.map +1 -0
  274. package/dist/runtime/browser.js +1 -0
  275. package/dist/runtime/browser.js.map +1 -0
  276. package/dist/runtime/cloud.d.ts +7 -0
  277. package/dist/runtime/cloud.d.ts.map +1 -0
  278. package/dist/runtime/cloud.js +1 -0
  279. package/dist/runtime/cloud.js.map +1 -0
  280. package/dist/runtime/node.d.ts +8 -0
  281. package/dist/runtime/node.d.ts.map +1 -0
  282. package/dist/runtime/node.js +1 -0
  283. package/dist/runtime/node.js.map +1 -0
  284. package/dist/runtime/universal.d.ts +21 -0
  285. package/dist/runtime/universal.d.ts.map +1 -0
  286. package/dist/runtime/universal.js +1 -0
  287. package/dist/runtime/universal.js.map +1 -0
  288. package/dist/shell/bracketedPasteManager.d.ts +23 -0
  289. package/dist/shell/bracketedPasteManager.d.ts.map +1 -0
  290. package/dist/shell/bracketedPasteManager.js +1 -0
  291. package/dist/shell/bracketedPasteManager.js.map +1 -0
  292. package/dist/shell/interactiveShell.d.ts +131 -0
  293. package/dist/shell/interactiveShell.d.ts.map +1 -0
  294. package/dist/shell/interactiveShell.js +325 -262
  295. package/dist/shell/interactiveShell.js.map +1 -0
  296. package/dist/shell/liveStatus.d.ts +29 -0
  297. package/dist/shell/liveStatus.d.ts.map +1 -0
  298. package/dist/shell/liveStatus.js +77 -0
  299. package/dist/shell/liveStatus.js.map +1 -0
  300. package/dist/shell/promptSkin.d.ts +43 -0
  301. package/dist/shell/promptSkin.d.ts.map +1 -0
  302. package/dist/shell/promptSkin.js +330 -0
  303. package/dist/shell/promptSkin.js.map +1 -0
  304. package/dist/shell/shellApp.d.ts +10 -0
  305. package/dist/shell/shellApp.d.ts.map +1 -0
  306. package/dist/shell/shellApp.js +149 -78
  307. package/dist/shell/shellApp.js.map +1 -0
  308. package/dist/shell/systemPrompt.d.ts +3 -0
  309. package/dist/shell/systemPrompt.d.ts.map +1 -0
  310. package/dist/shell/systemPrompt.js +64 -0
  311. package/dist/shell/systemPrompt.js.map +1 -0
  312. package/dist/shell/updateManager.d.ts +2 -0
  313. package/dist/shell/updateManager.d.ts.map +1 -0
  314. package/dist/shell/updateManager.js +109 -0
  315. package/dist/shell/updateManager.js.map +1 -0
  316. package/dist/tools/bashTools.d.ts +8 -0
  317. package/dist/tools/bashTools.d.ts.map +1 -0
  318. package/dist/tools/bashTools.js +18 -15
  319. package/dist/tools/bashTools.js.map +1 -0
  320. package/dist/tools/codeAnalysisTools.d.ts +74 -0
  321. package/dist/tools/codeAnalysisTools.d.ts.map +1 -0
  322. package/dist/tools/codeAnalysisTools.js +642 -0
  323. package/dist/tools/codeAnalysisTools.js.map +1 -0
  324. package/dist/tools/codeGenerationTools.d.ts +3 -0
  325. package/dist/tools/codeGenerationTools.d.ts.map +1 -0
  326. package/dist/tools/codeGenerationTools.js +369 -0
  327. package/dist/tools/codeGenerationTools.js.map +1 -0
  328. package/dist/tools/codeQualityTools.d.ts +3 -0
  329. package/dist/tools/codeQualityTools.d.ts.map +1 -0
  330. package/dist/tools/codeQualityTools.js +295 -0
  331. package/dist/tools/codeQualityTools.js.map +1 -0
  332. package/dist/tools/dependencyTools.d.ts +3 -0
  333. package/dist/tools/dependencyTools.d.ts.map +1 -0
  334. package/dist/tools/dependencyTools.js +283 -0
  335. package/dist/tools/dependencyTools.js.map +1 -0
  336. package/dist/tools/devTools.d.ts +10 -0
  337. package/dist/tools/devTools.d.ts.map +1 -0
  338. package/dist/tools/devTools.js +239 -0
  339. package/dist/tools/devTools.js.map +1 -0
  340. package/dist/tools/diffUtils.d.ts +8 -0
  341. package/dist/tools/diffUtils.d.ts.map +1 -0
  342. package/dist/tools/diffUtils.js +2 -1
  343. package/dist/tools/diffUtils.js.map +1 -0
  344. package/dist/tools/fileTools.d.ts +3 -0
  345. package/dist/tools/fileTools.d.ts.map +1 -0
  346. package/dist/tools/fileTools.js +72 -21
  347. package/dist/tools/fileTools.js.map +1 -0
  348. package/dist/tools/refactoringTools.d.ts +3 -0
  349. package/dist/tools/refactoringTools.d.ts.map +1 -0
  350. package/dist/tools/refactoringTools.js +294 -0
  351. package/dist/tools/refactoringTools.js.map +1 -0
  352. package/dist/tools/repoChecksTools.d.ts +3 -0
  353. package/dist/tools/repoChecksTools.d.ts.map +1 -0
  354. package/dist/tools/repoChecksTools.js +161 -0
  355. package/dist/tools/repoChecksTools.js.map +1 -0
  356. package/dist/tools/searchTools.d.ts +3 -0
  357. package/dist/tools/searchTools.d.ts.map +1 -0
  358. package/dist/tools/searchTools.js +50 -22
  359. package/dist/tools/searchTools.js.map +1 -0
  360. package/dist/tools/testingTools.d.ts +3 -0
  361. package/dist/tools/testingTools.d.ts.map +1 -0
  362. package/dist/tools/testingTools.js +233 -0
  363. package/dist/tools/testingTools.js.map +1 -0
  364. package/dist/ui/ShellUIAdapter.d.ts +96 -0
  365. package/dist/ui/ShellUIAdapter.d.ts.map +1 -0
  366. package/dist/ui/ShellUIAdapter.js +442 -0
  367. package/dist/ui/ShellUIAdapter.js.map +1 -0
  368. package/dist/ui/UnifiedUIController.d.ts +196 -0
  369. package/dist/ui/UnifiedUIController.d.ts.map +1 -0
  370. package/dist/ui/UnifiedUIController.js +640 -0
  371. package/dist/ui/UnifiedUIController.js.map +1 -0
  372. package/dist/ui/animation/AnimationScheduler.d.ts +192 -0
  373. package/dist/ui/animation/AnimationScheduler.d.ts.map +1 -0
  374. package/dist/ui/animation/AnimationScheduler.js +432 -0
  375. package/dist/ui/animation/AnimationScheduler.js.map +1 -0
  376. package/dist/ui/codeHighlighter.d.ts +6 -0
  377. package/dist/ui/codeHighlighter.d.ts.map +1 -0
  378. package/dist/ui/codeHighlighter.js +41 -29
  379. package/dist/ui/codeHighlighter.js.map +1 -0
  380. package/dist/ui/designSystem.d.ts +26 -0
  381. package/dist/ui/designSystem.d.ts.map +1 -0
  382. package/dist/ui/designSystem.js +17 -1
  383. package/dist/ui/designSystem.js.map +1 -0
  384. package/dist/ui/display.d.ts +160 -0
  385. package/dist/ui/display.d.ts.map +1 -0
  386. package/dist/ui/display.js +492 -231
  387. package/dist/ui/display.js.map +1 -0
  388. package/dist/ui/interrupts/InterruptManager.d.ts +142 -0
  389. package/dist/ui/interrupts/InterruptManager.d.ts.map +1 -0
  390. package/dist/ui/interrupts/InterruptManager.js +439 -0
  391. package/dist/ui/interrupts/InterruptManager.js.map +1 -0
  392. package/dist/ui/layout.d.ts +17 -0
  393. package/dist/ui/layout.d.ts.map +1 -0
  394. package/dist/ui/layout.js +12 -7
  395. package/dist/ui/layout.js.map +1 -0
  396. package/dist/ui/orchestration/StatusOrchestrator.d.ts +156 -0
  397. package/dist/ui/orchestration/StatusOrchestrator.d.ts.map +1 -0
  398. package/dist/ui/orchestration/StatusOrchestrator.js +405 -0
  399. package/dist/ui/orchestration/StatusOrchestrator.js.map +1 -0
  400. package/dist/ui/overlay/OverlayManager.d.ts +105 -0
  401. package/dist/ui/overlay/OverlayManager.d.ts.map +1 -0
  402. package/dist/ui/overlay/OverlayManager.js +291 -0
  403. package/dist/ui/overlay/OverlayManager.js.map +1 -0
  404. package/dist/ui/richText.d.ts +6 -0
  405. package/dist/ui/richText.d.ts.map +1 -0
  406. package/dist/ui/richText.js +13 -4
  407. package/dist/ui/richText.js.map +1 -0
  408. package/dist/ui/telemetry/UITelemetry.d.ts +181 -0
  409. package/dist/ui/telemetry/UITelemetry.d.ts.map +1 -0
  410. package/dist/ui/telemetry/UITelemetry.js +446 -0
  411. package/dist/ui/telemetry/UITelemetry.js.map +1 -0
  412. package/dist/ui/theme.d.ts +77 -0
  413. package/dist/ui/theme.d.ts.map +1 -0
  414. package/dist/ui/theme.js +19 -6
  415. package/dist/ui/theme.js.map +1 -0
  416. package/dist/utils/errorUtils.d.ts +16 -0
  417. package/dist/utils/errorUtils.d.ts.map +1 -0
  418. package/dist/utils/errorUtils.js +66 -0
  419. package/dist/utils/errorUtils.js.map +1 -0
  420. package/dist/utils/nodeVersion.d.ts +3 -0
  421. package/dist/utils/nodeVersion.d.ts.map +1 -0
  422. package/dist/utils/nodeVersion.js +51 -0
  423. package/dist/utils/nodeVersion.js.map +1 -0
  424. package/dist/workspace.d.ts +8 -0
  425. package/dist/workspace.d.ts.map +1 -0
  426. package/dist/workspace.js +72 -11
  427. package/dist/workspace.js.map +1 -0
  428. package/package.json +35 -16
  429. package/scripts/health-check.mjs +176 -0
  430. package/ARCHITECTURE.json +0 -157
  431. package/Agents.md +0 -207
  432. package/SOURCE_OF_TRUTH.json +0 -103
  433. package/dist/capabilities/tavilyCapability.js +0 -26
  434. package/dist/plugins/tools/tavily/tavilyPlugin.js +0 -16
  435. package/dist/shell/__tests__/bracketedPasteManager.test.js +0 -35
  436. package/dist/tools/tavilyTools.js +0 -176
  437. package/dist/ui/__tests__/richText.test.js +0 -36
@@ -1,9 +1,13 @@
1
1
  import { createSpinner } from 'nanospinner';
2
2
  import { cursorTo, moveCursor } from 'node:readline';
3
3
  import { theme, icons } from './theme.js';
4
- import { formatRichContent, renderMessageBody } from './richText.js';
4
+ import { formatRichContent, renderMessagePanel } from './richText.js';
5
5
  import { getTerminalColumns, wrapPreformatted } from './layout.js';
6
- import { renderCallout, renderSectionHeading, renderStatusBar } from './designSystem.js';
6
+ import { renderCallout, renderSectionHeading } from './designSystem.js';
7
+ /**
8
+ * Tracks line output to stdout for banner rewriting and cursor positioning.
9
+ * Singleton pattern ensures consistent tracking across the application.
10
+ */
7
11
  class StdoutLineTracker {
8
12
  static instance = null;
9
13
  static getInstance() {
@@ -24,13 +28,18 @@ class StdoutLineTracker {
24
28
  get totalLines() {
25
29
  return this.linesWritten;
26
30
  }
31
+ /**
32
+ * Temporarily suspends line tracking while executing a function.
33
+ * Useful for rewriting content without incrementing line count.
34
+ */
27
35
  withSuspended(fn) {
36
+ const wasSuspended = this.suspended;
28
37
  this.suspended = true;
29
38
  try {
30
39
  return fn();
31
40
  }
32
41
  finally {
33
- this.suspended = false;
42
+ this.suspended = wasSuspended;
34
43
  }
35
44
  }
36
45
  reset() {
@@ -39,86 +48,192 @@ class StdoutLineTracker {
39
48
  patchStream() {
40
49
  const tracker = this;
41
50
  this.stream.write = function patched(chunk, encoding, callback) {
42
- tracker.recordChunk(chunk, encoding);
43
- return tracker.originalWrite(chunk, encoding, callback);
51
+ const actualEncoding = typeof encoding === 'function' ? undefined : encoding;
52
+ tracker.recordChunk(chunk, actualEncoding);
53
+ return tracker.originalWrite.call(this, chunk, encoding, callback);
44
54
  };
45
55
  }
46
56
  recordChunk(chunk, encoding) {
47
57
  if (this.suspended) {
48
58
  return;
49
59
  }
50
- const text = this.toString(chunk, encoding);
60
+ const text = this.chunkToString(chunk, encoding);
51
61
  if (!text) {
52
62
  return;
53
63
  }
54
- for (let index = 0; index < text.length; index += 1) {
55
- if (text[index] === '\n') {
64
+ this.countNewlines(text);
65
+ }
66
+ countNewlines(text) {
67
+ for (const char of text) {
68
+ if (char === '\n') {
56
69
  this.linesWritten += 1;
57
70
  }
58
71
  }
59
72
  }
60
- toString(chunk, encoding) {
73
+ chunkToString(chunk, encoding) {
61
74
  if (typeof chunk === 'string') {
62
75
  return chunk;
63
76
  }
64
77
  if (chunk instanceof Uint8Array) {
65
- const enc = typeof encoding === 'string' ? encoding : 'utf8';
78
+ const enc = encoding ?? 'utf8';
66
79
  return Buffer.from(chunk).toString(enc);
67
80
  }
68
81
  return null;
69
82
  }
70
83
  }
84
+ // Display configuration constants
85
+ const DISPLAY_CONSTANTS = {
86
+ MIN_BANNER_WIDTH: 32,
87
+ MAX_BANNER_WIDTH: 120,
88
+ BANNER_PADDING: 4,
89
+ MIN_MESSAGE_WIDTH: 42,
90
+ MAX_MESSAGE_WIDTH: 110,
91
+ MESSAGE_PADDING: 4,
92
+ MIN_ACTION_WIDTH: 40,
93
+ MAX_ACTION_WIDTH: 90,
94
+ MIN_THOUGHT_WIDTH: 48,
95
+ MAX_THOUGHT_WIDTH: 96,
96
+ MIN_CONTENT_WIDTH: 10,
97
+ MIN_WRAP_WIDTH: 12,
98
+ SPINNER_INTERVAL: 80,
99
+ };
100
+ const SPINNER_FRAMES = ['∴', 'ε', '∴', '✻', 'ε', '✻'];
101
+ /**
102
+ * Display class manages all terminal UI output for the application.
103
+ *
104
+ * Architecture:
105
+ * - Singleton pattern via StdoutLineTracker for consistent line tracking
106
+ * - Output interceptor pattern for live update integration
107
+ * - Banner state management for in-place updates
108
+ * - Configurable width constraints via DISPLAY_CONSTANTS
109
+ *
110
+ * Key responsibilities:
111
+ * - Welcome banners and session information display
112
+ * - Message formatting (assistant, system, errors, warnings)
113
+ * - Spinner/thinking indicators
114
+ * - Action and sub-action formatting with tree-style prefixes
115
+ * - Text wrapping and layout management
116
+ *
117
+ * Error handling:
118
+ * - Graceful degradation for non-TTY environments
119
+ * - Input validation on public methods
120
+ * - Safe cursor manipulation with fallback
121
+ */
71
122
  export class Display {
72
123
  stdoutTracker = StdoutLineTracker.getInstance();
73
124
  activeSpinner = null;
125
+ outputInterceptors = new Set();
126
+ registerOutputInterceptor(interceptor) {
127
+ if (!interceptor) {
128
+ return () => { };
129
+ }
130
+ this.outputInterceptors.add(interceptor);
131
+ return () => {
132
+ this.outputInterceptors.delete(interceptor);
133
+ };
134
+ }
135
+ withOutput(fn) {
136
+ this.notifyBeforeOutput();
137
+ try {
138
+ return fn();
139
+ }
140
+ finally {
141
+ this.notifyAfterOutput();
142
+ }
143
+ }
144
+ notifyBeforeOutput() {
145
+ for (const interceptor of this.outputInterceptors) {
146
+ interceptor.beforeWrite?.();
147
+ }
148
+ }
149
+ notifyAfterOutput() {
150
+ const interceptors = Array.from(this.outputInterceptors);
151
+ for (let index = interceptors.length - 1; index >= 0; index -= 1) {
152
+ interceptors[index]?.afterWrite?.();
153
+ }
154
+ }
74
155
  bannerState = null;
75
- showWelcome(profileLabel, profileName, model, provider, workingDir, version, thoughtLevel) {
156
+ /**
157
+ * Displays the welcome banner with session information.
158
+ * Stores banner state for potential in-place updates.
159
+ */
160
+ showWelcome(profileLabel, profileName, model, provider, workingDir, version) {
161
+ // Validate required inputs
162
+ if (!model?.trim() || !provider?.trim() || !workingDir?.trim()) {
163
+ return;
164
+ }
76
165
  const width = this.getBannerWidth();
77
- const lines = this.buildSessionLines(profileLabel, profileName, model, provider, workingDir, width, thoughtLevel);
78
- const bannerOptions = version ? { badge: version } : undefined;
79
- const banner = this.buildBanner('Erosolar CLI', width, lines, bannerOptions);
166
+ const banner = this.buildClaudeStyleBanner(profileLabel ?? '', model, provider, workingDir, width);
167
+ if (!banner) {
168
+ return;
169
+ }
80
170
  const startLine = this.stdoutTracker.totalLines;
81
- console.log(banner);
82
- this.bannerState = {
171
+ this.withOutput(() => {
172
+ console.log(banner);
173
+ });
174
+ const nextState = {
83
175
  startLine,
84
176
  height: this.measureBannerHeight(banner),
85
177
  width,
86
178
  workingDir,
87
- version,
88
179
  model,
89
180
  provider,
90
- profileLabel,
91
- profileName,
92
- thoughtLevel,
181
+ profileLabel: profileLabel ?? '',
182
+ profileName: profileName ?? '',
93
183
  };
184
+ if (version?.trim()) {
185
+ nextState.version = version.trim();
186
+ }
187
+ this.bannerState = nextState;
94
188
  }
95
- updateSessionInfo(model, provider, thoughtLevel) {
189
+ /**
190
+ * Updates the session information banner with new model/provider.
191
+ * Attempts in-place update if possible, otherwise re-renders.
192
+ */
193
+ updateSessionInfo(model, provider) {
96
194
  const state = this.bannerState;
97
195
  if (!state) {
98
196
  return;
99
197
  }
100
- const nextThoughtLevel = typeof thoughtLevel === 'undefined' ? state.thoughtLevel : thoughtLevel;
101
- const lines = this.buildSessionLines(state.profileLabel, state.profileName, model, provider, state.workingDir, state.width, nextThoughtLevel);
102
- const bannerOptions = state.version ? { badge: state.version } : undefined;
103
- const banner = this.buildBanner('Erosolar CLI', state.width, lines, bannerOptions);
198
+ // Validate inputs
199
+ if (!model?.trim() || !provider?.trim()) {
200
+ return;
201
+ }
202
+ const lines = this.buildSessionLines(state.profileLabel, state.profileName, model, provider, state.workingDir, state.width);
203
+ const banner = this.buildBanner('Erosolar CLI', state.width, lines, this.buildBannerOptions(state.version));
104
204
  const height = this.measureBannerHeight(banner);
205
+ // If height changed or rewrite failed, do full re-render
105
206
  if (height !== state.height || !this.tryRewriteBanner(state, banner)) {
106
- this.renderAndStoreBanner(state, model, provider, nextThoughtLevel);
207
+ this.renderAndStoreBanner(state, model, provider);
107
208
  return;
108
209
  }
210
+ // Update succeeded, update state
109
211
  state.model = model;
110
212
  state.provider = provider;
111
- state.thoughtLevel = nextThoughtLevel;
112
213
  }
113
214
  showThinking(message = 'Thinking...') {
114
215
  if (this.activeSpinner) {
115
216
  this.activeSpinner.stop();
116
217
  }
117
- this.activeSpinner = createSpinner(message).start();
218
+ // Use Claude Code style spinner with epsilon: ∴, ε, and ✻
219
+ this.activeSpinner = createSpinner(message, {
220
+ spinner: {
221
+ interval: DISPLAY_CONSTANTS.SPINNER_INTERVAL,
222
+ frames: [...SPINNER_FRAMES],
223
+ },
224
+ }).start();
225
+ }
226
+ updateThinking(message) {
227
+ if (this.activeSpinner) {
228
+ this.activeSpinner.update({ text: message });
229
+ }
230
+ else {
231
+ this.showThinking(message);
232
+ }
118
233
  }
119
234
  stopThinking() {
120
235
  if (this.activeSpinner) {
121
- this.activeSpinner.success();
236
+ this.activeSpinner.clear();
122
237
  this.activeSpinner = null;
123
238
  }
124
239
  }
@@ -127,19 +242,23 @@ export class Display {
127
242
  return;
128
243
  }
129
244
  const isThought = metadata?.isFinal === false;
130
- const body = isThought ? this.buildThoughtStatus(content) : this.buildChatBox(content, metadata);
245
+ const body = isThought ? this.buildClaudeStyleThought(content) : this.buildChatBox(content, metadata);
131
246
  if (!body.trim()) {
132
247
  return;
133
248
  }
134
- console.log(body);
135
- console.log();
249
+ this.withOutput(() => {
250
+ console.log(body);
251
+ console.log();
252
+ });
136
253
  }
137
254
  showAction(text, status = 'info') {
138
255
  if (!text.trim()) {
139
256
  return;
140
257
  }
141
258
  const icon = this.formatActionIcon(status);
142
- console.log(this.wrapWithPrefix(text, `${icon} `));
259
+ this.withOutput(() => {
260
+ console.log(this.wrapWithPrefix(text, `${icon} `));
261
+ });
143
262
  }
144
263
  showSubAction(text, status = 'info') {
145
264
  if (!text.trim()) {
@@ -153,8 +272,10 @@ export class Display {
153
272
  if (!rendered.length) {
154
273
  return;
155
274
  }
156
- console.log(rendered.join('\n'));
157
- console.log();
275
+ this.withOutput(() => {
276
+ console.log(rendered.join('\n'));
277
+ console.log();
278
+ });
158
279
  }
159
280
  buildWrappedSubActionLines(text, status) {
160
281
  const lines = text.split('\n').map((line) => line.trimEnd());
@@ -166,7 +287,7 @@ export class Display {
166
287
  }
167
288
  const rendered = [];
168
289
  for (let index = 0; index < lines.length; index += 1) {
169
- const segment = lines[index];
290
+ const segment = lines[index] ?? '';
170
291
  const isLast = index === lines.length - 1;
171
292
  const { prefix, continuation } = this.buildSubActionPrefixes(status, isLast);
172
293
  rendered.push(this.wrapWithPrefix(segment, prefix, { continuationPrefix: continuation }));
@@ -178,9 +299,9 @@ export class Display {
178
299
  if (!normalized) {
179
300
  return [];
180
301
  }
181
- const width = Math.max(40, Math.min(getTerminalColumns(), 90));
302
+ const width = Math.max(DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, Math.min(getTerminalColumns(), DISPLAY_CONSTANTS.MAX_ACTION_WIDTH));
182
303
  const samplePrefix = this.buildSubActionPrefixes(status, true).prefix;
183
- const contentWidth = Math.max(16, width - this.visibleLength(samplePrefix));
304
+ const contentWidth = Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - this.visibleLength(samplePrefix));
184
305
  const blocks = formatRichContent(normalized, contentWidth);
185
306
  if (!blocks.length) {
186
307
  return [];
@@ -203,8 +324,10 @@ export class Display {
203
324
  }
204
325
  }
205
326
  showSystemMessage(content) {
206
- console.log(content.trim());
207
- console.log();
327
+ this.withOutput(() => {
328
+ console.log(content.trim());
329
+ console.log();
330
+ });
208
331
  }
209
332
  showError(message) {
210
333
  const callout = renderCallout(message, {
@@ -213,7 +336,9 @@ export class Display {
213
336
  title: 'Error',
214
337
  width: this.getBannerWidth(),
215
338
  });
216
- console.error(`\n${callout}\n`);
339
+ this.withOutput(() => {
340
+ console.error(`\n${callout}\n`);
341
+ });
217
342
  }
218
343
  showWarning(message) {
219
344
  const callout = renderCallout(message, {
@@ -222,7 +347,9 @@ export class Display {
222
347
  title: 'Warning',
223
348
  width: this.getBannerWidth(),
224
349
  });
225
- console.warn(`${callout}`);
350
+ this.withOutput(() => {
351
+ console.warn(`${callout}`);
352
+ });
226
353
  }
227
354
  showInfo(message) {
228
355
  const callout = renderCallout(message, {
@@ -231,80 +358,65 @@ export class Display {
231
358
  title: 'Info',
232
359
  width: this.getBannerWidth(),
233
360
  });
234
- console.log(callout);
361
+ this.withOutput(() => {
362
+ console.log(callout);
363
+ });
364
+ }
365
+ showAvailableTools(_tools) {
366
+ // Hidden by default to match Claude Code style
367
+ // Tools are available but not listed verbosely on startup
368
+ // Parameter prefixed with underscore to indicate intentionally unused
235
369
  }
236
370
  showPlanningStep(step, index, total) {
371
+ // Validate inputs
372
+ if (!step?.trim()) {
373
+ return;
374
+ }
375
+ if (index < 1 || total < 1 || index > total) {
376
+ return;
377
+ }
378
+ const width = Math.max(DISPLAY_CONSTANTS.MIN_THOUGHT_WIDTH, Math.min(getTerminalColumns(), DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
237
379
  const heading = renderSectionHeading(`Plan ${index}/${total}`, {
238
380
  subtitle: step,
239
381
  icon: icons.arrow,
240
382
  tone: 'info',
241
- width: Math.max(48, Math.min(getTerminalColumns(), 110)),
383
+ width,
384
+ });
385
+ this.withOutput(() => {
386
+ console.log(heading);
242
387
  });
243
- console.log(heading);
244
388
  }
245
389
  clear() {
246
- console.clear();
390
+ this.withOutput(() => {
391
+ console.clear();
392
+ });
247
393
  this.stdoutTracker.reset();
248
394
  if (this.bannerState) {
249
- this.renderAndStoreBanner(this.bannerState, this.bannerState.model, this.bannerState.provider, this.bannerState.thoughtLevel);
395
+ this.renderAndStoreBanner(this.bannerState, this.bannerState.model, this.bannerState.provider);
250
396
  }
251
397
  }
252
398
  newLine() {
253
- console.log();
399
+ this.withOutput(() => {
400
+ console.log();
401
+ });
254
402
  }
255
403
  getBannerWidth() {
256
404
  const availableColumns = getTerminalColumns();
257
- const effectiveWidth = Math.max(32, availableColumns - 4);
258
- return Math.min(Math.max(effectiveWidth, 32), 120);
405
+ const effectiveWidth = Math.max(DISPLAY_CONSTANTS.MIN_BANNER_WIDTH, availableColumns - DISPLAY_CONSTANTS.BANNER_PADDING);
406
+ return Math.min(Math.max(effectiveWidth, DISPLAY_CONSTANTS.MIN_BANNER_WIDTH), DISPLAY_CONSTANTS.MAX_BANNER_WIDTH);
259
407
  }
260
- buildSessionLines(profileLabel, profileName, model, provider, workingDir, width, thoughtLevel) {
261
- const accent = `${theme.accent('◆')} ${theme.bold('Interactive Session Ready')}`;
408
+ buildSessionLines(profileLabel, profileName, model, provider, workingDir, width) {
262
409
  const normalizedLabel = profileLabel ? profileLabel.trim() : '';
263
410
  const normalizedProfile = profileName ? profileName.trim() : '';
264
411
  const agentLabel = normalizedLabel || normalizedProfile || 'Active agent';
265
- const modelSegments = [this.formatModelLabel(model), provider];
266
- if (thoughtLevel) {
267
- modelSegments.push(`${thoughtLevel} thought level`);
268
- }
269
- const modelSummary = modelSegments.join(' • ');
412
+ const modelSummary = [this.formatModelLabel(model), provider].join(' • ');
270
413
  const lines = [
271
- accent,
272
- '',
273
- ...this.formatInfoBlock('Agent', agentLabel, width),
274
- ];
275
- if (normalizedProfile) {
276
- lines.push(...this.formatInfoBlock('Profile', normalizedProfile, width));
277
- }
278
- lines.push(...this.formatInfoBlock('Model', modelSummary, width), ...this.formatInfoBlock('Workspace', workingDir, width));
279
- const statusSegments = [
280
- { label: 'Agent', value: agentLabel, tone: 'accent', icon: icons.assistant },
414
+ ...this.formatInfoBlock('Agent', agentLabel, width, this.getInfoFieldStyle('agent')),
281
415
  ];
282
416
  if (normalizedProfile) {
283
- statusSegments.push({
284
- label: 'Profile',
285
- value: normalizedProfile,
286
- tone: 'neutral',
287
- icon: icons.info,
288
- });
289
- }
290
- statusSegments.push({
291
- label: 'Model',
292
- value: this.describeModelForStatus(model, thoughtLevel),
293
- tone: 'info',
294
- icon: icons.info,
295
- }, {
296
- label: 'Tool Budget',
297
- value: 'Unlimited',
298
- tone: 'success',
299
- icon: icons.tool,
300
- });
301
- const statusBar = renderStatusBar(statusSegments, { width });
302
- if (statusBar.trim().length) {
303
- lines.push('');
304
- for (const row of statusBar.split('\n')) {
305
- lines.push(this.padLine(row, width));
306
- }
417
+ lines.push(...this.formatInfoBlock('Profile', normalizedProfile, width, this.getInfoFieldStyle('profile')));
307
418
  }
419
+ lines.push(...this.formatInfoBlock('Model', modelSummary, width, this.getInfoFieldStyle('model')), ...this.formatInfoBlock('Workspace', workingDir, width, this.getInfoFieldStyle('workspace')));
308
420
  return lines;
309
421
  }
310
422
  measureBannerHeight(banner) {
@@ -314,53 +426,69 @@ export class Display {
314
426
  const lines = banner.split('\n').length;
315
427
  return lines;
316
428
  }
429
+ /**
430
+ * Attempts to rewrite the banner in place using terminal cursor manipulation.
431
+ * Returns true if successful, false if rewrite is not possible.
432
+ */
317
433
  tryRewriteBanner(state, banner) {
434
+ // Validate TTY availability
318
435
  if (!process.stdout.isTTY) {
319
436
  return false;
320
437
  }
438
+ // Validate banner state
439
+ if (!banner || state.height <= 0) {
440
+ return false;
441
+ }
321
442
  const linesWritten = this.stdoutTracker.totalLines;
322
443
  const linesAfterBanner = linesWritten - (state.startLine + state.height);
444
+ // Cannot rewrite if banner position is invalid
323
445
  if (linesAfterBanner < 0) {
324
446
  return false;
325
447
  }
326
448
  const totalOffset = linesAfterBanner + state.height;
327
449
  const maxRows = process.stdout.rows;
328
- if (typeof maxRows === 'number' && totalOffset > maxRows) {
450
+ // Cannot rewrite if offset exceeds terminal height
451
+ if (typeof maxRows === 'number' && maxRows > 0 && totalOffset > maxRows) {
329
452
  return false;
330
453
  }
331
454
  try {
332
- moveCursor(process.stdout, 0, -totalOffset);
333
- cursorTo(process.stdout, 0);
455
+ this.withOutput(() => {
456
+ // Move cursor up to banner start
457
+ moveCursor(process.stdout, 0, -totalOffset);
458
+ cursorTo(process.stdout, 0);
459
+ // Write new banner without tracking
460
+ this.stdoutTracker.withSuspended(() => {
461
+ process.stdout.write(`${banner}\n`);
462
+ });
463
+ // Restore cursor position
464
+ if (linesAfterBanner > 0) {
465
+ moveCursor(process.stdout, 0, linesAfterBanner);
466
+ }
467
+ cursorTo(process.stdout, 0);
468
+ });
469
+ return true;
334
470
  }
335
- catch {
471
+ catch (error) {
472
+ // Cursor manipulation failed (e.g., terminal doesn't support it)
473
+ if (error instanceof Error) {
474
+ // Could log error in debug mode if needed
475
+ }
336
476
  return false;
337
477
  }
338
- this.stdoutTracker.withSuspended(() => {
339
- process.stdout.write(`${banner}\n`);
340
- });
341
- if (linesAfterBanner > 0) {
342
- moveCursor(process.stdout, 0, linesAfterBanner);
343
- cursorTo(process.stdout, 0);
344
- }
345
- else {
346
- cursorTo(process.stdout, 0);
347
- }
348
- return true;
349
478
  }
350
- renderAndStoreBanner(state, model, provider, thoughtLevel) {
479
+ renderAndStoreBanner(state, model, provider) {
351
480
  const width = this.getBannerWidth();
352
- const effectiveThought = typeof thoughtLevel === 'undefined' ? state.thoughtLevel : thoughtLevel;
353
- const lines = this.buildSessionLines(state.profileLabel, state.profileName, model, provider, state.workingDir, width, effectiveThought);
354
- const bannerOptions = state.version ? { badge: state.version } : undefined;
355
- const banner = this.buildBanner('Erosolar CLI', width, lines, bannerOptions);
481
+ const lines = this.buildSessionLines(state.profileLabel, state.profileName, model, provider, state.workingDir, width);
482
+ const banner = this.buildBanner('Erosolar CLI', width, lines, this.buildBannerOptions(state.version));
356
483
  const startLine = this.stdoutTracker.totalLines;
357
- console.log(banner);
484
+ this.withOutput(() => {
485
+ console.log(banner);
486
+ });
358
487
  state.startLine = startLine;
359
488
  state.height = this.measureBannerHeight(banner);
360
489
  state.width = width;
361
490
  state.model = model;
362
491
  state.provider = provider;
363
- state.thoughtLevel = effectiveThought;
364
492
  }
365
493
  formatModelLabel(model) {
366
494
  if (/gpt-5\.1-?codex/i.test(model)) {
@@ -389,55 +517,36 @@ export class Display {
389
517
  }
390
518
  return model;
391
519
  }
392
- describeModelForStatus(model, thoughtLevel) {
393
- const base = this.formatModelLabel(model);
394
- return thoughtLevel ? `${base} (${thoughtLevel})` : base;
395
- }
396
520
  buildChatBox(content, metadata) {
397
521
  const normalized = content.trim();
398
522
  if (!normalized) {
399
523
  return '';
400
524
  }
401
525
  const width = this.resolveMessageWidth();
402
- const header = this.formatAssistantHeader(width);
403
- const body = renderMessageBody(normalized, width);
526
+ const panel = renderMessagePanel(normalized, {
527
+ width,
528
+ title: 'Assistant',
529
+ icon: icons.assistant,
530
+ accentColor: theme.assistant ?? theme.primary,
531
+ borderColor: theme.ui.border,
532
+ });
404
533
  const telemetry = this.formatTelemetryLine(metadata);
405
- const lines = [header, body];
406
- if (telemetry) {
407
- lines.push(telemetry);
534
+ if (!telemetry) {
535
+ return panel;
408
536
  }
409
- return lines.join('\n');
537
+ return `${panel}\n${telemetry}`;
410
538
  }
411
539
  resolveMessageWidth() {
412
540
  const columns = getTerminalColumns();
413
- return Math.max(42, Math.min(columns - 4, 110));
414
- }
415
- formatAssistantHeader(width) {
416
- const label = `${icons.assistant} Assistant`;
417
- const visible = this.visibleLength(label);
418
- const dividerWidth = Math.max(0, width - visible - 1);
419
- const divider = dividerWidth > 0 ? ` ${theme.ui.muted('─'.repeat(dividerWidth))}` : '';
420
- return theme.assistant(label) + divider;
421
- }
422
- buildThoughtStatus(content) {
423
- const blocks = content
424
- .split(/\n{2,}/)
425
- .map((block) => block.trimEnd())
426
- .filter((block) => block.trim().length > 0);
427
- if (!blocks.length) {
428
- return '';
429
- }
430
- const format = this.getThoughtFormat();
431
- const lines = [];
432
- blocks.forEach((block, index) => {
433
- this.appendThoughtBlock(block, format, lines);
434
- if (index < blocks.length - 1) {
435
- lines.push('');
436
- }
437
- });
438
- return lines.join('\n');
439
- }
440
- appendThoughtBlock(block, format, output) {
541
+ return Math.max(DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, Math.min(columns - DISPLAY_CONSTANTS.MESSAGE_PADDING, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
542
+ }
543
+ /**
544
+ * Legacy method for appending thought blocks with tree-like formatting.
545
+ * Kept for backwards compatibility but not actively used.
546
+ * @deprecated Use buildClaudeStyleThought instead
547
+ */
548
+ // @ts-expect-error - Legacy method kept for backwards compatibility
549
+ _appendThoughtBlock(block, format, output) {
441
550
  const rawLines = block.split('\n');
442
551
  const indices = rawLines
443
552
  .map((line, index) => (line.trim().length ? index : -1))
@@ -448,7 +557,7 @@ export class Display {
448
557
  const lastIndex = indices[indices.length - 1];
449
558
  let usedFirst = false;
450
559
  for (let index = 0; index < rawLines.length; index += 1) {
451
- const rawLine = rawLines[index];
560
+ const rawLine = rawLines[index] ?? '';
452
561
  if (!rawLine.trim()) {
453
562
  continue;
454
563
  }
@@ -478,10 +587,16 @@ export class Display {
478
587
  }
479
588
  return options.format.spacer;
480
589
  }
481
- getThoughtFormat() {
482
- const totalWidth = Math.max(48, Math.min(getTerminalColumns(), 96));
590
+ /**
591
+ * Legacy method for generating thought formatting configuration.
592
+ * Kept for backwards compatibility but not actively used.
593
+ * @deprecated Use buildClaudeStyleThought instead
594
+ */
595
+ // @ts-expect-error - Legacy method kept for backwards compatibility
596
+ _getThoughtFormat() {
597
+ const totalWidth = Math.max(DISPLAY_CONSTANTS.MIN_THOUGHT_WIDTH, Math.min(getTerminalColumns(), DISPLAY_CONSTANTS.MAX_THOUGHT_WIDTH));
483
598
  const prefixWidth = Math.max(3, this.visibleLength(`${icons.bullet} `));
484
- const available = Math.max(12, totalWidth - prefixWidth);
599
+ const available = Math.max(DISPLAY_CONSTANTS.MIN_WRAP_WIDTH, totalWidth - prefixWidth);
485
600
  return {
486
601
  totalWidth,
487
602
  prefixWidth,
@@ -510,11 +625,9 @@ export class Display {
510
625
  const parts = [];
511
626
  const elapsed = this.formatElapsed(metadata.elapsedMs);
512
627
  if (elapsed) {
513
- parts.push(`${theme.ui.muted('elapsed')} ${elapsed}`);
514
- }
515
- const context = this.formatContextRemaining(metadata.usage, metadata.contextWindowTokens);
516
- if (context) {
517
- parts.push(theme.primary(context));
628
+ const elapsedLabel = theme.metrics?.elapsedLabel ?? theme.accent;
629
+ const elapsedValue = theme.metrics?.elapsedValue ?? theme.secondary;
630
+ parts.push(`${elapsedLabel('elapsed')} ${elapsedValue(elapsed)}`);
518
631
  }
519
632
  if (!parts.length) {
520
633
  return '';
@@ -534,32 +647,63 @@ export class Display {
534
647
  }
535
648
  return `${seconds}s`;
536
649
  }
537
- formatContextRemaining(usage, windowTokens) {
538
- const totalUsed = this.totalTokens(usage);
539
- if (!windowTokens || windowTokens <= 0 || totalUsed === null) {
540
- return null;
541
- }
542
- const remaining = Math.max(0, windowTokens - totalUsed);
543
- const percent = Math.max(0, Math.min(100, Math.round((remaining / windowTokens) * 100)));
544
- const remainingLabel = this.formatTokenQuantity(remaining);
545
- const usedLabel = this.formatTokenQuantity(totalUsed);
546
- const windowLabel = this.formatTokenQuantity(windowTokens);
547
- return `Context ${remainingLabel} left (${usedLabel} used of ${windowLabel}, ${percent}% remaining)`;
548
- }
549
- formatTokenQuantity(value) {
550
- return value.toLocaleString('en-US');
650
+ buildClaudeStyleBanner(profileLabel, model, _provider, workingDir, width) {
651
+ const gradient = theme.gradient.cool;
652
+ const dim = theme.ui.muted;
653
+ // Build centered content
654
+ const lines = [];
655
+ // Top border
656
+ lines.push(gradient(`╭${'─'.repeat(width)}╮`));
657
+ // Empty line
658
+ lines.push(gradient('│') + ' '.repeat(width) + gradient('│'));
659
+ // Welcome message - centered
660
+ const userName = process.env['USER'] || 'User';
661
+ const welcome = `Welcome back ${userName}!`;
662
+ lines.push(this.centerLine(welcome, width, gradient));
663
+ // Empty line
664
+ lines.push(gradient('│') + ' '.repeat(width) + gradient('│'));
665
+ // Empty line
666
+ lines.push(gradient('│') + ' '.repeat(width) + gradient('│'));
667
+ // Epsilon logo - centered (Claude Code style)
668
+ const logo = [
669
+ '∴ ε ∴',
670
+ '✻ ε ε ε ✻',
671
+ '∴ ε ∴',
672
+ ];
673
+ for (const logoLine of logo) {
674
+ lines.push(this.centerLine(logoLine, width, gradient));
675
+ }
676
+ // Empty line
677
+ lines.push(gradient('│') + ' '.repeat(width) + gradient('│'));
678
+ // Empty line
679
+ lines.push(gradient('│') + ' '.repeat(width) + gradient('│'));
680
+ // Model name - centered
681
+ lines.push(this.centerLine(model, width, gradient));
682
+ // Profile label - centered
683
+ lines.push(this.centerLine(profileLabel, width, gradient, dim));
684
+ // Workspace - centered
685
+ const shortPath = this.abbreviatePath(workingDir, width - 8);
686
+ lines.push(this.centerLine(shortPath, width, gradient, dim));
687
+ // Empty line
688
+ lines.push(gradient('│') + ' '.repeat(width) + gradient('│'));
689
+ // Bottom border
690
+ lines.push(gradient(`╰${'─'.repeat(width)}╯`));
691
+ return lines.join('\n');
551
692
  }
552
- totalTokens(usage) {
553
- if (!usage) {
554
- return null;
555
- }
556
- if (typeof usage.totalTokens === 'number' && Number.isFinite(usage.totalTokens)) {
557
- return usage.totalTokens;
558
- }
559
- const input = typeof usage.inputTokens === 'number' ? usage.inputTokens : 0;
560
- const output = typeof usage.outputTokens === 'number' ? usage.outputTokens : 0;
561
- const sum = input + output;
562
- return sum > 0 ? sum : null;
693
+ centerLine(text, width, borderColor, textColor) {
694
+ const visibleLen = this.visibleLength(text);
695
+ const padding = Math.max(0, Math.floor((width - visibleLen) / 2));
696
+ const rightPad = width - visibleLen - padding;
697
+ const colored = textColor ? textColor(text) : text;
698
+ return borderColor('│') + ' '.repeat(padding) + colored + ' '.repeat(rightPad) + borderColor('│');
699
+ }
700
+ abbreviatePath(path, maxLen) {
701
+ if (path.length <= maxLen)
702
+ return path;
703
+ const parts = path.split('/');
704
+ if (parts.length <= 2)
705
+ return path;
706
+ return parts[0] + '/.../' + parts[parts.length - 1];
563
707
  }
564
708
  buildBanner(title, width, lines, options) {
565
709
  const badge = options?.badge ? ` ${options.badge}` : '';
@@ -571,6 +715,12 @@ export class Display {
571
715
  const bottom = gradient(`╰${'─'.repeat(width)}╯`);
572
716
  return `${top}\n${body}\n${bottom}`;
573
717
  }
718
+ buildBannerOptions(version) {
719
+ if (!version?.trim()) {
720
+ return undefined;
721
+ }
722
+ return { badge: `${version.trim()} • support@ero.solar` };
723
+ }
574
724
  buildBannerLine(text, width) {
575
725
  const padded = this.padLine(text, width);
576
726
  const tinted = theme.ui.background(theme.ui.text(padded));
@@ -578,23 +728,72 @@ export class Display {
578
728
  return `${edge}${tinted}${edge}`;
579
729
  }
580
730
  padLine(text, width) {
581
- const normalized = text.length > width ? text.slice(0, width) : text;
582
- const padding = width - normalized.length;
583
- return `${normalized}${' '.repeat(Math.max(0, padding))}`;
584
- }
585
- formatInfoBlock(label, value, width) {
731
+ const visible = this.visibleLength(text);
732
+ if (visible >= width) {
733
+ return this.truncateVisible(text, width);
734
+ }
735
+ const padding = Math.max(0, width - visible);
736
+ return `${text}${' '.repeat(padding)}`;
737
+ }
738
+ /**
739
+ * Formats an info block with label and value, wrapping if needed.
740
+ * First line gets the label prefix, subsequent lines are indented.
741
+ */
742
+ formatInfoBlock(label, value, width, options) {
743
+ // Validate inputs
744
+ if (!label?.trim() || !value?.trim()) {
745
+ return [];
746
+ }
747
+ if (width <= 0) {
748
+ return [value];
749
+ }
586
750
  const prefix = `${label.toUpperCase()}: `;
587
- const available = Math.max(10, width - prefix.length);
751
+ const prefixLength = prefix.length;
752
+ const available = Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - prefixLength);
588
753
  const wrapped = this.wrapLine(value, available);
589
754
  return wrapped.map((line, index) => {
590
- const indent = index === 0 ? prefix : ' '.repeat(prefix.length);
591
- return this.padLine(`${indent}${line}`, width);
755
+ const indent = index === 0 ? prefix : ' '.repeat(prefixLength);
756
+ const raw = `${indent}${line}`;
757
+ const padded = this.padLine(raw, width);
758
+ if (!options) {
759
+ return padded;
760
+ }
761
+ const labelColor = index === 0 ? options.labelColor : undefined;
762
+ return this.applyInfoLineStyles(padded, prefixLength, line.length, labelColor, options.valueColor);
592
763
  });
593
764
  }
765
+ applyInfoLineStyles(line, prefixLength, valueLength, labelColor, valueColor) {
766
+ const prefix = line.slice(0, prefixLength);
767
+ const remainder = line.slice(prefixLength);
768
+ const tintedPrefix = labelColor ? labelColor(prefix) : prefix;
769
+ const safeValueLength = Math.max(0, Math.min(valueLength, remainder.length));
770
+ if (!valueColor || safeValueLength <= 0) {
771
+ return `${tintedPrefix}${remainder}`;
772
+ }
773
+ const valueSegment = remainder.slice(0, safeValueLength);
774
+ const trailing = remainder.slice(safeValueLength);
775
+ const tintedValue = valueColor(valueSegment);
776
+ return `${tintedPrefix}${tintedValue}${trailing}`;
777
+ }
778
+ getInfoFieldStyle(field) {
779
+ const labelColor = theme.fields?.label ?? ((text) => text);
780
+ const valueColor = theme.fields?.[field] ?? ((text) => text);
781
+ return {
782
+ labelColor,
783
+ valueColor,
784
+ };
785
+ }
786
+ /**
787
+ * Wraps text with a prefix on the first line and optional continuation prefix.
788
+ * Handles multi-line text and word wrapping intelligently.
789
+ */
594
790
  wrapWithPrefix(text, prefix, options) {
595
- const width = Math.max(40, Math.min(getTerminalColumns(), 90));
791
+ if (!text) {
792
+ return prefix.trimEnd();
793
+ }
794
+ const width = Math.max(DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, Math.min(getTerminalColumns(), DISPLAY_CONSTANTS.MAX_ACTION_WIDTH));
596
795
  const prefixWidth = this.visibleLength(prefix);
597
- const available = Math.max(10, width - prefixWidth);
796
+ const available = Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - prefixWidth);
598
797
  const indent = typeof options?.continuationPrefix === 'string'
599
798
  ? options.continuationPrefix
600
799
  : ' '.repeat(Math.max(0, prefixWidth));
@@ -638,11 +837,19 @@ export class Display {
638
837
  const colorize = this.resolveStatusColor(status);
639
838
  return colorize(`${icons.action}`);
640
839
  }
840
+ buildClaudeStyleThought(content) {
841
+ // Claude Code style: compact ⏺ prefix for thoughts
842
+ const prefix = theme.ui.muted('⏺') + ' ';
843
+ return this.wrapWithPrefix(content, prefix);
844
+ }
845
+ // @ts-ignore - Legacy method kept for compatibility
846
+ // Keep legacy method to avoid breaking changes
641
847
  buildSubActionPrefixes(status, isLast) {
642
848
  if (isLast) {
643
849
  const colorize = this.resolveStatusColor(status);
850
+ // Claude Code style: use ⎿ for sub-action prefix
644
851
  return {
645
- prefix: ` ${colorize('└')} `,
852
+ prefix: ` ${colorize(icons.subaction)} `,
646
853
  continuation: ' ',
647
854
  };
648
855
  }
@@ -652,7 +859,12 @@ export class Display {
652
859
  continuation: ` ${branch} `,
653
860
  };
654
861
  }
862
+ /**
863
+ * Wraps a single line of text to fit within the specified width.
864
+ * Intelligently handles word breaking and preserves spaces.
865
+ */
655
866
  wrapLine(text, width) {
867
+ // Handle edge cases
656
868
  if (width <= 0) {
657
869
  return [text];
658
870
  }
@@ -662,70 +874,119 @@ export class Display {
662
874
  if (text.length <= width) {
663
875
  return [text];
664
876
  }
877
+ const words = text.split(/\s+/).filter(Boolean);
878
+ // If no words, chunk the entire text
879
+ if (!words.length) {
880
+ return this.chunkWord(text, width);
881
+ }
665
882
  const lines = [];
666
883
  let current = '';
667
- const words = text.split(/\s+/).filter(Boolean);
668
- const appendWord = (word) => {
669
- if (!word.length) {
670
- return;
671
- }
672
- if (!current.length) {
673
- if (word.length <= width) {
674
- current = word;
675
- return;
676
- }
677
- const chunks = this.chunkWord(word, width);
678
- lines.push(...chunks.slice(0, -1));
679
- current = chunks[chunks.length - 1];
680
- return;
681
- }
682
- if (current.length + 1 + word.length <= width) {
683
- current += ` ${word}`;
684
- return;
884
+ for (const word of words) {
885
+ const appendResult = this.tryAppendWord(current, word, width);
886
+ if (appendResult.shouldFlush) {
887
+ lines.push(current);
685
888
  }
686
- lines.push(current);
687
- if (word.length <= width) {
688
- current = word;
889
+ if (appendResult.chunks.length > 0) {
890
+ // Word was too long and was chunked
891
+ lines.push(...appendResult.chunks.slice(0, -1));
892
+ current = appendResult.chunks[appendResult.chunks.length - 1] ?? '';
689
893
  }
690
894
  else {
691
- const chunks = this.chunkWord(word, width);
692
- lines.push(...chunks.slice(0, -1));
693
- current = chunks[chunks.length - 1];
895
+ current = appendResult.newCurrent;
694
896
  }
695
- };
696
- if (!words.length) {
697
- const chunks = this.chunkWord(text, width);
698
- return chunks;
699
- }
700
- for (const word of words) {
701
- appendWord(word);
702
897
  }
703
898
  if (current) {
704
899
  lines.push(current);
705
900
  }
706
901
  return lines.length ? lines : [''];
707
902
  }
903
+ /**
904
+ * Attempts to append a word to the current line.
905
+ * Returns instructions on how to handle the word.
906
+ */
907
+ tryAppendWord(current, word, width) {
908
+ if (!word) {
909
+ return { shouldFlush: false, newCurrent: current, chunks: [] };
910
+ }
911
+ // Empty current line - start new line with word
912
+ if (!current) {
913
+ if (word.length <= width) {
914
+ return { shouldFlush: false, newCurrent: word, chunks: [] };
915
+ }
916
+ // Word too long, need to chunk it
917
+ return { shouldFlush: false, newCurrent: '', chunks: this.chunkWord(word, width) };
918
+ }
919
+ // Word fits on current line with space
920
+ if (current.length + 1 + word.length <= width) {
921
+ return { shouldFlush: false, newCurrent: `${current} ${word}`, chunks: [] };
922
+ }
923
+ // Word doesn't fit - flush current and start new line
924
+ if (word.length <= width) {
925
+ return { shouldFlush: true, newCurrent: word, chunks: [] };
926
+ }
927
+ // Word doesn't fit and is too long - flush current and chunk word
928
+ return { shouldFlush: true, newCurrent: '', chunks: this.chunkWord(word, width) };
929
+ }
930
+ /**
931
+ * Splits a long word into chunks that fit within the specified width.
932
+ * Used when a single word is too long to fit on one line.
933
+ */
708
934
  chunkWord(word, width) {
709
- if (width <= 0) {
710
- return [word];
935
+ if (width <= 0 || !word) {
936
+ return word ? [word] : [''];
711
937
  }
712
938
  const chunks = [];
713
939
  for (let i = 0; i < word.length; i += width) {
714
940
  chunks.push(word.slice(i, i + width));
715
941
  }
716
- return chunks;
942
+ return chunks.length > 0 ? chunks : [''];
717
943
  }
944
+ /**
945
+ * Pads a prefix string to the specified width with spaces.
946
+ */
718
947
  padPrefix(value, width) {
719
- if (value.length >= width) {
948
+ if (!value || value.length >= width || width <= 0) {
720
949
  return value;
721
950
  }
722
951
  return value.padEnd(width, ' ');
723
952
  }
953
+ /**
954
+ * Truncates a string to fit within the specified width,
955
+ * accounting for ANSI color codes and adding ellipsis.
956
+ */
957
+ truncateVisible(value, width) {
958
+ if (width <= 0) {
959
+ return '';
960
+ }
961
+ if (!value) {
962
+ return '';
963
+ }
964
+ const plain = this.stripAnsi(value);
965
+ if (plain.length <= width) {
966
+ return value;
967
+ }
968
+ const slice = plain.slice(0, Math.max(1, width - 1));
969
+ return `${slice}…`;
970
+ }
971
+ /**
972
+ * Returns the visible length of a string, excluding ANSI escape codes.
973
+ */
724
974
  visibleLength(value) {
975
+ if (!value) {
976
+ return 0;
977
+ }
725
978
  return this.stripAnsi(value).length;
726
979
  }
980
+ /**
981
+ * Removes ANSI escape codes from a string to get the visible text.
982
+ * Uses the standard ANSI escape sequence pattern.
983
+ */
727
984
  stripAnsi(value) {
985
+ if (!value) {
986
+ return '';
987
+ }
728
988
  return value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, '');
729
989
  }
730
990
  }
731
991
  export const display = new Display();
992
+ //# sourceMappingURL=display.js.map