@codemieai/code 0.0.2 → 0.0.4

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 (360) hide show
  1. package/README.md +357 -719
  2. package/bin/codemie-claude.js +145 -0
  3. package/bin/codemie-code.js +128 -15
  4. package/bin/codemie-codex.js +137 -0
  5. package/bin/codemie.js +1 -1
  6. package/dist/agents/adapters/claude-code.d.ts +7 -2
  7. package/dist/agents/adapters/claude-code.d.ts.map +1 -1
  8. package/dist/agents/adapters/claude-code.js +94 -58
  9. package/dist/agents/adapters/claude-code.js.map +1 -1
  10. package/dist/agents/adapters/codemie-code.d.ts +11 -2
  11. package/dist/agents/adapters/codemie-code.d.ts.map +1 -1
  12. package/dist/agents/adapters/codemie-code.js +93 -25
  13. package/dist/agents/adapters/codemie-code.js.map +1 -1
  14. package/dist/agents/adapters/codex.d.ts +7 -2
  15. package/dist/agents/adapters/codex.d.ts.map +1 -1
  16. package/dist/agents/adapters/codex.js +104 -39
  17. package/dist/agents/adapters/codex.js.map +1 -1
  18. package/dist/agents/codemie-code/agent.d.ts +89 -0
  19. package/dist/agents/codemie-code/agent.d.ts.map +1 -0
  20. package/dist/agents/codemie-code/agent.js +689 -0
  21. package/dist/agents/codemie-code/agent.js.map +1 -0
  22. package/dist/agents/codemie-code/config.d.ts +40 -0
  23. package/dist/agents/codemie-code/config.d.ts.map +1 -0
  24. package/dist/agents/codemie-code/config.js +278 -0
  25. package/dist/agents/codemie-code/config.js.map +1 -0
  26. package/dist/agents/codemie-code/filters.d.ts +91 -0
  27. package/dist/agents/codemie-code/filters.d.ts.map +1 -0
  28. package/dist/agents/codemie-code/filters.js +328 -0
  29. package/dist/agents/codemie-code/filters.js.map +1 -0
  30. package/dist/agents/codemie-code/index.d.ts +92 -0
  31. package/dist/agents/codemie-code/index.d.ts.map +1 -0
  32. package/dist/agents/codemie-code/index.js +327 -0
  33. package/dist/agents/codemie-code/index.js.map +1 -0
  34. package/dist/agents/codemie-code/modes/contextAwarePlanning.d.ts +87 -0
  35. package/dist/agents/codemie-code/modes/contextAwarePlanning.d.ts.map +1 -0
  36. package/dist/agents/codemie-code/modes/contextAwarePlanning.js +957 -0
  37. package/dist/agents/codemie-code/modes/contextAwarePlanning.js.map +1 -0
  38. package/dist/agents/codemie-code/modes/planMode.d.ts +116 -0
  39. package/dist/agents/codemie-code/modes/planMode.d.ts.map +1 -0
  40. package/dist/agents/codemie-code/modes/planMode.js +537 -0
  41. package/dist/agents/codemie-code/modes/planMode.js.map +1 -0
  42. package/dist/agents/codemie-code/prompts.d.ts +40 -0
  43. package/dist/agents/codemie-code/prompts.d.ts.map +1 -0
  44. package/dist/agents/codemie-code/prompts.js +160 -0
  45. package/dist/agents/codemie-code/prompts.js.map +1 -0
  46. package/dist/agents/codemie-code/storage/todoStorage.d.ts +78 -0
  47. package/dist/agents/codemie-code/storage/todoStorage.d.ts.map +1 -0
  48. package/dist/agents/codemie-code/storage/todoStorage.js +225 -0
  49. package/dist/agents/codemie-code/storage/todoStorage.js.map +1 -0
  50. package/dist/agents/codemie-code/tokenUtils.d.ts +108 -0
  51. package/dist/agents/codemie-code/tokenUtils.d.ts.map +1 -0
  52. package/dist/agents/codemie-code/tokenUtils.js +220 -0
  53. package/dist/agents/codemie-code/tokenUtils.js.map +1 -0
  54. package/dist/agents/codemie-code/toolMetadata.d.ts +15 -0
  55. package/dist/agents/codemie-code/toolMetadata.d.ts.map +1 -0
  56. package/dist/agents/codemie-code/toolMetadata.js +315 -0
  57. package/dist/agents/codemie-code/toolMetadata.js.map +1 -0
  58. package/dist/agents/codemie-code/tools/index.d.ts +45 -0
  59. package/dist/agents/codemie-code/tools/index.d.ts.map +1 -0
  60. package/dist/agents/codemie-code/tools/index.js +407 -0
  61. package/dist/agents/codemie-code/tools/index.js.map +1 -0
  62. package/dist/agents/codemie-code/tools/planning.d.ts +53 -0
  63. package/dist/agents/codemie-code/tools/planning.d.ts.map +1 -0
  64. package/dist/agents/codemie-code/tools/planning.js +224 -0
  65. package/dist/agents/codemie-code/tools/planning.js.map +1 -0
  66. package/dist/agents/codemie-code/types.d.ts +418 -0
  67. package/dist/agents/codemie-code/types.d.ts.map +1 -0
  68. package/dist/agents/codemie-code/types.js +35 -0
  69. package/dist/agents/codemie-code/types.js.map +1 -0
  70. package/dist/agents/codemie-code/ui/progressTracker.d.ts +125 -0
  71. package/dist/agents/codemie-code/ui/progressTracker.d.ts.map +1 -0
  72. package/dist/agents/codemie-code/ui/progressTracker.js +343 -0
  73. package/dist/agents/codemie-code/ui/progressTracker.js.map +1 -0
  74. package/dist/agents/codemie-code/ui/todoPanel.d.ts +112 -0
  75. package/dist/agents/codemie-code/ui/todoPanel.d.ts.map +1 -0
  76. package/dist/agents/codemie-code/ui/todoPanel.js +318 -0
  77. package/dist/agents/codemie-code/ui/todoPanel.js.map +1 -0
  78. package/dist/agents/codemie-code/ui.d.ts +179 -0
  79. package/dist/agents/codemie-code/ui.d.ts.map +1 -0
  80. package/dist/agents/codemie-code/ui.js +1408 -0
  81. package/dist/agents/codemie-code/ui.js.map +1 -0
  82. package/dist/agents/codemie-code/utils/progressionEnforcer.d.ts +87 -0
  83. package/dist/agents/codemie-code/utils/progressionEnforcer.d.ts.map +1 -0
  84. package/dist/agents/codemie-code/utils/progressionEnforcer.js +293 -0
  85. package/dist/agents/codemie-code/utils/progressionEnforcer.js.map +1 -0
  86. package/dist/agents/codemie-code/utils/todoParser.d.ts +41 -0
  87. package/dist/agents/codemie-code/utils/todoParser.d.ts.map +1 -0
  88. package/dist/agents/codemie-code/utils/todoParser.js +305 -0
  89. package/dist/agents/codemie-code/utils/todoParser.js.map +1 -0
  90. package/dist/agents/codemie-code/utils/todoValidator.d.ts +65 -0
  91. package/dist/agents/codemie-code/utils/todoValidator.d.ts.map +1 -0
  92. package/dist/agents/codemie-code/utils/todoValidator.js +249 -0
  93. package/dist/agents/codemie-code/utils/todoValidator.js.map +1 -0
  94. package/dist/agents/codemie-code/validators/planValidator.d.ts +94 -0
  95. package/dist/agents/codemie-code/validators/planValidator.d.ts.map +1 -0
  96. package/dist/agents/codemie-code/validators/planValidator.js +281 -0
  97. package/dist/agents/codemie-code/validators/planValidator.js.map +1 -0
  98. package/dist/agents/registry.d.ts +1 -1
  99. package/dist/agents/registry.d.ts.map +1 -1
  100. package/dist/agents/registry.js +11 -15
  101. package/dist/agents/registry.js.map +1 -1
  102. package/dist/cli/commands/auth.d.ts +3 -0
  103. package/dist/cli/commands/auth.d.ts.map +1 -0
  104. package/dist/cli/commands/auth.js +170 -0
  105. package/dist/cli/commands/auth.js.map +1 -0
  106. package/dist/cli/commands/config.d.ts +3 -0
  107. package/dist/cli/commands/config.d.ts.map +1 -0
  108. package/dist/cli/commands/config.js +350 -0
  109. package/dist/cli/commands/config.js.map +1 -0
  110. package/dist/cli/commands/doctor.d.ts.map +1 -1
  111. package/dist/cli/commands/doctor.js +308 -71
  112. package/dist/cli/commands/doctor.js.map +1 -1
  113. package/dist/cli/commands/env.d.ts +3 -0
  114. package/dist/cli/commands/env.d.ts.map +1 -0
  115. package/dist/cli/commands/env.js +19 -0
  116. package/dist/cli/commands/env.js.map +1 -0
  117. package/dist/cli/commands/install.d.ts.map +1 -1
  118. package/dist/cli/commands/install.js +28 -33
  119. package/dist/cli/commands/install.js.map +1 -1
  120. package/dist/cli/commands/list.js +18 -24
  121. package/dist/cli/commands/list.js.map +1 -1
  122. package/dist/cli/commands/run.d.ts.map +1 -1
  123. package/dist/cli/commands/run.js +297 -31
  124. package/dist/cli/commands/run.js.map +1 -1
  125. package/dist/cli/commands/setup.d.ts +3 -0
  126. package/dist/cli/commands/setup.d.ts.map +1 -0
  127. package/dist/cli/commands/setup.js +523 -0
  128. package/dist/cli/commands/setup.js.map +1 -0
  129. package/dist/cli/commands/tools.d.ts +6 -0
  130. package/dist/cli/commands/tools.d.ts.map +1 -0
  131. package/dist/cli/commands/tools.js +244 -0
  132. package/dist/cli/commands/tools.js.map +1 -0
  133. package/dist/cli/commands/uninstall.js +24 -30
  134. package/dist/cli/commands/uninstall.js.map +1 -1
  135. package/dist/cli/commands/version.d.ts.map +1 -1
  136. package/dist/cli/commands/version.js +11 -16
  137. package/dist/cli/commands/version.js.map +1 -1
  138. package/dist/cli/commands/workflow.d.ts +6 -0
  139. package/dist/cli/commands/workflow.d.ts.map +1 -0
  140. package/dist/cli/commands/workflow.js +424 -0
  141. package/dist/cli/commands/workflow.js.map +1 -0
  142. package/dist/cli/index.js +85 -35
  143. package/dist/cli/index.js.map +1 -1
  144. package/dist/clients/adapters/github.d.ts +17 -0
  145. package/dist/clients/adapters/github.d.ts.map +1 -0
  146. package/dist/clients/adapters/github.js +150 -0
  147. package/dist/clients/adapters/github.js.map +1 -0
  148. package/dist/clients/adapters/gitlab.d.ts +17 -0
  149. package/dist/clients/adapters/gitlab.d.ts.map +1 -0
  150. package/dist/clients/adapters/gitlab.js +147 -0
  151. package/dist/clients/adapters/gitlab.js.map +1 -0
  152. package/dist/clients/registry.d.ts +20 -0
  153. package/dist/clients/registry.d.ts.map +1 -0
  154. package/dist/clients/registry.js +27 -0
  155. package/dist/clients/registry.js.map +1 -0
  156. package/dist/env/manager.js +9 -46
  157. package/dist/env/manager.js.map +1 -1
  158. package/dist/index.d.ts +6 -13
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/index.js +6 -43
  161. package/dist/index.js.map +1 -1
  162. package/dist/tools/detector.d.ts +33 -0
  163. package/dist/tools/detector.d.ts.map +1 -0
  164. package/dist/tools/detector.js +145 -0
  165. package/dist/tools/detector.js.map +1 -0
  166. package/dist/tools/index.d.ts +8 -0
  167. package/dist/tools/index.d.ts.map +1 -0
  168. package/dist/tools/index.js +8 -0
  169. package/dist/tools/index.js.map +1 -0
  170. package/dist/tools/manager.d.ts +21 -0
  171. package/dist/tools/manager.d.ts.map +1 -0
  172. package/dist/tools/manager.js +104 -0
  173. package/dist/tools/manager.js.map +1 -0
  174. package/dist/tools/registry.d.ts +8 -0
  175. package/dist/tools/registry.d.ts.map +1 -0
  176. package/dist/tools/registry.js +36 -0
  177. package/dist/tools/registry.js.map +1 -0
  178. package/dist/tools/types.d.ts +41 -0
  179. package/dist/tools/types.d.ts.map +1 -0
  180. package/dist/tools/types.js +5 -0
  181. package/dist/tools/types.js.map +1 -0
  182. package/dist/types/sso.d.ts +42 -0
  183. package/dist/types/sso.d.ts.map +1 -0
  184. package/dist/types/sso.js +2 -0
  185. package/dist/types/sso.js.map +1 -0
  186. package/dist/utils/agent-compatibility.d.ts +32 -0
  187. package/dist/utils/agent-compatibility.d.ts.map +1 -0
  188. package/dist/utils/agent-compatibility.js +140 -0
  189. package/dist/utils/agent-compatibility.js.map +1 -0
  190. package/dist/utils/async-tips.d.ts.map +1 -1
  191. package/dist/utils/async-tips.js +16 -55
  192. package/dist/utils/async-tips.js.map +1 -1
  193. package/dist/utils/clipboard.d.ts +16 -0
  194. package/dist/utils/clipboard.d.ts.map +1 -0
  195. package/dist/utils/clipboard.js +179 -0
  196. package/dist/utils/clipboard.js.map +1 -0
  197. package/dist/utils/codemie-integration-validator.d.ts +17 -0
  198. package/dist/utils/codemie-integration-validator.d.ts.map +1 -0
  199. package/dist/utils/codemie-integration-validator.js +105 -0
  200. package/dist/utils/codemie-integration-validator.js.map +1 -0
  201. package/dist/utils/codemie-model-fetcher.d.ts +11 -0
  202. package/dist/utils/codemie-model-fetcher.d.ts.map +1 -0
  203. package/dist/utils/codemie-model-fetcher.js +242 -0
  204. package/dist/utils/codemie-model-fetcher.js.map +1 -0
  205. package/dist/utils/config-loader.d.ts +118 -0
  206. package/dist/utils/config-loader.d.ts.map +1 -0
  207. package/dist/utils/config-loader.js +397 -0
  208. package/dist/utils/config-loader.js.map +1 -0
  209. package/dist/utils/credential-store.d.ts +16 -0
  210. package/dist/utils/credential-store.d.ts.map +1 -0
  211. package/dist/utils/credential-store.js +109 -0
  212. package/dist/utils/credential-store.js.map +1 -0
  213. package/dist/utils/dirname.d.ts +7 -0
  214. package/dist/utils/dirname.d.ts.map +1 -0
  215. package/dist/utils/dirname.js +11 -0
  216. package/dist/utils/dirname.js.map +1 -0
  217. package/dist/utils/errors.js +7 -17
  218. package/dist/utils/errors.js.map +1 -1
  219. package/dist/utils/exec.js +3 -6
  220. package/dist/utils/exec.js.map +1 -1
  221. package/dist/utils/first-time.d.ts +34 -0
  222. package/dist/utils/first-time.d.ts.map +1 -0
  223. package/dist/utils/first-time.js +226 -0
  224. package/dist/utils/first-time.js.map +1 -0
  225. package/dist/utils/health-checker.d.ts +20 -0
  226. package/dist/utils/health-checker.d.ts.map +1 -0
  227. package/dist/utils/health-checker.js +172 -0
  228. package/dist/utils/health-checker.js.map +1 -0
  229. package/dist/utils/logger.js +12 -18
  230. package/dist/utils/logger.js.map +1 -1
  231. package/dist/utils/model-fetcher.d.ts +21 -0
  232. package/dist/utils/model-fetcher.d.ts.map +1 -0
  233. package/dist/utils/model-fetcher.js +150 -0
  234. package/dist/utils/model-fetcher.js.map +1 -0
  235. package/dist/utils/sso-auth.d.ts +15 -0
  236. package/dist/utils/sso-auth.d.ts.map +1 -0
  237. package/dist/utils/sso-auth.js +207 -0
  238. package/dist/utils/sso-auth.js.map +1 -0
  239. package/dist/utils/sso-gateway.d.ts +47 -0
  240. package/dist/utils/sso-gateway.d.ts.map +1 -0
  241. package/dist/utils/sso-gateway.js +298 -0
  242. package/dist/utils/sso-gateway.js.map +1 -0
  243. package/dist/utils/tips.d.ts.map +1 -1
  244. package/dist/utils/tips.js +13 -52
  245. package/dist/utils/tips.js.map +1 -1
  246. package/dist/workflows/detector.d.ts +37 -0
  247. package/dist/workflows/detector.d.ts.map +1 -0
  248. package/dist/workflows/detector.js +160 -0
  249. package/dist/workflows/detector.js.map +1 -0
  250. package/dist/workflows/index.d.ts +8 -0
  251. package/dist/workflows/index.d.ts.map +1 -0
  252. package/dist/workflows/index.js +8 -0
  253. package/dist/workflows/index.js.map +1 -0
  254. package/dist/workflows/installer.d.ts +24 -0
  255. package/dist/workflows/installer.d.ts.map +1 -0
  256. package/dist/workflows/installer.js +105 -0
  257. package/dist/workflows/installer.js.map +1 -0
  258. package/dist/workflows/registry.d.ts +29 -0
  259. package/dist/workflows/registry.d.ts.map +1 -0
  260. package/dist/workflows/registry.js +54 -0
  261. package/dist/workflows/registry.js.map +1 -0
  262. package/dist/workflows/templates/github/metadata.d.ts +6 -0
  263. package/dist/workflows/templates/github/metadata.d.ts.map +1 -0
  264. package/dist/workflows/templates/github/metadata.js +111 -0
  265. package/dist/workflows/templates/github/metadata.js.map +1 -0
  266. package/dist/workflows/templates/gitlab/metadata.d.ts +6 -0
  267. package/dist/workflows/templates/gitlab/metadata.d.ts.map +1 -0
  268. package/dist/workflows/templates/gitlab/metadata.js +14 -0
  269. package/dist/workflows/templates/gitlab/metadata.js.map +1 -0
  270. package/dist/workflows/types.d.ts +71 -0
  271. package/dist/workflows/types.d.ts.map +1 -0
  272. package/dist/workflows/types.js +5 -0
  273. package/dist/workflows/types.js.map +1 -0
  274. package/package.json +32 -25
  275. package/src/workflows/templates/github/code-ci.yml +529 -0
  276. package/src/workflows/templates/github/inline-fix.yml +665 -0
  277. package/src/workflows/templates/github/pr-review.yml +677 -0
  278. package/.claude/agents/README.md +0 -298
  279. package/.claude/agents/release-manager.md +0 -857
  280. package/.codemie/guides/git-workflow.md +0 -493
  281. package/CLAUDE.md +0 -855
  282. package/dist/agents/adapters/aider.d.ts +0 -12
  283. package/dist/agents/adapters/aider.d.ts.map +0 -1
  284. package/dist/agents/adapters/aider.js +0 -80
  285. package/dist/agents/adapters/aider.js.map +0 -1
  286. package/dist/cli/cli.d.ts +0 -4
  287. package/dist/cli/cli.d.ts.map +0 -1
  288. package/dist/cli/cli.js +0 -107
  289. package/dist/cli/cli.js.map +0 -1
  290. package/dist/cli/commands/mcp.d.ts +0 -3
  291. package/dist/cli/commands/mcp.d.ts.map +0 -1
  292. package/dist/cli/commands/mcp.js +0 -459
  293. package/dist/cli/commands/mcp.js.map +0 -1
  294. package/dist/code/agent-events.d.ts +0 -39
  295. package/dist/code/agent-events.d.ts.map +0 -1
  296. package/dist/code/agent-events.js +0 -4
  297. package/dist/code/agent-events.js.map +0 -1
  298. package/dist/code/agent.d.ts +0 -19
  299. package/dist/code/agent.d.ts.map +0 -1
  300. package/dist/code/agent.js +0 -144
  301. package/dist/code/agent.js.map +0 -1
  302. package/dist/code/config.d.ts +0 -13
  303. package/dist/code/config.d.ts.map +0 -1
  304. package/dist/code/config.js +0 -41
  305. package/dist/code/config.js.map +0 -1
  306. package/dist/code/index.d.ts +0 -19
  307. package/dist/code/index.d.ts.map +0 -1
  308. package/dist/code/index.js +0 -400
  309. package/dist/code/index.js.map +0 -1
  310. package/dist/code/prompts.d.ts +0 -2
  311. package/dist/code/prompts.d.ts.map +0 -1
  312. package/dist/code/prompts.js +0 -45
  313. package/dist/code/prompts.js.map +0 -1
  314. package/dist/code/tools/command.d.ts +0 -8
  315. package/dist/code/tools/command.d.ts.map +0 -1
  316. package/dist/code/tools/command.js +0 -83
  317. package/dist/code/tools/command.js.map +0 -1
  318. package/dist/code/tools/diff-utils.d.ts +0 -2
  319. package/dist/code/tools/diff-utils.d.ts.map +0 -1
  320. package/dist/code/tools/diff-utils.js +0 -45
  321. package/dist/code/tools/diff-utils.js.map +0 -1
  322. package/dist/code/tools/filesystem.d.ts +0 -11
  323. package/dist/code/tools/filesystem.d.ts.map +0 -1
  324. package/dist/code/tools/filesystem.js +0 -442
  325. package/dist/code/tools/filesystem.js.map +0 -1
  326. package/dist/code/tools/git.d.ts +0 -7
  327. package/dist/code/tools/git.d.ts.map +0 -1
  328. package/dist/code/tools/git.js +0 -111
  329. package/dist/code/tools/git.js.map +0 -1
  330. package/dist/code/tools/mcp.d.ts +0 -13
  331. package/dist/code/tools/mcp.d.ts.map +0 -1
  332. package/dist/code/tools/mcp.js +0 -230
  333. package/dist/code/tools/mcp.js.map +0 -1
  334. package/dist/data/tips.json +0 -118
  335. package/dist/ui/terminal-ui.d.ts +0 -73
  336. package/dist/ui/terminal-ui.d.ts.map +0 -1
  337. package/dist/ui/terminal-ui.js +0 -900
  338. package/dist/ui/terminal-ui.js.map +0 -1
  339. package/dist/utils/env-mapper.d.ts +0 -40
  340. package/dist/utils/env-mapper.d.ts.map +0 -1
  341. package/dist/utils/env-mapper.js +0 -122
  342. package/dist/utils/env-mapper.js.map +0 -1
  343. package/docs/USER_GUIDE.md +0 -573
  344. package/eslint.config.mjs +0 -43
  345. package/tests/agent-direct.test.mjs +0 -45
  346. package/tests/agent-output.test.mjs +0 -64
  347. package/tests/codemie-code.test.mjs +0 -42
  348. package/tests/context7-only.test.mjs +0 -42
  349. package/tests/conversation-flow.test.mjs +0 -63
  350. package/tests/interactive-simulation.test.mjs +0 -60
  351. package/tests/live-output.test.mjs +0 -53
  352. package/tests/mcp-context7.test.mjs +0 -105
  353. package/tests/mcp-e2e.test.mjs +0 -109
  354. package/tests/mcp-time-server.test.mjs +0 -58
  355. package/tests/streaming.test.mjs +0 -57
  356. package/tests/test-helpers.mjs +0 -94
  357. package/tests/text-wrapping.test.mjs +0 -33
  358. package/tests/tool-count.test.mjs +0 -81
  359. package/tests/ui-format.test.mjs +0 -39
  360. package/tests/ui-state.test.mjs +0 -72
@@ -0,0 +1,1408 @@
1
+ import { intro, outro, text, spinner, note, isCancel } from '@clack/prompts';
2
+ import chalk from 'chalk';
3
+ import { formatToolMetadata } from './toolMetadata.js';
4
+ import { formatCost, formatTokens, formatTokenUsageSummary } from './tokenUtils.js';
5
+ import { hasClipboardImage, getClipboardImage } from '../../utils/clipboard.js';
6
+ import { TodoPanel } from './ui/todoPanel.js';
7
+ import { getProgressTracker } from './ui/progressTracker.js';
8
+ import { TodoStateManager } from './tools/planning.js';
9
+ /**
10
+ * Terminal UI interface for CodeMie Agent using Clack
11
+ */
12
+ export class CodeMieTerminalUI {
13
+ agent;
14
+ currentSpinner;
15
+ todoPanel;
16
+ progressTracker;
17
+ planMode = false;
18
+ activePlanningPhase = null;
19
+ constructor(agent) {
20
+ this.agent = agent;
21
+ this.todoPanel = new TodoPanel({
22
+ showProgress: true,
23
+ compact: false
24
+ });
25
+ this.progressTracker = getProgressTracker({
26
+ realTimeUpdates: true,
27
+ showCelebrations: true,
28
+ compact: true
29
+ });
30
+ // Register for todo update events
31
+ TodoStateManager.addEventCallback(this.handleTodoUpdate.bind(this));
32
+ }
33
+ /**
34
+ * Start interactive terminal session
35
+ */
36
+ async startInteractive() {
37
+ // Welcome message
38
+ intro(chalk.cyan('šŸ¤– CodeMie Native Agent'));
39
+ const config = this.agent.getConfig();
40
+ if (config) {
41
+ // Use displayProvider for user-facing output, or fall back to normalized provider
42
+ const displayProvider = config.displayProvider || config.provider;
43
+ console.log(chalk.cyan('ā—‡ Configuration'));
44
+ console.log(` Provider: ${chalk.yellow(displayProvider)}`);
45
+ console.log(` Model: ${chalk.cyan(config.model)}`);
46
+ console.log(` Working Directory: ${chalk.dim(config.workingDirectory)}`);
47
+ console.log(` Mode: ${this.planMode ? chalk.green('Plan Mode') : chalk.yellow('Direct Mode')}`);
48
+ console.log('');
49
+ }
50
+ console.log(chalk.dim('Type /help for commands, /exit to quit'));
51
+ console.log(chalk.dim('Enter = send, Shift+Enter = new line, Cmd+V = paste text'));
52
+ console.log(chalk.dim('šŸ“ø Tab = insert clipboard image • Multiple images supported'));
53
+ console.log(chalk.dim('šŸ’” Press Ctrl+H for hotkeys, Ctrl+P to toggle plan mode\n'));
54
+ // Main interaction loop
55
+ while (true) {
56
+ const input = await this.getMultilineInput();
57
+ if (input === null) {
58
+ outro(chalk.dim('Goodbye! šŸ‘‹'));
59
+ break;
60
+ }
61
+ const trimmed = input.text.trim();
62
+ // Handle special commands
63
+ if (trimmed.startsWith('/')) {
64
+ const handled = await this.handleCommand(trimmed);
65
+ if (handled === 'exit')
66
+ break;
67
+ continue;
68
+ }
69
+ if (trimmed === '')
70
+ continue;
71
+ // Execute the task with streaming UI (respecting current mode)
72
+ await this.executeTaskWithCurrentMode(trimmed, input.images);
73
+ }
74
+ }
75
+ /**
76
+ * Get input from user with Shift+Enter multiline support and image pasting
77
+ */
78
+ async getMultilineInput() {
79
+ return new Promise((resolve) => {
80
+ if (!process.stdin.setRawMode) {
81
+ // Fallback for environments without raw mode
82
+ this.getFallbackInput().then(resolve).catch(() => resolve(null));
83
+ return;
84
+ }
85
+ let cleanupDone = false;
86
+ const performCleanup = () => {
87
+ if (cleanupDone)
88
+ return;
89
+ cleanupDone = true;
90
+ try {
91
+ if (process.stdin.setRawMode) {
92
+ process.stdin.setRawMode(false);
93
+ }
94
+ process.stdin.pause();
95
+ process.stdin.removeAllListeners('data');
96
+ process.stdin.removeAllListeners('error');
97
+ }
98
+ catch {
99
+ // Ignore cleanup errors
100
+ }
101
+ };
102
+ // Set up error handling first
103
+ process.stdin.once('error', (error) => {
104
+ performCleanup();
105
+ const config = this.agent.getConfig();
106
+ if (config?.debug) {
107
+ console.error('[DEBUG] Stdin error:', error);
108
+ }
109
+ resolve(null);
110
+ });
111
+ try {
112
+ process.stdin.setRawMode(true);
113
+ process.stdin.resume();
114
+ process.stdin.setEncoding('utf8');
115
+ let lines = [];
116
+ let currentLine = '';
117
+ let isFirstLine = true;
118
+ let escapeSequence = '';
119
+ let images = [];
120
+ let imageCounter = 0;
121
+ const writePrompt = () => {
122
+ try {
123
+ const prompt = isFirstLine ? '> ' : '... ';
124
+ process.stdout.write(prompt);
125
+ }
126
+ catch {
127
+ // Ignore prompt write errors
128
+ }
129
+ };
130
+ // Add timeout to prevent hanging indefinitely
131
+ const inputTimeout = setTimeout(() => {
132
+ performCleanup();
133
+ resolve(null);
134
+ }, 30000); // 30 second timeout
135
+ writePrompt();
136
+ process.stdin.on('data', (key) => {
137
+ if (cleanupDone)
138
+ return; // Prevent processing after cleanup
139
+ try {
140
+ const data = key.toString('utf8');
141
+ // Handle escape sequences
142
+ if (data.startsWith('\x1b')) {
143
+ escapeSequence += data;
144
+ // Wait for complete escape sequence (timeout after short delay)
145
+ setTimeout(() => {
146
+ escapeSequence = '';
147
+ }, 10);
148
+ return;
149
+ }
150
+ // Check for Shift+Enter patterns on macOS
151
+ // Shift+Enter in macOS Terminal typically sends: \r\n or \n\r
152
+ if (data === '\r\n' || data === '\n\r' || (escapeSequence && data === '\r')) {
153
+ // This is Shift+Enter - add line and continue
154
+ lines.push(currentLine);
155
+ currentLine = '';
156
+ isFirstLine = false;
157
+ process.stdout.write('\n');
158
+ writePrompt();
159
+ escapeSequence = '';
160
+ return;
161
+ }
162
+ // Regular Enter - send message
163
+ if (data === '\r' || data === '\n') {
164
+ if (currentLine.trim() === '' && lines.length === 0) {
165
+ // Empty input, continue asking
166
+ writePrompt();
167
+ return;
168
+ }
169
+ // Send the message
170
+ if (currentLine.trim() !== '') {
171
+ lines.push(currentLine);
172
+ }
173
+ process.stdout.write('\n');
174
+ clearTimeout(inputTimeout);
175
+ performCleanup();
176
+ resolve({
177
+ text: lines.join('\n'),
178
+ images: images
179
+ });
180
+ return;
181
+ }
182
+ // Ctrl+C
183
+ if (data === '\u0003') {
184
+ clearTimeout(inputTimeout);
185
+ performCleanup();
186
+ resolve(null);
187
+ return;
188
+ }
189
+ // Hotkeys for mode switching
190
+ // Ctrl+P - Toggle plan mode
191
+ if (data === '\u0010') {
192
+ this.handleHotkey('toggle-plan-mode');
193
+ // Mode change notification is handled in showModeChangeNotification()
194
+ writePrompt();
195
+ process.stdout.write(currentLine);
196
+ return;
197
+ }
198
+ // Ctrl+H - Show hotkey help
199
+ if (data === '\u0008') {
200
+ this.showHotkeyHelp();
201
+ writePrompt();
202
+ process.stdout.write(currentLine);
203
+ return;
204
+ }
205
+ // Ctrl+T - Show current todos
206
+ if (data === '\u0014') {
207
+ this.handleHotkey('show-todos');
208
+ writePrompt();
209
+ process.stdout.write(currentLine);
210
+ return;
211
+ }
212
+ // Ctrl+S - Show current mode status (changed from Ctrl+M to avoid Enter conflict)
213
+ if (data === '\u0013') {
214
+ this.showModeStatus();
215
+ writePrompt();
216
+ process.stdout.write(currentLine);
217
+ return;
218
+ }
219
+ // Alt+M - Show mode status (Alt key sequences start with \x1b)
220
+ if (data === 'm' && escapeSequence.includes('\x1b')) {
221
+ this.showModeStatus();
222
+ writePrompt();
223
+ process.stdout.write(currentLine);
224
+ escapeSequence = '';
225
+ return;
226
+ }
227
+ // Ctrl+I - Insert image from clipboard (Tab key)
228
+ if (data === '\u0009') {
229
+ // Check if there's an image in clipboard
230
+ hasClipboardImage().then(hasImage => {
231
+ if (hasImage) {
232
+ getClipboardImage().then(clipboardImage => {
233
+ if (clipboardImage) {
234
+ imageCounter++;
235
+ images.push(clipboardImage);
236
+ // Insert visual indicator in current line
237
+ const imageIndicator = chalk.blueBright(`[Image #${imageCounter}]`);
238
+ currentLine += imageIndicator;
239
+ process.stdout.write(imageIndicator);
240
+ console.log(chalk.green(`\nšŸ“ø Image #${imageCounter} added from clipboard (${clipboardImage.mimeType})`));
241
+ writePrompt();
242
+ process.stdout.write(currentLine);
243
+ }
244
+ });
245
+ }
246
+ else {
247
+ console.log(chalk.yellow('\nāš ļø No image found in clipboard'));
248
+ writePrompt();
249
+ process.stdout.write(currentLine);
250
+ }
251
+ }).catch(() => {
252
+ console.log(chalk.rgb(255, 120, 120)('\nāŒ Error accessing clipboard'));
253
+ writePrompt();
254
+ process.stdout.write(currentLine);
255
+ });
256
+ return;
257
+ }
258
+ // Backspace
259
+ if (data === '\u007F' || data === '\b') {
260
+ if (currentLine.length > 0) {
261
+ currentLine = currentLine.slice(0, -1);
262
+ process.stdout.write('\b \b');
263
+ }
264
+ return;
265
+ }
266
+ // Handle clipboard paste (Cmd+V on macOS, Ctrl+V on Windows/Linux)
267
+ // Pasted content can be multiple characters, so handle any printable text
268
+ if (data.length > 1 || (data.length === 1 && (data.charCodeAt(0) >= 32 || data === '\t'))) {
269
+ // Filter out non-printable characters except tabs
270
+ const printableData = data.split('').filter(char => char.charCodeAt(0) >= 32 || char === '\t').join('');
271
+ if (printableData.length > 0) {
272
+ currentLine += printableData;
273
+ process.stdout.write(printableData);
274
+ }
275
+ }
276
+ escapeSequence = '';
277
+ }
278
+ catch (error) {
279
+ // Handle data processing errors gracefully
280
+ const config = this.agent.getConfig();
281
+ if (config?.debug) {
282
+ console.error('[DEBUG] Input processing error:', error);
283
+ }
284
+ // Continue processing - don't crash on single key errors
285
+ }
286
+ });
287
+ }
288
+ catch (error) {
289
+ performCleanup();
290
+ const config = this.agent.getConfig();
291
+ if (config?.debug) {
292
+ console.error('[DEBUG] Input setup error:', error);
293
+ }
294
+ resolve(null);
295
+ }
296
+ });
297
+ }
298
+ /**
299
+ * Fallback input method for environments without raw mode
300
+ */
301
+ async getFallbackInput() {
302
+ const input = await text({
303
+ message: '>',
304
+ placeholder: 'Type your message... (Tab to insert clipboard image)'
305
+ });
306
+ if (isCancel(input)) {
307
+ return null;
308
+ }
309
+ const textInput = input === undefined || input === null ? '' : String(input);
310
+ // In fallback mode, we can't handle interactive image insertion
311
+ // So just return text with empty images array
312
+ return {
313
+ text: textInput,
314
+ images: []
315
+ };
316
+ }
317
+ /**
318
+ * Handle special commands
319
+ */
320
+ async handleCommand(command) {
321
+ const [cmd] = command.slice(1).split(' ');
322
+ switch (cmd) {
323
+ case 'help':
324
+ note(`${chalk.cyan('/help')} - Show this help message\n` +
325
+ `${chalk.cyan('/clear')} - Clear conversation history\n` +
326
+ `${chalk.cyan('/stats')} - Show agent statistics\n` +
327
+ `${chalk.cyan('/todos')} - Show current todo list and progress\n` +
328
+ `${chalk.cyan('/config')} - Show configuration\n` +
329
+ `${chalk.cyan('/health')} - Run health check\n` +
330
+ `${chalk.cyan('/exit')} - Exit the agent\n\n` +
331
+ `${chalk.yellow('Hotkeys:')}\n` +
332
+ `- ${chalk.cyan('Ctrl+P')} - Toggle plan mode on/off\n` +
333
+ `- ${chalk.cyan('Ctrl+H')} - Show detailed hotkey help\n` +
334
+ `- ${chalk.cyan('Ctrl+T')} - Show current todo list\n` +
335
+ `- ${chalk.cyan('Ctrl+S')} - Show current mode status\n\n` +
336
+ `${chalk.yellow('Input Controls:')}\n` +
337
+ `- ${chalk.cyan('Enter')} - Send message\n` +
338
+ `- ${chalk.cyan('Shift+Enter')} - New line (multiline input)\n` +
339
+ `- ${chalk.cyan('Cmd+V / Ctrl+V')} - Paste text from clipboard\n` +
340
+ `- ${chalk.cyan('Tab')} - Insert image from clipboard\n` +
341
+ `- ${chalk.cyan('Ctrl+C')} - Cancel current input\n\n` +
342
+ `${chalk.yellow('Image Support:')}\n` +
343
+ `- Copy image/screenshot to clipboard\n` +
344
+ `- Press ${chalk.cyan('Tab')} to insert as ${chalk.blueBright('[Image #N]')}\n` +
345
+ `- Multiple images supported per message\n` +
346
+ `- AI analyzes both text and all images`, 'Available Commands');
347
+ break;
348
+ case 'clear':
349
+ this.agent.clearHistory();
350
+ note('Conversation history cleared', 'History');
351
+ break;
352
+ case 'stats':
353
+ await this.showStats();
354
+ break;
355
+ case 'todos':
356
+ await this.showTodos();
357
+ break;
358
+ case 'config':
359
+ await this.showConfig();
360
+ break;
361
+ case 'health':
362
+ await this.runHealthCheck();
363
+ break;
364
+ case 'exit':
365
+ outro(chalk.dim('Goodbye! šŸ‘‹'));
366
+ return 'exit';
367
+ default:
368
+ note(chalk.rgb(255, 120, 120)(`Unknown command: ${command}\nType /help for available commands`), 'Error');
369
+ break;
370
+ }
371
+ }
372
+ /**
373
+ * Execute task with modern UI feedback
374
+ */
375
+ async executeTaskWithUI(task, images = []) {
376
+ let hasStarted = false;
377
+ let toolCallCount = 0;
378
+ try {
379
+ // Show image summary if any images were provided
380
+ if (images.length > 0) {
381
+ const imageTypes = images.map(img => img.mimeType).join(', ');
382
+ note(chalk.green(`šŸ“ø ${images.length} image${images.length > 1 ? 's' : ''} included: ${imageTypes}`), 'Image Input');
383
+ }
384
+ // Start streaming with visual feedback
385
+ await this.agent.chatStream(task, (event) => {
386
+ switch (event.type) {
387
+ case 'thinking_start':
388
+ // Show "Thinking..." spinner when the agent starts processing
389
+ if (!this.currentSpinner) {
390
+ this.currentSpinner = spinner();
391
+ }
392
+ this.currentSpinner.start(chalk.dim('Thinking...'));
393
+ break;
394
+ case 'content_chunk':
395
+ if (!hasStarted) {
396
+ // Stop any existing spinner and start response
397
+ if (this.currentSpinner) {
398
+ this.currentSpinner.stop();
399
+ this.currentSpinner = undefined;
400
+ }
401
+ console.log(chalk.cyan('\nCodeMie Thoughts:'));
402
+ hasStarted = true;
403
+ }
404
+ process.stdout.write(event.content || '');
405
+ break;
406
+ case 'tool_call_start':
407
+ toolCallCount++;
408
+ if (!this.currentSpinner) {
409
+ this.currentSpinner = spinner();
410
+ }
411
+ this.currentSpinner.start(chalk.yellow(`Using ${event.toolName}...`));
412
+ break;
413
+ case 'tool_call_progress':
414
+ if (this.currentSpinner && event.toolProgress) {
415
+ const { percentage, operation, details } = event.toolProgress;
416
+ const progressBar = this.createToolProgressBar(percentage);
417
+ let message = `${operation} ${Math.round(percentage)}% ${progressBar}`;
418
+ if (details) {
419
+ message += ` (${details})`;
420
+ }
421
+ this.currentSpinner.message(chalk.cyan(message));
422
+ }
423
+ break;
424
+ case 'tool_call_result':
425
+ if (this.currentSpinner) {
426
+ // Use enhanced metadata if available, otherwise fall back to basic message
427
+ const message = event.toolMetadata
428
+ ? formatToolMetadata(event.toolName || 'tool', event.toolMetadata)
429
+ : `āœ“ ${event.toolName} completed`;
430
+ this.currentSpinner.stop(chalk.green(message));
431
+ // Show additional details if available and not an error (but avoid duplication)
432
+ if (event.toolMetadata && event.toolMetadata.success && this.shouldShowDetails(event.toolName || '')) {
433
+ this.showToolDetails(event.toolName || '', event.toolMetadata);
434
+ }
435
+ this.currentSpinner = undefined;
436
+ }
437
+ break;
438
+ case 'complete':
439
+ if (this.currentSpinner) {
440
+ this.currentSpinner.stop();
441
+ this.currentSpinner = undefined;
442
+ }
443
+ if (hasStarted) {
444
+ console.log('\n'); // Add spacing after response
445
+ }
446
+ // Show detailed task summary
447
+ this.showTaskSummary(toolCallCount);
448
+ break;
449
+ case 'error':
450
+ if (this.currentSpinner) {
451
+ this.currentSpinner.stop();
452
+ this.currentSpinner = undefined;
453
+ }
454
+ note(chalk.red(`Error: ${event.error}`), 'Error');
455
+ break;
456
+ }
457
+ }, images);
458
+ }
459
+ catch (error) {
460
+ if (this.currentSpinner) {
461
+ this.currentSpinner.stop();
462
+ this.currentSpinner = undefined;
463
+ }
464
+ const errorMessage = error instanceof Error ? error.message : String(error);
465
+ note(chalk.red(`Execution failed: ${errorMessage}`), 'Error');
466
+ }
467
+ finally {
468
+ // Cleanup complete
469
+ }
470
+ }
471
+ /**
472
+ * Show agent statistics
473
+ */
474
+ async showStats() {
475
+ const stats = this.agent.getStats();
476
+ if (!stats) {
477
+ note('No statistics available', 'Stats');
478
+ return;
479
+ }
480
+ const statsText = [
481
+ `${chalk.yellow('Token Usage:')}`,
482
+ ` Input: ${chalk.cyan(formatTokens(stats.inputTokens))}`,
483
+ ` Output: ${chalk.cyan(formatTokens(stats.outputTokens))}${stats.cachedTokens > 0 ? ` + ${chalk.green(formatTokens(stats.cachedTokens))} cached` : ''}`,
484
+ ` Total: ${chalk.cyan(formatTokens(stats.totalTokens))}`,
485
+ ` Cost: ${chalk.green(formatCost(stats.estimatedTotalCost))}`,
486
+ '',
487
+ `${chalk.yellow('Execution Stats:')}`,
488
+ ` LLM Calls: ${chalk.cyan(stats.llmCalls)}`,
489
+ ` Tool Calls: ${chalk.cyan(stats.toolCalls)} (${chalk.green(stats.successfulTools)} success, ${chalk.red(stats.failedTools)} failed)`,
490
+ ` Total Steps: ${chalk.cyan(stats.executionSteps.length)}`,
491
+ ` Duration: ${chalk.dim(stats.executionTime + 'ms')}`
492
+ ].join('\n');
493
+ note(statsText, 'Agent Statistics');
494
+ // Show detailed step breakdown if available
495
+ if (stats.executionSteps.length > 0) {
496
+ this.showExecutionSteps(stats.executionSteps);
497
+ }
498
+ }
499
+ /**
500
+ * Show current configuration
501
+ */
502
+ async showConfig() {
503
+ const config = this.agent.getConfig();
504
+ if (!config) {
505
+ note('No configuration available', 'Config');
506
+ return;
507
+ }
508
+ // Use displayProvider for user-facing output, or fall back to normalized provider
509
+ const displayProvider = config.displayProvider || config.provider;
510
+ const configText = [
511
+ `Provider: ${chalk.yellow(displayProvider)}`,
512
+ `Model: ${chalk.cyan(config.model)}`,
513
+ `Base URL: ${chalk.dim(config.baseUrl)}`,
514
+ `Working Directory: ${chalk.dim(config.workingDirectory)}`,
515
+ `Debug Mode: ${config.debug ? chalk.green('enabled') : chalk.dim('disabled')}`,
516
+ `Timeout: ${chalk.dim(config.timeout + 'ms')}`
517
+ ].join('\n');
518
+ note(configText, 'Configuration');
519
+ }
520
+ /**
521
+ * Run health check with UI feedback
522
+ */
523
+ async runHealthCheck() {
524
+ const healthSpinner = spinner();
525
+ healthSpinner.start('Running health check...');
526
+ try {
527
+ const health = await this.agent.healthCheck();
528
+ if (health.status === 'healthy') {
529
+ healthSpinner.stop(chalk.green('āœ“ System is healthy'));
530
+ const healthText = [
531
+ `Status: ${chalk.green('Healthy')}`,
532
+ `Provider: ${chalk.yellow(health.provider)}`,
533
+ `Model: ${chalk.cyan(health.model)}`,
534
+ `Tools: ${chalk.cyan(health.toolCount)}`
535
+ ].join('\n');
536
+ note(healthText, 'Health Check');
537
+ }
538
+ else {
539
+ healthSpinner.stop(chalk.red('āœ— System is unhealthy'));
540
+ note(chalk.red(health.error || 'Unknown error'), 'Health Check Failed');
541
+ }
542
+ }
543
+ catch (error) {
544
+ healthSpinner.stop(chalk.red('āœ— Health check failed'));
545
+ note(chalk.red(error instanceof Error ? error.message : String(error)), 'Error');
546
+ }
547
+ }
548
+ /**
549
+ * Format uptime duration
550
+ */
551
+ formatUptime(uptimeMs) {
552
+ const seconds = Math.floor(uptimeMs / 1000);
553
+ const minutes = Math.floor(seconds / 60);
554
+ const hours = Math.floor(minutes / 60);
555
+ if (hours > 0) {
556
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
557
+ }
558
+ else if (minutes > 0) {
559
+ return `${minutes}m ${seconds % 60}s`;
560
+ }
561
+ else {
562
+ return `${seconds}s`;
563
+ }
564
+ }
565
+ /**
566
+ * Execute a single task with minimal UI (for non-interactive mode)
567
+ */
568
+ async executeSingleTask(task, images = []) {
569
+ const taskSpinner = spinner();
570
+ let response = '';
571
+ let toolCallCount = 0;
572
+ let hasStarted = false;
573
+ try {
574
+ await this.agent.chatStream(task, (event) => {
575
+ switch (event.type) {
576
+ case 'thinking_start':
577
+ // Show "Thinking..." when the agent starts processing
578
+ taskSpinner.start(chalk.dim('Thinking...'));
579
+ break;
580
+ case 'content_chunk':
581
+ if (!hasStarted) {
582
+ // Switch from "Thinking..." to "Processing task..." when content starts
583
+ taskSpinner.message(chalk.dim('Processing task...'));
584
+ hasStarted = true;
585
+ }
586
+ response += event.content || '';
587
+ break;
588
+ case 'tool_call_start':
589
+ toolCallCount++;
590
+ taskSpinner.message(chalk.yellow(`Using ${event.toolName}...`));
591
+ break;
592
+ case 'tool_call_progress':
593
+ if (event.toolProgress) {
594
+ const { percentage, operation, details } = event.toolProgress;
595
+ const progressBar = this.createToolProgressBar(percentage);
596
+ let message = `${operation} ${Math.round(percentage)}% ${progressBar}`;
597
+ if (details) {
598
+ message += ` (${details})`;
599
+ }
600
+ taskSpinner.message(chalk.cyan(message));
601
+ }
602
+ break;
603
+ case 'tool_call_result':
604
+ // Show enhanced tool info in single task mode too
605
+ if (event.toolMetadata) {
606
+ const message = formatToolMetadata(event.toolName || 'tool', event.toolMetadata);
607
+ taskSpinner.message(chalk.green(message));
608
+ }
609
+ else {
610
+ taskSpinner.message(chalk.dim('Processing task...'));
611
+ }
612
+ break;
613
+ case 'complete':
614
+ taskSpinner.stop();
615
+ break;
616
+ case 'error':
617
+ taskSpinner.stop(chalk.red(`Error: ${event.error}`));
618
+ break;
619
+ }
620
+ }, images);
621
+ // Show success message with token usage
622
+ const stats = this.agent.getStats();
623
+ const summaryParts = [];
624
+ if (toolCallCount > 0) {
625
+ summaryParts.push(`Used ${toolCallCount} tool${toolCallCount > 1 ? 's' : ''}`);
626
+ }
627
+ if (stats.totalTokens > 0) {
628
+ summaryParts.push(`${formatTokens(stats.totalTokens)} tokens`);
629
+ }
630
+ if (stats.estimatedTotalCost > 0) {
631
+ summaryParts.push(`${formatCost(stats.estimatedTotalCost)}`);
632
+ }
633
+ if (summaryParts.length > 0) {
634
+ console.log(chalk.dim(`${summaryParts.join(' • ')}\n`));
635
+ }
636
+ return response;
637
+ }
638
+ catch (error) {
639
+ taskSpinner.stop(chalk.red('Task failed'));
640
+ throw error;
641
+ }
642
+ }
643
+ /**
644
+ * Execute a task with planning mode and UI streaming (for plan mode)
645
+ */
646
+ async executePlanningTask(task, _images = [], planOnly = false) {
647
+ const planSpinner = spinner();
648
+ this.currentSpinner = planSpinner; // Store reference to stop when progress starts
649
+ planSpinner.start(chalk.blueBright('šŸ“‹ Starting planning phase...'));
650
+ try {
651
+ // Import PlanMode
652
+ const { PlanMode } = await import('./modes/planMode.js');
653
+ const planMode = new PlanMode(this.agent, {
654
+ requirePlanning: true,
655
+ enforceSequential: true,
656
+ showPlanningFeedback: true
657
+ });
658
+ // Create a UI-connected event callback that handles both planning and streaming events
659
+ const uiEventCallback = (event) => {
660
+ // Handle planning-specific events
661
+ this.handleStreamingEvent(event);
662
+ // Handle regular streaming events too
663
+ switch (event.type) {
664
+ case 'thinking_start':
665
+ planSpinner.message(chalk.dim('Thinking...'));
666
+ break;
667
+ case 'content_chunk':
668
+ // For planning phase, we might not want to show content chunks immediately
669
+ break;
670
+ case 'tool_call_start':
671
+ planSpinner.message(chalk.yellow(`Using ${event.toolName}...`));
672
+ break;
673
+ case 'tool_call_progress':
674
+ if (event.toolProgress) {
675
+ const { percentage, operation, details } = event.toolProgress;
676
+ const progressBar = this.createToolProgressBar(percentage);
677
+ let message = `${operation} ${Math.round(percentage)}% ${progressBar}`;
678
+ if (details) {
679
+ message += ` (${details})`;
680
+ }
681
+ planSpinner.message(chalk.cyan(message));
682
+ }
683
+ break;
684
+ case 'tool_call_result':
685
+ if (event.toolMetadata) {
686
+ const message = formatToolMetadata(event.toolName || 'tool', event.toolMetadata);
687
+ planSpinner.message(chalk.green(message));
688
+ }
689
+ else {
690
+ planSpinner.message(chalk.dim('Processing...'));
691
+ }
692
+ break;
693
+ case 'complete':
694
+ planSpinner.stop();
695
+ break;
696
+ case 'error':
697
+ planSpinner.stop(chalk.red(`Error: ${event.error}`));
698
+ break;
699
+ }
700
+ };
701
+ if (planOnly) {
702
+ // Only generate plan, don't execute
703
+ const planningResult = await planMode.planningPhase(task, uiEventCallback);
704
+ planSpinner.stop();
705
+ if (!planningResult.success) {
706
+ throw new Error(`Planning failed: ${planningResult.error}`);
707
+ }
708
+ return `šŸ“‹ Plan generated successfully with ${planningResult.todos.length} steps:\n\n` +
709
+ planningResult.todos.map((todo, i) => `${i + 1}. ${todo.content}`).join('\n') +
710
+ `\n\nQuality Score: ${planningResult.qualityScore}/100\n` +
711
+ (planningResult.suggestions.length > 0 ?
712
+ `\nSuggestions:\n${planningResult.suggestions.map((s) => `• ${s}`).join('\n')}` : '') +
713
+ `\n\nšŸŽÆ **Plan-only mode**: Plan created. Use --plan flag (without --plan-only) to execute this plan.`;
714
+ }
715
+ // Full planning + execution
716
+ const result = await planMode.executePlannedTask(task, uiEventCallback);
717
+ planSpinner.stop();
718
+ // Show success message with token usage
719
+ const stats = this.agent.getStats();
720
+ const summaryParts = [];
721
+ if (stats.totalTokens > 0) {
722
+ summaryParts.push(`${formatTokens(stats.totalTokens)} tokens`);
723
+ }
724
+ if (stats.estimatedTotalCost > 0) {
725
+ summaryParts.push(`${formatCost(stats.estimatedTotalCost)}`);
726
+ }
727
+ if (summaryParts.length > 0) {
728
+ console.log(chalk.dim(`${summaryParts.join(' • ')}\n`));
729
+ }
730
+ return result;
731
+ }
732
+ catch (error) {
733
+ planSpinner.stop(chalk.red('Planning failed'));
734
+ throw error;
735
+ }
736
+ }
737
+ /**
738
+ * Check if we should show detailed information for a tool
739
+ */
740
+ shouldShowDetails(toolName) {
741
+ // Show details for these tools when they have interesting information
742
+ return ['read_file', 'list_directory', 'execute_command'].includes(toolName);
743
+ }
744
+ /**
745
+ * Show additional tool details
746
+ */
747
+ showToolDetails(toolName, metadata) {
748
+ if (!metadata || !metadata.success)
749
+ return;
750
+ let details = '';
751
+ switch (toolName) {
752
+ case 'read_file':
753
+ if (metadata.contentPreview) {
754
+ details = chalk.dim(`Preview:\n${metadata.contentPreview}`);
755
+ }
756
+ break;
757
+ case 'list_directory':
758
+ if (metadata.contentPreview && metadata.contentPreview !== 'Empty directory') {
759
+ // Parse the content preview and format each item on a new line
760
+ const preview = metadata.contentPreview;
761
+ if (preview.includes(' +') && preview.includes(' more')) {
762
+ // Extract items and remaining count: "item1, item2, item3 +15 more"
763
+ const [itemsStr, remainingStr] = preview.split(' +');
764
+ const items = itemsStr.split(', ');
765
+ // Format with each item on a new line
766
+ const formattedItems = items.map(item => ` ${item}`).join('\n');
767
+ details = chalk.dim(`${formattedItems}\n +${remainingStr}`);
768
+ }
769
+ else {
770
+ // Fallback for simple lists without truncation
771
+ const items = preview.split(', ');
772
+ details = chalk.dim(items.map(item => ` ${item}`).join('\n'));
773
+ }
774
+ }
775
+ break;
776
+ case 'execute_command':
777
+ if (metadata.outputPreview && metadata.outputPreview !== 'No output') {
778
+ details = chalk.dim(`Output:\n${metadata.outputPreview}`);
779
+ }
780
+ break;
781
+ }
782
+ if (details) {
783
+ console.log(chalk.dim(' ' + details.replace(/\n/g, '\n ')));
784
+ }
785
+ }
786
+ /**
787
+ * Show task completion summary with token usage
788
+ */
789
+ showTaskSummary(toolCallCount) {
790
+ const stats = this.agent.getStats();
791
+ const summaryParts = [];
792
+ if (toolCallCount > 0) {
793
+ summaryParts.push(`Used ${toolCallCount} tool${toolCallCount > 1 ? 's' : ''}`);
794
+ }
795
+ if (stats.totalTokens > 0) {
796
+ summaryParts.push(`${formatTokens(stats.totalTokens)} tokens (${formatTokens(stats.inputTokens)} in, ${formatTokens(stats.outputTokens)} out)`);
797
+ }
798
+ if (stats.estimatedTotalCost > 0) {
799
+ summaryParts.push(`${formatCost(stats.estimatedTotalCost)} estimated cost`);
800
+ }
801
+ if (summaryParts.length > 0) {
802
+ note(summaryParts.join(' • '), 'Task Summary');
803
+ }
804
+ }
805
+ /**
806
+ * Show detailed execution steps breakdown
807
+ */
808
+ showExecutionSteps(steps) {
809
+ const stepLines = [];
810
+ stepLines.push(chalk.yellow('Execution Steps:'));
811
+ for (const step of steps) {
812
+ const duration = step.duration ? `${step.duration}ms` : 'ongoing';
813
+ if (step.type === 'llm_call') {
814
+ const tokenInfo = step.tokenUsage
815
+ ? ` (${formatTokenUsageSummary(step.tokenUsage)})`
816
+ : '';
817
+ // Create descriptive label based on LLM context
818
+ let llmLabel = 'LLM Call';
819
+ if (step.llmContext === 'initial_input') {
820
+ llmLabel = 'Processing Input';
821
+ }
822
+ else if (step.llmContext === 'processing_tool_result') {
823
+ llmLabel = 'Processing Tool Output';
824
+ }
825
+ else if (step.llmContext === 'final_response') {
826
+ llmLabel = 'Final Reasoning';
827
+ }
828
+ stepLines.push(` ${chalk.cyan(`${step.stepNumber}.`)} ${llmLabel} - ${chalk.dim(duration)}${tokenInfo}`);
829
+ }
830
+ else {
831
+ const success = step.toolSuccess !== undefined
832
+ ? (step.toolSuccess ? chalk.green('āœ“') : chalk.red('āœ—'))
833
+ : chalk.yellow('?');
834
+ stepLines.push(` ${chalk.cyan(`${step.stepNumber}.`)} ${success} ${step.toolName} - ${chalk.dim(duration)}`);
835
+ }
836
+ }
837
+ note(stepLines.join('\n'), 'Step Details');
838
+ }
839
+ /**
840
+ * Handle todo update events
841
+ */
842
+ handleTodoUpdate(event) {
843
+ // Update internal state
844
+ this.todoPanel.update(event.todos);
845
+ // Only show progress tracker updates when NOT in active planning phase
846
+ // This prevents duplicate progress displays during context-aware planning
847
+ if (!this.activePlanningPhase) {
848
+ this.progressTracker.updateTodos(event.todos, event);
849
+ // Show visual feedback based on change type
850
+ if (event.changeType === 'create' && event.todos.length > 0) {
851
+ this.progressTracker.showPlanningComplete(event.todos.length);
852
+ }
853
+ }
854
+ else {
855
+ // During planning phase, update internal state but suppress visual feedback
856
+ this.progressTracker.updateTodos(event.todos, event, true);
857
+ }
858
+ }
859
+ /**
860
+ * Enable plan mode for structured planning
861
+ */
862
+ enablePlanMode() {
863
+ this.planMode = true;
864
+ // Remove redundant messages - UI will show plan mode status in the configuration display
865
+ }
866
+ /**
867
+ * Disable plan mode
868
+ */
869
+ disablePlanMode() {
870
+ this.planMode = false;
871
+ this.progressTracker.stop();
872
+ }
873
+ /**
874
+ * Show current todo status
875
+ */
876
+ async showTodos() {
877
+ const _todos = this.todoPanel.getTodos();
878
+ if (_todos.length === 0) {
879
+ note('No todos found. Use a planning task to create todos automatically.', 'šŸ“‹ Todo Status');
880
+ return;
881
+ }
882
+ const todoDisplay = this.todoPanel.render();
883
+ note(todoDisplay, 'šŸ“‹ Current Todo List');
884
+ }
885
+ /**
886
+ * Show planning phase welcome
887
+ */
888
+ showPlanningWelcome() {
889
+ if (this.planMode) {
890
+ console.log(chalk.cyan('šŸ’” Plan mode features: structured planning, progress tracking, sequential execution'));
891
+ console.log('');
892
+ }
893
+ }
894
+ /**
895
+ * Show task welcome with planning context
896
+ */
897
+ showTaskWelcome(task) {
898
+ intro(chalk.cyan('šŸ¤– CodeMie Native Agent'));
899
+ if (this.planMode) {
900
+ console.log(chalk.cyan('ā—‡ Task Execution'));
901
+ console.log(` Task: ${chalk.yellow(task)}`);
902
+ console.log(` Mode: ${chalk.cyan('Plan Mode')} - Structured planning enabled`);
903
+ console.log('');
904
+ this.showPlanningWelcome();
905
+ }
906
+ else {
907
+ console.log(chalk.cyan('ā—‡ Task Execution'));
908
+ console.log(` Task: ${chalk.yellow(task)}`);
909
+ console.log('');
910
+ }
911
+ }
912
+ /**
913
+ * Show task completion with todo summary
914
+ */
915
+ showTaskComplete() {
916
+ const _todos = this.todoPanel.getTodos();
917
+ const progressInfo = this.todoPanel.getProgressInfo();
918
+ if (progressInfo && progressInfo.total > 0) {
919
+ const stats = {
920
+ tasksCompleted: progressInfo.completed,
921
+ totalTime: undefined // Could track this if needed
922
+ };
923
+ this.progressTracker.showOverallCompletion(stats);
924
+ // Show final todo status if relevant
925
+ if (progressInfo.completed === progressInfo.total) {
926
+ note('All planned tasks completed successfully! šŸŽ‰', 'āœ… Task Complete');
927
+ }
928
+ else if (progressInfo.completed > 0) {
929
+ note(`Completed ${progressInfo.completed}/${progressInfo.total} planned tasks`, 'šŸ“Š Progress Summary');
930
+ }
931
+ }
932
+ else {
933
+ outro(chalk.green('āœ… Task completed!'));
934
+ }
935
+ }
936
+ /**
937
+ * Show error with todo context
938
+ */
939
+ showError(error) {
940
+ const _todos = this.todoPanel.getTodos();
941
+ const progressInfo = this.todoPanel.getProgressInfo();
942
+ let contextInfo = '';
943
+ if (progressInfo?.currentTodo) {
944
+ contextInfo = `\nšŸ“ Error occurred while working on: ${progressInfo.currentTodo.content}`;
945
+ }
946
+ note(chalk.rgb(255, 120, 120)(`āŒ ${error}${contextInfo}`), 'Error');
947
+ }
948
+ /**
949
+ * Execute task respecting current mode settings
950
+ */
951
+ async executeTaskWithCurrentMode(task, images = []) {
952
+ if (this.planMode) {
953
+ // Use plan mode execution with confirmation
954
+ try {
955
+ const { PlanMode } = await import('./modes/planMode.js');
956
+ const planMode = new PlanMode(this.agent, {
957
+ requirePlanning: true,
958
+ enforceSequential: true,
959
+ showPlanningFeedback: true
960
+ });
961
+ // First, create the plan only - using UI-integrated planning
962
+ const planningResult = await planMode.planningPhase(task, (event) => {
963
+ // Handle planning-specific events with UI integration
964
+ this.handleStreamingEvent(event);
965
+ // Handle regular streaming events for spinner updates
966
+ switch (event.type) {
967
+ case 'thinking_start':
968
+ // Already handled in handleStreamingEvent
969
+ break;
970
+ case 'tool_call_start':
971
+ // Already handled in handleStreamingEvent
972
+ break;
973
+ case 'tool_call_result':
974
+ // Already handled in handleStreamingEvent
975
+ break;
976
+ }
977
+ });
978
+ if (!planningResult.success) {
979
+ this.showError(`Planning failed: ${planningResult.error}`);
980
+ return;
981
+ }
982
+ // Show the plan to the user with proper formatting
983
+ const formattedPlan = this.formatPlanForDisplay(planningResult.todos, planningResult.qualityScore);
984
+ note(formattedPlan, 'Planning Complete');
985
+ // Ask for confirmation
986
+ const shouldExecute = await text({
987
+ message: 'Execute this plan?',
988
+ placeholder: 'Type "yes" to execute, or "no" to cancel',
989
+ validate: (value) => {
990
+ const normalized = value.toLowerCase().trim();
991
+ if (!['yes', 'y', 'no', 'n'].includes(normalized)) {
992
+ return 'Please type "yes" or "no"';
993
+ }
994
+ }
995
+ });
996
+ if (isCancel(shouldExecute)) {
997
+ note('Plan execution cancelled by user', 'Cancelled');
998
+ return;
999
+ }
1000
+ const response = shouldExecute.toLowerCase().trim();
1001
+ if (['yes', 'y'].includes(response)) {
1002
+ // Execute the plan
1003
+ await planMode.executePlannedTask(task, (event) => {
1004
+ this.handleStreamingEvent(event);
1005
+ });
1006
+ }
1007
+ else {
1008
+ note('Plan execution cancelled by user', 'Cancelled');
1009
+ }
1010
+ }
1011
+ catch (error) {
1012
+ this.showError(error instanceof Error ? error.message : String(error));
1013
+ }
1014
+ }
1015
+ else {
1016
+ // Use direct execution
1017
+ await this.executeTaskWithUI(task, images);
1018
+ }
1019
+ }
1020
+ /**
1021
+ * Handle streaming events from plan mode execution
1022
+ */
1023
+ handleStreamingEvent(event) {
1024
+ switch (event.type) {
1025
+ case 'planning_start':
1026
+ this.activePlanningPhase = event.planningInfo?.phase || 'planning';
1027
+ // Don't duplicate the planning start message - handled by spinner
1028
+ break;
1029
+ case 'planning_complete':
1030
+ this.activePlanningPhase = null;
1031
+ console.log(chalk.green(`šŸ“‹ Plan created with ${event.planningInfo?.totalSteps || 0} steps`));
1032
+ break;
1033
+ case 'planning_progress':
1034
+ this.handlePlanningProgress(event.planningProgress);
1035
+ break;
1036
+ case 'planning_tool_call':
1037
+ this.handlePlanningToolCall(event.planningToolCall);
1038
+ break;
1039
+ case 'content_chunk':
1040
+ if (event.content) {
1041
+ process.stdout.write(event.content);
1042
+ }
1043
+ break;
1044
+ case 'todo_update':
1045
+ // Todo updates are handled automatically by the TodoStateManager
1046
+ // During planning phase, we suppress duplicate visual feedback
1047
+ break;
1048
+ case 'error':
1049
+ this.activePlanningPhase = null;
1050
+ this.showError(event.error || 'Unknown error');
1051
+ break;
1052
+ default:
1053
+ // Handle other event types silently
1054
+ break;
1055
+ }
1056
+ }
1057
+ /**
1058
+ * Handle planning progress streaming events
1059
+ */
1060
+ currentPhase = '';
1061
+ currentTool = '';
1062
+ currentToolCall = '';
1063
+ handlePlanningProgress(progressInfo) {
1064
+ if (!progressInfo)
1065
+ return;
1066
+ // Keep the spinner running and use it to display progress updates
1067
+ const phaseNames = {
1068
+ 'context_gathering': 'Discovery',
1069
+ 'task_analysis': 'Analysis',
1070
+ 'plan_generation': 'Planning',
1071
+ 'plan_validation': 'Validation'
1072
+ };
1073
+ this.currentPhase = phaseNames[progressInfo.phase] || 'Planning';
1074
+ this.updateGlobalProgress(progressInfo.overallProgress || 0);
1075
+ }
1076
+ /**
1077
+ * Handle planning tool call events
1078
+ */
1079
+ handlePlanningToolCall(toolInfo) {
1080
+ if (!toolInfo)
1081
+ return;
1082
+ const toolNames = {
1083
+ 'list_directory': 'Exploring',
1084
+ 'read_file': 'Reading',
1085
+ 'execute_command': 'Executing',
1086
+ 'llm_analysis': 'Analyzing',
1087
+ 'llm_plan_generation': 'Generating Plan',
1088
+ 'plan_validation': 'Validating',
1089
+ 'analyze_dependencies': 'Dependencies'
1090
+ };
1091
+ this.currentTool = toolNames[toolInfo.toolName] || 'Tool';
1092
+ // Format tool call details with arguments
1093
+ this.currentToolCall = this.formatToolCall(toolInfo.toolName, toolInfo.args);
1094
+ // Update progress with current tool info
1095
+ this.updateGlobalProgress();
1096
+ }
1097
+ /**
1098
+ * Format tool call with arguments for display
1099
+ */
1100
+ formatToolCall(toolName, args) {
1101
+ if (!args || Object.keys(args).length === 0) {
1102
+ return toolName;
1103
+ }
1104
+ // Extract key arguments for concise display
1105
+ const formatArg = (key, value) => {
1106
+ if (typeof value === 'string') {
1107
+ // Truncate long strings and show just the relevant part
1108
+ if (key === 'path' || key === 'directory') {
1109
+ // Show just the last part of paths
1110
+ const parts = value.split('/');
1111
+ return parts[parts.length - 1] || value;
1112
+ }
1113
+ if (key === 'command') {
1114
+ // Show first word of commands
1115
+ return value.split(' ')[0];
1116
+ }
1117
+ if (value.length > 20) {
1118
+ return value.substring(0, 17) + '...';
1119
+ }
1120
+ return value;
1121
+ }
1122
+ if (Array.isArray(value)) {
1123
+ return `[${value.length} items]`;
1124
+ }
1125
+ if (typeof value === 'object') {
1126
+ return '{...}';
1127
+ }
1128
+ return String(value);
1129
+ };
1130
+ // Pick the most relevant arguments to show
1131
+ const relevantKeys = ['path', 'directory', 'file', 'command', 'query', 'pattern'];
1132
+ const argsToShow = [];
1133
+ for (const key of relevantKeys) {
1134
+ if (key in args) {
1135
+ argsToShow.push(formatArg(key, args[key]));
1136
+ if (argsToShow.length >= 2)
1137
+ break; // Show max 2 args
1138
+ }
1139
+ }
1140
+ // If no relevant keys found, show first few keys
1141
+ if (argsToShow.length === 0) {
1142
+ const keys = Object.keys(args).slice(0, 2);
1143
+ for (const key of keys) {
1144
+ argsToShow.push(formatArg(key, args[key]));
1145
+ }
1146
+ }
1147
+ return argsToShow.length > 0 ? `${toolName} (${argsToShow.join(', ')})` : toolName;
1148
+ }
1149
+ /**
1150
+ * Update global progress display
1151
+ */
1152
+ currentProgress = 0;
1153
+ updateGlobalProgress(progress) {
1154
+ if (progress !== undefined) {
1155
+ this.currentProgress = progress;
1156
+ }
1157
+ const progressBar = this.createProgressBar(this.currentProgress);
1158
+ let displayText = `Progress ${Math.round(this.currentProgress)}% ${progressBar}`;
1159
+ // Show tool call details if available, otherwise fall back to tool name or phase
1160
+ // This provides detailed info like "(Discovery, calling list_dir (src))"
1161
+ if (this.currentToolCall) {
1162
+ displayText += ` (${this.currentPhase}, calling ${this.currentToolCall})`;
1163
+ }
1164
+ else if (this.currentTool) {
1165
+ displayText += ` (${this.currentTool})`;
1166
+ }
1167
+ else if (this.currentPhase) {
1168
+ displayText += ` (${this.currentPhase})`;
1169
+ }
1170
+ // Use clack-style spinner update for reliable real-time display
1171
+ if (this.currentSpinner) {
1172
+ this.currentSpinner.message(displayText);
1173
+ }
1174
+ else {
1175
+ // Fallback to direct output if no spinner
1176
+ process.stdout.write('\r' + displayText);
1177
+ }
1178
+ // Only add newline when planning is completely finished (100%)
1179
+ if (this.currentProgress >= 100) {
1180
+ if (this.currentSpinner) {
1181
+ this.currentSpinner.stop(displayText);
1182
+ this.currentSpinner = undefined;
1183
+ }
1184
+ else {
1185
+ process.stdout.write('\n');
1186
+ }
1187
+ this.currentPhase = '';
1188
+ this.currentTool = '';
1189
+ this.currentToolCall = '';
1190
+ }
1191
+ }
1192
+ /**
1193
+ * Create a simple progress bar
1194
+ */
1195
+ createProgressBar(percentage, width = 20) {
1196
+ const filled = Math.round((percentage / 100) * width);
1197
+ const empty = width - filled;
1198
+ const filledBar = chalk.cyan('ā–ˆ'.repeat(filled));
1199
+ const emptyBar = chalk.dim('ā–‘'.repeat(empty));
1200
+ return `[${filledBar}${emptyBar}]`;
1201
+ }
1202
+ /**
1203
+ * Create a tool progress bar (smaller for inline use)
1204
+ */
1205
+ createToolProgressBar(percentage, width = 12) {
1206
+ const filled = Math.round((percentage / 100) * width);
1207
+ const empty = width - filled;
1208
+ const filledBar = chalk.green('ā–ˆ'.repeat(filled));
1209
+ const emptyBar = chalk.dim('ā–‘'.repeat(empty));
1210
+ return `[${filledBar}${emptyBar}]`;
1211
+ }
1212
+ /**
1213
+ * Handle hotkey actions
1214
+ */
1215
+ handleHotkey(action) {
1216
+ switch (action) {
1217
+ case 'toggle-plan-mode':
1218
+ this.togglePlanMode();
1219
+ break;
1220
+ case 'show-todos':
1221
+ this.showTodosHotkey();
1222
+ break;
1223
+ case 'show-help':
1224
+ this.showHotkeyHelp();
1225
+ break;
1226
+ case 'show-status':
1227
+ this.showModeStatus();
1228
+ break;
1229
+ default:
1230
+ console.log(chalk.red(`\nāŒ Unknown hotkey action: ${action}`));
1231
+ break;
1232
+ }
1233
+ }
1234
+ /**
1235
+ * Toggle plan mode on/off
1236
+ */
1237
+ togglePlanMode() {
1238
+ const wasEnabled = this.planMode;
1239
+ if (this.planMode) {
1240
+ this.disablePlanMode();
1241
+ }
1242
+ else {
1243
+ this.enablePlanMode();
1244
+ }
1245
+ // Show enhanced visual feedback
1246
+ this.showModeChangeNotification(wasEnabled, this.planMode);
1247
+ }
1248
+ /**
1249
+ * Show enhanced visual feedback for mode changes
1250
+ */
1251
+ showModeChangeNotification(wasEnabled, nowEnabled) {
1252
+ const modeIcon = nowEnabled ? 'šŸ“‹' : '⚔';
1253
+ const statusText = nowEnabled ? 'enabled' : 'disabled';
1254
+ // Simple, concise mode change message
1255
+ console.log(chalk.blueBright(`\n${modeIcon} Plan mode ${statusText}`));
1256
+ // Start progress tracking when plan mode is enabled (but don't show 0/0 progress yet)
1257
+ if (nowEnabled) {
1258
+ this.progressTracker.start();
1259
+ }
1260
+ }
1261
+ /**
1262
+ * Show todos via hotkey (non-blocking)
1263
+ */
1264
+ showTodosHotkey() {
1265
+ const _todos = this.todoPanel.getTodos();
1266
+ if (_todos.length === 0) {
1267
+ console.log(chalk.dim('\nšŸ“‹ No todos found'));
1268
+ }
1269
+ else {
1270
+ const todoDisplay = this.todoPanel.render();
1271
+ console.log(`\n${todoDisplay}`);
1272
+ }
1273
+ }
1274
+ /**
1275
+ * Show hotkey help
1276
+ */
1277
+ showHotkeyHelp() {
1278
+ const helpText = `
1279
+ ${chalk.bold.cyan('šŸ”„ Interactive Mode Hotkeys')}
1280
+
1281
+ Mode Control:
1282
+ ${chalk.yellow('Ctrl+P')} Toggle plan mode on/off
1283
+ ${chalk.yellow('Ctrl+S')} Show current mode status
1284
+
1285
+ Todo Management:
1286
+ ${chalk.yellow('Ctrl+T')} Show current todo list and progress
1287
+
1288
+ General:
1289
+ ${chalk.yellow('Ctrl+H')} Show this help
1290
+ ${chalk.yellow('Tab')} Insert image from clipboard
1291
+ ${chalk.yellow('Ctrl+C')} Cancel input / Exit
1292
+
1293
+ Input Controls:
1294
+ ${chalk.yellow('Enter')} Send message
1295
+ ${chalk.yellow('Shift+Enter')} New line (multiline input)
1296
+
1297
+ Chat Commands:
1298
+ ${chalk.yellow('/help')} Show chat commands
1299
+ ${chalk.yellow('/todos')} Show detailed todo information
1300
+ ${chalk.yellow('/stats')} Show agent statistics
1301
+ ${chalk.yellow('/exit')} Exit the session
1302
+ `;
1303
+ console.log(helpText);
1304
+ }
1305
+ /**
1306
+ * Show current mode status
1307
+ */
1308
+ showModeStatus() {
1309
+ const _todos = this.todoPanel.getTodos();
1310
+ const progressInfo = this.todoPanel.getProgressInfo();
1311
+ let statusText = chalk.bold.blueBright('\nšŸ“Š Current Status\n');
1312
+ // Mode information
1313
+ statusText += `Mode: ${this.planMode ?
1314
+ chalk.green('Plan Mode (structured todos)') :
1315
+ chalk.yellow('Direct Mode (immediate execution)')}\n`;
1316
+ // Todo information
1317
+ if (progressInfo && progressInfo.total > 0) {
1318
+ statusText += `Todos: ${progressInfo.completed}/${progressInfo.total} completed (${progressInfo.percentage}%)\n`;
1319
+ if (progressInfo.currentTodo) {
1320
+ statusText += `Current: ${chalk.cyan(progressInfo.currentTodo.content)}\n`;
1321
+ }
1322
+ }
1323
+ else {
1324
+ statusText += 'Todos: None active\n';
1325
+ }
1326
+ // Agent status
1327
+ const agentStats = this.agent.getStats();
1328
+ if (agentStats) {
1329
+ statusText += `Session: ${agentStats.toolCalls} tool calls, ${agentStats.llmCalls} LLM calls\n`;
1330
+ }
1331
+ console.log(statusText);
1332
+ }
1333
+ /**
1334
+ * Format plan for display with proper text wrapping
1335
+ */
1336
+ formatPlanForDisplay(todos, qualityScore) {
1337
+ const maxWidth = Math.min(process.stdout.columns - 10, 100); // Leave some margin
1338
+ let formatted = `šŸ“‹ **Plan Created** (${todos.length} steps):\n\n`;
1339
+ todos.forEach((todo, index) => {
1340
+ const stepNumber = `${index + 1}. `;
1341
+ const content = todo.content;
1342
+ // Wrap long lines properly
1343
+ const wrappedContent = this.wrapText(content, maxWidth - stepNumber.length);
1344
+ const lines = wrappedContent.split('\n');
1345
+ // First line with step number
1346
+ formatted += stepNumber + lines[0] + '\n';
1347
+ // Subsequent lines indented to align with content
1348
+ for (let i = 1; i < lines.length; i++) {
1349
+ formatted += ' '.repeat(stepNumber.length) + lines[i] + '\n';
1350
+ }
1351
+ // Add spacing between steps
1352
+ if (index < todos.length - 1) {
1353
+ formatted += '\n';
1354
+ }
1355
+ });
1356
+ formatted += `\n\nQuality Score: ${qualityScore}/100`;
1357
+ return formatted;
1358
+ }
1359
+ /**
1360
+ * Wrap text to specified width
1361
+ */
1362
+ wrapText(text, maxWidth) {
1363
+ if (text.length <= maxWidth) {
1364
+ return text;
1365
+ }
1366
+ const words = text.split(' ');
1367
+ const lines = [];
1368
+ let currentLine = '';
1369
+ for (const word of words) {
1370
+ // If adding this word would exceed the width
1371
+ if (currentLine.length + word.length + 1 > maxWidth) {
1372
+ if (currentLine) {
1373
+ lines.push(currentLine.trim());
1374
+ currentLine = word;
1375
+ }
1376
+ else {
1377
+ // Single word is longer than maxWidth, force break
1378
+ lines.push(word);
1379
+ }
1380
+ }
1381
+ else {
1382
+ if (currentLine) {
1383
+ currentLine += ' ' + word;
1384
+ }
1385
+ else {
1386
+ currentLine = word;
1387
+ }
1388
+ }
1389
+ }
1390
+ if (currentLine) {
1391
+ lines.push(currentLine.trim());
1392
+ }
1393
+ return lines.join('\n');
1394
+ }
1395
+ /**
1396
+ * Cleanup resources
1397
+ */
1398
+ dispose() {
1399
+ if (this.currentSpinner) {
1400
+ this.currentSpinner.stop();
1401
+ this.currentSpinner = undefined;
1402
+ }
1403
+ // Clean up todo tracking
1404
+ this.progressTracker.stop();
1405
+ TodoStateManager.removeEventCallback(this.handleTodoUpdate.bind(this));
1406
+ }
1407
+ }
1408
+ //# sourceMappingURL=ui.js.map