@xagent-ai/cli 1.2.2 → 1.3.0

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 (568) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/workflows/ci.yml +72 -0
  4. package/.github/workflows/release.yml +109 -0
  5. package/.gitmodules +3 -3
  6. package/README.md +326 -280
  7. package/README_CN.md +325 -279
  8. package/dist/ai-client/factory.d.ts +52 -0
  9. package/dist/ai-client/factory.d.ts.map +1 -0
  10. package/dist/ai-client/factory.js +132 -0
  11. package/dist/ai-client/factory.js.map +1 -0
  12. package/dist/ai-client/index.d.ts +20 -0
  13. package/dist/ai-client/index.d.ts.map +1 -0
  14. package/dist/ai-client/index.js +49 -0
  15. package/dist/ai-client/index.js.map +1 -0
  16. package/dist/ai-client/providers/anthropic.d.ts +57 -0
  17. package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
  18. package/dist/ai-client/providers/anthropic.js +400 -0
  19. package/dist/ai-client/providers/anthropic.js.map +1 -0
  20. package/dist/ai-client/providers/openai.d.ts +57 -0
  21. package/dist/ai-client/providers/openai.d.ts.map +1 -0
  22. package/dist/ai-client/providers/openai.js +286 -0
  23. package/dist/ai-client/providers/openai.js.map +1 -0
  24. package/dist/ai-client/providers/remote.d.ts +111 -0
  25. package/dist/ai-client/providers/remote.d.ts.map +1 -0
  26. package/dist/ai-client/providers/remote.js +351 -0
  27. package/dist/ai-client/providers/remote.js.map +1 -0
  28. package/dist/ai-client/registry.d.ts +51 -0
  29. package/dist/ai-client/registry.d.ts.map +1 -0
  30. package/dist/ai-client/registry.js +81 -0
  31. package/dist/ai-client/registry.js.map +1 -0
  32. package/dist/ai-client/types.d.ts +260 -0
  33. package/dist/ai-client/types.d.ts.map +1 -0
  34. package/dist/ai-client/types.js +73 -0
  35. package/dist/ai-client/types.js.map +1 -0
  36. package/dist/ai-client-factory.d.ts +62 -0
  37. package/dist/ai-client-factory.d.ts.map +1 -0
  38. package/dist/ai-client-factory.js +157 -0
  39. package/dist/ai-client-factory.js.map +1 -0
  40. package/dist/auth.d.ts +23 -1
  41. package/dist/auth.d.ts.map +1 -1
  42. package/dist/auth.js +160 -168
  43. package/dist/auth.js.map +1 -1
  44. package/dist/cancellation.d.ts +5 -4
  45. package/dist/cancellation.d.ts.map +1 -1
  46. package/dist/cancellation.js +55 -32
  47. package/dist/cancellation.js.map +1 -1
  48. package/dist/checkpoint.d.ts +1 -1
  49. package/dist/checkpoint.d.ts.map +1 -1
  50. package/dist/checkpoint.js +2 -2
  51. package/dist/checkpoint.js.map +1 -1
  52. package/dist/cli.js +626 -13
  53. package/dist/cli.js.map +1 -1
  54. package/dist/config.d.ts +10 -4
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +62 -25
  57. package/dist/config.js.map +1 -1
  58. package/dist/context-compressor.d.ts +81 -16
  59. package/dist/context-compressor.d.ts.map +1 -1
  60. package/dist/context-compressor.js +712 -153
  61. package/dist/context-compressor.js.map +1 -1
  62. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  63. package/dist/gui-subagent/action-parser/actionParser.js +4 -2
  64. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  65. package/dist/gui-subagent/agent/gui-agent.d.ts +29 -2
  66. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  67. package/dist/gui-subagent/agent/gui-agent.js +87 -45
  68. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  69. package/dist/gui-subagent/index.d.ts +16 -1
  70. package/dist/gui-subagent/index.d.ts.map +1 -1
  71. package/dist/gui-subagent/index.js +4 -0
  72. package/dist/gui-subagent/index.js.map +1 -1
  73. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
  74. package/dist/gui-subagent/operator/base-operator.js +0 -1
  75. package/dist/gui-subagent/operator/base-operator.js.map +1 -1
  76. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  77. package/dist/gui-subagent/operator/computer-operator.js +29 -8
  78. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  79. package/dist/gui-subagent/types/actions.d.ts +1 -1
  80. package/dist/gui-subagent/types/actions.d.ts.map +1 -1
  81. package/dist/gui-subagent/types/actions.js +0 -1
  82. package/dist/gui-subagent/types/actions.js.map +1 -1
  83. package/dist/gui-subagent/types/operator.d.ts +1 -1
  84. package/dist/gui-subagent/types/operator.d.ts.map +1 -1
  85. package/dist/index.d.ts +1 -2
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/input-processor.d.ts.map +1 -1
  90. package/dist/input-processor.js +6 -3
  91. package/dist/input-processor.js.map +1 -1
  92. package/dist/mcp.d.ts +5 -0
  93. package/dist/mcp.d.ts.map +1 -1
  94. package/dist/mcp.js +81 -35
  95. package/dist/mcp.js.map +1 -1
  96. package/dist/ripgrep.d.ts +29 -0
  97. package/dist/ripgrep.d.ts.map +1 -0
  98. package/dist/ripgrep.js +292 -0
  99. package/dist/ripgrep.js.map +1 -0
  100. package/dist/session.d.ts +23 -7
  101. package/dist/session.d.ts.map +1 -1
  102. package/dist/session.js +624 -243
  103. package/dist/session.js.map +1 -1
  104. package/dist/shell.d.ts +33 -0
  105. package/dist/shell.d.ts.map +1 -0
  106. package/dist/shell.js +125 -0
  107. package/dist/shell.js.map +1 -0
  108. package/dist/skill-installer.d.ts +38 -0
  109. package/dist/skill-installer.d.ts.map +1 -0
  110. package/dist/skill-installer.js +447 -0
  111. package/dist/skill-installer.js.map +1 -0
  112. package/dist/skill-invoker.d.ts +7 -1
  113. package/dist/skill-invoker.d.ts.map +1 -1
  114. package/dist/skill-invoker.js +34 -13
  115. package/dist/skill-invoker.js.map +1 -1
  116. package/dist/skill-loader.d.ts +8 -3
  117. package/dist/skill-loader.d.ts.map +1 -1
  118. package/dist/skill-loader.js +46 -44
  119. package/dist/skill-loader.js.map +1 -1
  120. package/dist/skill-manager.d.ts +85 -0
  121. package/dist/skill-manager.d.ts.map +1 -0
  122. package/dist/skill-manager.js +340 -0
  123. package/dist/skill-manager.js.map +1 -0
  124. package/dist/slash-commands.d.ts +38 -1
  125. package/dist/slash-commands.d.ts.map +1 -1
  126. package/dist/slash-commands.js +912 -296
  127. package/dist/slash-commands.js.map +1 -1
  128. package/dist/smart-approval.d.ts.map +1 -1
  129. package/dist/smart-approval.js +67 -55
  130. package/dist/smart-approval.js.map +1 -1
  131. package/dist/system-prompt-generator.d.ts +6 -0
  132. package/dist/system-prompt-generator.d.ts.map +1 -1
  133. package/dist/system-prompt-generator.js +84 -34
  134. package/dist/system-prompt-generator.js.map +1 -1
  135. package/dist/terminal.d.ts +28 -0
  136. package/dist/terminal.d.ts.map +1 -0
  137. package/dist/terminal.js +82 -0
  138. package/dist/terminal.js.map +1 -0
  139. package/dist/tools.d.ts +23 -7
  140. package/dist/tools.d.ts.map +1 -1
  141. package/dist/tools.js +797 -437
  142. package/dist/tools.js.map +1 -1
  143. package/dist/truncate.d.ts +55 -0
  144. package/dist/truncate.d.ts.map +1 -0
  145. package/dist/truncate.js +130 -0
  146. package/dist/truncate.js.map +1 -0
  147. package/dist/types.d.ts +27 -9
  148. package/dist/types.d.ts.map +1 -1
  149. package/dist/update.d.ts.map +1 -1
  150. package/dist/update.js +17 -28
  151. package/dist/update.js.map +1 -1
  152. package/dist/workflow.d.ts +5 -1
  153. package/dist/workflow.d.ts.map +1 -1
  154. package/dist/workflow.js +60 -47
  155. package/dist/workflow.js.map +1 -1
  156. package/docs/architecture/mcp-integration-guide.md +304 -194
  157. package/docs/architecture/overview.md +169 -169
  158. package/docs/architecture/tool-system-design.md +134 -134
  159. package/docs/cli/commands.md +349 -238
  160. package/docs/smart-mode.md +281 -281
  161. package/docs/third-party-models.md +439 -439
  162. package/find-skills/SKILL.md +133 -0
  163. package/package.json +89 -90
  164. package/scripts/install-ripgrep.js +241 -0
  165. package/src/ai-client/factory.ts +151 -0
  166. package/src/ai-client/index.ts +61 -0
  167. package/src/ai-client/providers/anthropic.ts +466 -0
  168. package/src/ai-client/providers/openai.ts +342 -0
  169. package/src/ai-client/providers/remote.ts +436 -0
  170. package/src/ai-client/registry.ts +97 -0
  171. package/src/ai-client/types.ts +345 -0
  172. package/src/ai-client-factory.ts +204 -0
  173. package/src/auth.ts +663 -614
  174. package/src/cancellation.ts +205 -176
  175. package/src/checkpoint.ts +219 -219
  176. package/src/cli.ts +1406 -743
  177. package/src/config.ts +341 -297
  178. package/src/context-compressor.ts +982 -290
  179. package/src/conversation.ts +288 -288
  180. package/src/gui-subagent/action-parser/actionParser.ts +318 -315
  181. package/src/gui-subagent/action-parser/constants.ts +14 -14
  182. package/src/gui-subagent/action-parser/index.ts +8 -8
  183. package/src/gui-subagent/action-parser/types.ts +31 -31
  184. package/src/gui-subagent/agent/gui-agent.ts +1151 -1089
  185. package/src/gui-subagent/agent/index.ts +5 -5
  186. package/src/gui-subagent/index.ts +177 -163
  187. package/src/gui-subagent/operator/base-operator.ts +244 -245
  188. package/src/gui-subagent/operator/computer-operator.ts +540 -520
  189. package/src/gui-subagent/operator/index.ts +6 -6
  190. package/src/gui-subagent/types/actions.ts +260 -262
  191. package/src/gui-subagent/types/index.ts +6 -6
  192. package/src/gui-subagent/types/operator.ts +106 -106
  193. package/src/gui-subagent/utils.ts +51 -51
  194. package/src/index.ts +17 -18
  195. package/src/input-processor.ts +6 -3
  196. package/src/logger.ts +438 -438
  197. package/src/mcp.ts +730 -682
  198. package/src/memory.ts +344 -344
  199. package/src/ripgrep.ts +368 -0
  200. package/src/session-manager.ts +308 -308
  201. package/src/session.ts +948 -386
  202. package/src/shell.ts +133 -0
  203. package/src/skill-installer.ts +518 -0
  204. package/src/skill-invoker.ts +960 -935
  205. package/src/skill-loader.ts +501 -496
  206. package/src/skill-manager.ts +384 -0
  207. package/src/slash-commands.ts +2181 -1389
  208. package/src/smart-approval.ts +117 -73
  209. package/src/system-prompt-generator.ts +89 -34
  210. package/src/terminal.ts +96 -0
  211. package/src/theme.ts +738 -738
  212. package/src/tools.ts +1336 -773
  213. package/src/truncate.ts +173 -0
  214. package/src/types.ts +219 -198
  215. package/src/update.ts +22 -32
  216. package/src/workflow.ts +523 -508
  217. package/tsconfig.json +22 -22
  218. package/vitest.config.ts +19 -19
  219. package/dist/ai-client.d.ts +0 -86
  220. package/dist/ai-client.d.ts.map +0 -1
  221. package/dist/ai-client.js +0 -1372
  222. package/dist/ai-client.js.map +0 -1
  223. package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
  224. package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
  225. package/dist/gui-subagent/operator/browser-operator.js +0 -306
  226. package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
  227. package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
  228. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
  229. package/dist/gui-subagent/operator/desktop-operator.js +0 -527
  230. package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
  231. package/dist/hook.d.ts +0 -73
  232. package/dist/hook.d.ts.map +0 -1
  233. package/dist/hook.js +0 -156
  234. package/dist/hook.js.map +0 -1
  235. package/dist/input-history.d.ts +0 -24
  236. package/dist/input-history.d.ts.map +0 -1
  237. package/dist/input-history.js +0 -94
  238. package/dist/input-history.js.map +0 -1
  239. package/dist/keyboard-manager.d.ts +0 -151
  240. package/dist/keyboard-manager.d.ts.map +0 -1
  241. package/dist/keyboard-manager.js +0 -396
  242. package/dist/keyboard-manager.js.map +0 -1
  243. package/dist/print-system-prompt.d.ts +0 -2
  244. package/dist/print-system-prompt.d.ts.map +0 -1
  245. package/dist/print-system-prompt.js +0 -40
  246. package/dist/print-system-prompt.js.map +0 -1
  247. package/dist/remote-ai-client.d.ts +0 -104
  248. package/dist/remote-ai-client.d.ts.map +0 -1
  249. package/dist/remote-ai-client.js +0 -552
  250. package/dist/remote-ai-client.js.map +0 -1
  251. package/dist/sdk-output-adapter.d.ts +0 -232
  252. package/dist/sdk-output-adapter.d.ts.map +0 -1
  253. package/dist/sdk-output-adapter.js +0 -636
  254. package/dist/sdk-output-adapter.js.map +0 -1
  255. package/dist/sdk-session-v2.d.ts +0 -13
  256. package/dist/sdk-session-v2.d.ts.map +0 -1
  257. package/dist/sdk-session-v2.js +0 -46
  258. package/dist/sdk-session-v2.js.map +0 -1
  259. package/dist/sdk-session.d.ts +0 -13
  260. package/dist/sdk-session.d.ts.map +0 -1
  261. package/dist/sdk-session.js +0 -48
  262. package/dist/sdk-session.js.map +0 -1
  263. package/dist/test-boundary-conditions.d.ts.map +0 -1
  264. package/dist/test-boundary-conditions.js.map +0 -1
  265. package/dist/test-cancellation-fix.d.ts.map +0 -1
  266. package/dist/test-cancellation-fix.js.map +0 -1
  267. package/dist/test-input-history.d.ts.map +0 -1
  268. package/dist/test-input-history.js.map +0 -1
  269. package/dist/test-interaction-flow.d.ts.map +0 -1
  270. package/dist/test-interaction-flow.js.map +0 -1
  271. package/dist/test-quick.d.ts.map +0 -1
  272. package/dist/test-quick.js.map +0 -1
  273. package/dist/test-user-interaction.d.ts.map +0 -1
  274. package/dist/test-user-interaction.js.map +0 -1
  275. package/dist/tools/edit-diff.d.ts +0 -32
  276. package/dist/tools/edit-diff.d.ts.map +0 -1
  277. package/dist/tools/edit-diff.js +0 -185
  278. package/dist/tools/edit-diff.js.map +0 -1
  279. package/dist/tools/edit.d.ts +0 -11
  280. package/dist/tools/edit.d.ts.map +0 -1
  281. package/dist/tools/edit.js +0 -129
  282. package/dist/tools/edit.js.map +0 -1
  283. package/dist/unified-session.d.ts +0 -42
  284. package/dist/unified-session.d.ts.map +0 -1
  285. package/dist/unified-session.js +0 -271
  286. package/dist/unified-session.js.map +0 -1
  287. package/skills/.claude-plugin/marketplace.json +0 -45
  288. package/skills/README.md +0 -94
  289. package/skills/THIRD_PARTY_NOTICES.md +0 -405
  290. package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
  291. package/skills/skills/algorithmic-art/SKILL.md +0 -405
  292. package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
  293. package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
  294. package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
  295. package/skills/skills/brand-guidelines/SKILL.md +0 -73
  296. package/skills/skills/canvas-design/LICENSE.txt +0 -202
  297. package/skills/skills/canvas-design/SKILL.md +0 -130
  298. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  299. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  300. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  301. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  302. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  303. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  304. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  305. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  306. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  307. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  308. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  309. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  310. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  311. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  312. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  313. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  314. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  315. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  316. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  317. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  318. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  319. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  320. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  321. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  322. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  323. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  324. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  325. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  326. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  327. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  328. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  329. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  330. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  331. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  332. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  333. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  334. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  335. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  336. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  337. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  338. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  339. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  340. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  341. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  342. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  343. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  344. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  345. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  346. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  347. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  348. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  349. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  350. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  351. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  352. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  353. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  354. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  355. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  356. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  357. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  358. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  359. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  360. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  361. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  362. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  363. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  364. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  365. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  366. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  367. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  368. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  369. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  370. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  371. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  372. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  373. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  374. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  375. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  376. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  377. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  378. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  379. package/skills/skills/doc-coauthoring/SKILL.md +0 -375
  380. package/skills/skills/docx/LICENSE.txt +0 -30
  381. package/skills/skills/docx/SKILL.md +0 -197
  382. package/skills/skills/docx/docx-js.md +0 -350
  383. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  384. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  385. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  386. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  387. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  388. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  389. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  390. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  391. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  392. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  393. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  394. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  395. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  396. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  397. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  398. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  399. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  400. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  401. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  402. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  403. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  404. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  405. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  406. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  407. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  408. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  409. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  410. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  411. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  412. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  413. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  414. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  415. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  416. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  417. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  418. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  419. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  420. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  421. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  422. package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
  423. package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
  424. package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
  425. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  426. package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
  427. package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  428. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  429. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  430. package/skills/skills/docx/ooxml.md +0 -610
  431. package/skills/skills/docx/scripts/__init__.py +0 -1
  432. package/skills/skills/docx/scripts/document.py +0 -1276
  433. package/skills/skills/docx/scripts/templates/comments.xml +0 -3
  434. package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  435. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  436. package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
  437. package/skills/skills/docx/scripts/templates/people.xml +0 -3
  438. package/skills/skills/docx/scripts/utilities.py +0 -374
  439. package/skills/skills/frontend-design/LICENSE.txt +0 -177
  440. package/skills/skills/frontend-design/SKILL.md +0 -42
  441. package/skills/skills/internal-comms/LICENSE.txt +0 -202
  442. package/skills/skills/internal-comms/SKILL.md +0 -32
  443. package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
  444. package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
  445. package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
  446. package/skills/skills/internal-comms/examples/general-comms.md +0 -16
  447. package/skills/skills/mcp-builder/LICENSE.txt +0 -202
  448. package/skills/skills/mcp-builder/SKILL.md +0 -236
  449. package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
  450. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  451. package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  452. package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  453. package/skills/skills/mcp-builder/scripts/connections.py +0 -151
  454. package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
  455. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  456. package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
  457. package/skills/skills/pdf/LICENSE.txt +0 -30
  458. package/skills/skills/pdf/SKILL.md +0 -294
  459. package/skills/skills/pdf/forms.md +0 -205
  460. package/skills/skills/pdf/reference.md +0 -612
  461. package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  462. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  463. package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
  464. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  465. package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
  466. package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
  467. package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  468. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  469. package/skills/skills/pptx/LICENSE.txt +0 -30
  470. package/skills/skills/pptx/SKILL.md +0 -484
  471. package/skills/skills/pptx/html2pptx.md +0 -625
  472. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  473. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  474. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  475. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  476. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  477. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  478. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  479. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  480. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  481. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  482. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  483. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  484. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  485. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  486. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  487. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  488. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  489. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  490. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  491. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  492. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  493. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  494. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  495. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  496. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  497. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  498. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  499. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  500. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  501. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  502. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  503. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  504. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  505. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  506. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  507. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  508. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  509. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  510. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  511. package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
  512. package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
  513. package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
  514. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  515. package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  516. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  517. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  518. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  519. package/skills/skills/pptx/ooxml.md +0 -427
  520. package/skills/skills/pptx/scripts/html2pptx.js +0 -979
  521. package/skills/skills/pptx/scripts/inventory.py +0 -1020
  522. package/skills/skills/pptx/scripts/rearrange.py +0 -231
  523. package/skills/skills/pptx/scripts/replace.py +0 -385
  524. package/skills/skills/pptx/scripts/thumbnail.py +0 -450
  525. package/skills/skills/skill-creator/LICENSE.txt +0 -202
  526. package/skills/skills/skill-creator/SKILL.md +0 -356
  527. package/skills/skills/skill-creator/references/output-patterns.md +0 -82
  528. package/skills/skills/skill-creator/references/workflows.md +0 -28
  529. package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
  530. package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
  531. package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
  532. package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
  533. package/skills/skills/slack-gif-creator/SKILL.md +0 -254
  534. package/skills/skills/slack-gif-creator/core/easing.py +0 -234
  535. package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
  536. package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
  537. package/skills/skills/slack-gif-creator/core/validators.py +0 -136
  538. package/skills/skills/slack-gif-creator/requirements.txt +0 -4
  539. package/skills/skills/theme-factory/LICENSE.txt +0 -202
  540. package/skills/skills/theme-factory/SKILL.md +0 -59
  541. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  542. package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
  543. package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
  544. package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
  545. package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
  546. package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
  547. package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
  548. package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
  549. package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
  550. package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
  551. package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
  552. package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
  553. package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
  554. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
  555. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
  556. package/skills/skills/webapp-testing/LICENSE.txt +0 -202
  557. package/skills/skills/webapp-testing/SKILL.md +0 -96
  558. package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
  559. package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
  560. package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
  561. package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
  562. package/skills/skills/xlsx/LICENSE.txt +0 -30
  563. package/skills/skills/xlsx/SKILL.md +0 -289
  564. package/skills/skills/xlsx/recalc.py +0 -178
  565. package/skills/spec/agent-skills-spec.md +0 -3
  566. package/skills/template/SKILL.md +0 -6
  567. package/src/ai-client.ts +0 -1560
  568. package/src/remote-ai-client.ts +0 -664
@@ -1,1389 +1,2181 @@
1
- import inquirer from 'inquirer';
2
- import chalk from 'chalk';
3
- import ora from 'ora';
4
- import fs from 'fs/promises';
5
- import path from 'path';
6
- import { ExecutionMode, ChatMessage, InputType, ToolCall, Checkpoint, AgentConfig, CompressionConfig, AuthType } from './types.js';
7
- import { AIClient, Message, detectThinkingKeywords, getThinkingTokens } from './ai-client.js';
8
- import { getToolRegistry } from './tools.js';
9
- import { getAgentManager } from './agents.js';
10
- import { getMemoryManager, MemoryFile } from './memory.js';
11
- import { getMCPManager, MCPServer } from './mcp.js';
12
- import { getCheckpointManager } from './checkpoint.js';
13
- import { getConfigManager, ConfigManager } from './config.js';
14
- import { getLogger } from './logger.js';
15
- import { getContextCompressor, ContextCompressor, CompressionResult } from './context-compressor.js';
16
- import { getConversationManager, ConversationManager } from './conversation.js';
17
- import { icons, colors } from './theme.js';
18
- import { SystemPromptGenerator } from './system-prompt-generator.js';
19
- import { AuthService } from './auth.js';
20
-
21
- const logger = getLogger();
22
-
23
- export class SlashCommandHandler {
24
- private configManager: ConfigManager;
25
- private agentManager: any;
26
- private memoryManager: any;
27
- private mcpManager: any;
28
- private checkpointManager: any;
29
- private contextCompressor: ContextCompressor;
30
- private conversationManager: ConversationManager;
31
- private conversationHistory: ChatMessage[] = [];
32
- private onClearCallback: (() => void) | null = null;
33
- private onSystemPromptUpdate: (() => Promise<void>) | null = null;
34
-
35
- constructor() {
36
- this.configManager = getConfigManager(process.cwd());
37
- this.agentManager = getAgentManager(process.cwd());
38
- this.memoryManager = getMemoryManager(process.cwd());
39
- this.mcpManager = getMCPManager();
40
- this.checkpointManager = getCheckpointManager(process.cwd());
41
- this.contextCompressor = getContextCompressor();
42
- this.conversationManager = getConversationManager();
43
- }
44
-
45
- /**
46
- * Set callback for clearing conversation
47
- */
48
- setClearCallback(callback: () => void): void {
49
- this.onClearCallback = callback;
50
- }
51
-
52
- /**
53
- * Set callback for system prompt update
54
- */
55
- setSystemPromptUpdateCallback(callback: () => Promise<void>): void {
56
- this.onSystemPromptUpdate = callback;
57
- }
58
-
59
- /**
60
- * Set current conversation history (includes all user/assistant/tool messages)
61
- */
62
- setConversationHistory(messages: ChatMessage[]): void {
63
- this.conversationHistory = messages;
64
- }
65
-
66
- async handleCommand(input: string): Promise<boolean> {
67
- if (!input.startsWith('/')) {
68
- return false;
69
- }
70
-
71
- const [command, ...args] = input.slice(1).split(' ');
72
-
73
- switch (command.toLowerCase()) {
74
- case 'help':
75
- await this.showHelp();
76
- break;
77
- case 'init':
78
- await this.handleInit();
79
- break;
80
- case 'clear':
81
- await this.handleClear();
82
- break;
83
- case 'exit':
84
- case 'quit':
85
- await this.handleExit();
86
- break;
87
- case 'auth':
88
- await this.handleAuth();
89
- break;
90
- case 'login':
91
- await this.handleLogin();
92
- break;
93
- case 'mode':
94
- await this.handleMode(args);
95
- break;
96
- case 'think':
97
- await this.handleThink(args);
98
- break;
99
- case 'agents':
100
- await this.handleAgents(args);
101
- break;
102
- case 'mcp':
103
- await this.handleMcp(args);
104
- break;
105
- case 'vlm':
106
- await this.handleVlm();
107
- break;
108
- case 'memory':
109
- await this.handleMemory(args);
110
- break;
111
- case 'restore':
112
- await this.handleRestore(args);
113
- break;
114
- case 'tools':
115
- await this.handleToolsVerbose(args);
116
- break;
117
- case 'stats':
118
- await this.handleStats();
119
- break;
120
- case 'theme':
121
- await this.handleTheme();
122
- break;
123
- // case 'language':
124
- // await this.handleLanguage();
125
- // break;
126
- case 'about':
127
- await this.handleAbout();
128
- break;
129
- case 'compress':
130
- await this.handleCompress(args);
131
- break;
132
- default:
133
- logger.warn(`Unknown command: /${command}`, 'Type /help for available commands');
134
- }
135
-
136
- return true;
137
- }
138
-
139
- private async showHelp(): Promise<void> {
140
- const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80));
141
-
142
- console.log('');
143
- console.log(colors.primaryBright('╔════════════════════════════════════════════════════════════╗'));
144
- console.log(colors.primaryBright('║') + ' '.repeat(56) + colors.primaryBright('║'));
145
- console.log(' '.repeat(14) + colors.gradient('📚 XAGENT CLI Help') + ' '.repeat(31) + colors.primaryBright('║'));
146
- console.log(colors.primaryBright('║') + ' '.repeat(56) + colors.primaryBright('║'));
147
- console.log(colors.primaryBright('╚════════════════════════════════════════════════════════════╝'));
148
- console.log('');
149
-
150
- // Shortcuts
151
- console.log(colors.accent('Shortcuts'));
152
- console.log(colors.border(separator));
153
- console.log('');
154
- console.log(colors.textDim(` ${colors.accent('!')} - ${colors.textMuted('Enter bash mode')}`));
155
- console.log(colors.textDim(` ${colors.accent('/')} - ${colors.textMuted('Commands')}`));
156
- console.log(colors.textDim(` ${colors.accent('@')} - ${colors.textMuted('File paths')}`));
157
- console.log('');
158
-
159
- // Basic Commands
160
- this.showHelpCategory('Basic Commands', [
161
- {
162
- cmd: '/help [command]',
163
- desc: 'Show help information',
164
- detail: 'View all available commands or detailed description of specific command',
165
- example: '/help\n/help mode'
166
- },
167
- {
168
- cmd: '/clear',
169
- desc: 'Clear conversation history',
170
- detail: 'Clear all conversation records of current session, start new conversation',
171
- example: '/clear'
172
- },
173
- {
174
- cmd: '/exit',
175
- desc: 'Exit program',
176
- detail: 'Safely exit XAGENT CLI',
177
- example: '/exit'
178
- }
179
- ]);
180
-
181
- // Project Management
182
- this.showHelpCategory('Project Management', [
183
- {
184
- cmd: '/init',
185
- desc: 'Initialize project context',
186
- detail: 'Create XAGENT.md file in current directory, used to store project context information',
187
- example: '/init'
188
- },
189
- {
190
- cmd: '/memory [show|clear]',
191
- desc: 'Manage project memory',
192
- detail: 'View or clear memory (global, current, all, or filename)',
193
- example: '/memory show\n/memory clear\n/memory clear global\n/memory clear all'
194
- }
195
- ]);
196
-
197
- // Authentication & Configuration
198
- this.showHelpCategory('Authentication & Configuration', [
199
- {
200
- cmd: '/auth',
201
- desc: 'Configure authentication information',
202
- detail: 'Change or view current authentication configuration',
203
- example: '/auth'
204
- },
205
- {
206
- cmd: '/mode [mode]',
207
- desc: 'Switch approval mode',
208
- detail: 'Switch security approval mode for tool execution',
209
- example: '/mode\n/mode smart\n/mode yolo',
210
- modes: [
211
- 'yolo - Execute all operations without restriction',
212
- 'accept_edits - Automatically accept edit operations',
213
- 'plan - Plan before executing',
214
- 'default - Safe execution, requires confirmation',
215
- 'smart - Smart approval (recommended)'
216
- ]
217
- },
218
- {
219
- cmd: '/think [on|off|display]',
220
- desc: 'Control thinking mode',
221
- detail: 'Enable/disable AI thinking process display',
222
- example: '/think on\n/think off\n/think display compact'
223
- },
224
- // {
225
- // cmd: '/language [zh|en]',
226
- // desc: 'Switch language',
227
- // detail: 'Switch between Chinese and English interface',
228
- // example: '/language zh\n/language en'
229
- // },
230
- {
231
- cmd: '/theme',
232
- desc: 'Switch theme',
233
- detail: 'Change UI theme style',
234
- example: '/theme'
235
- }
236
- ]);
237
-
238
- // Feature Extensions
239
- this.showHelpCategory('Feature Extensions', [
240
- {
241
- cmd: '/agents [list|online|install|remove]',
242
- desc: 'Manage sub-agents',
243
- detail: 'View, install or remove specialized AI sub-agents',
244
- example: '/agents list\n/agents online\n/agents install explore-agent'
245
- },
246
- {
247
- cmd: '/mcp [list|add|remove|refresh]',
248
- desc: 'Manage MCP servers',
249
- detail: 'Manage Model Context Protocol servers',
250
- example: '/mcp list\n/mcp add server-name'
251
- },
252
- {
253
- cmd: '/vlm',
254
- desc: 'Configure VLM for GUI Agent',
255
- detail: 'Configure Vision-Language Model for browser/desktop automation',
256
- example: '/vlm'
257
- },
258
- {
259
- cmd: '/tools [verbose|simple]',
260
- desc: 'Manage tool display',
261
- detail: 'View available tools or switch tool call display mode',
262
- example: '/tools\n/tools verbose\n/tools simple'
263
- }
264
- ]);
265
-
266
- // Advanced Features
267
- this.showHelpCategory('Advanced Features', [
268
- {
269
- cmd: '/restore',
270
- desc: 'Restore from checkpoint',
271
- detail: 'Restore conversation state from historical checkpoints',
272
- example: '/restore'
273
- },
274
- {
275
- cmd: '/compress [on|off|max_message|max_token|exec]',
276
- desc: 'Manage context compression',
277
- detail: 'Configure compression settings or execute compression manually',
278
- example: '/compress\n/compress exec\n/compress on\n/compress max_message 50\n/compress max_token 1500000'
279
- },
280
- {
281
- cmd: '/stats',
282
- desc: 'Show session statistics',
283
- detail: 'View statistics information of current session',
284
- example: '/stats'
285
- },
286
- {
287
- cmd: '/about',
288
- desc: 'Show version information',
289
- detail: 'View version and related information of XAGENT CLI',
290
- example: '/about'
291
- }
292
- ]);
293
-
294
- // Keyboard Shortcuts
295
- console.log('');
296
- console.log(colors.border(separator));
297
- console.log(colors.primaryBright('Keyboard Shortcuts'));
298
- console.log(colors.border(separator));
299
- console.log('');
300
- console.log(colors.textMuted(' ESC - Cancel current operation'));
301
- console.log(colors.textMuted(' Ctrl+C - Exit program'));
302
- console.log('');
303
- }
304
-
305
- private showHelpCategory(title: string, commands: Array<{
306
- cmd: string;
307
- desc: string;
308
- detail: string;
309
- example: string;
310
- modes?: string[];
311
- }>): void {
312
- const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80));
313
-
314
- console.log('');
315
- console.log(colors.border(separator));
316
- console.log(colors.primaryBright(title));
317
- console.log(colors.border(separator));
318
- console.log('');
319
-
320
- commands.forEach(cmd => {
321
- console.log(colors.primaryBright(` ${cmd.cmd}`));
322
- console.log(colors.textDim(` ${cmd.desc}`));
323
- console.log(colors.textMuted(` ${cmd.detail}`));
324
-
325
- if (cmd.modes) {
326
- console.log(colors.textDim(` Available modes:`));
327
- cmd.modes.forEach(mode => {
328
- console.log(colors.textDim(` • ${mode}`));
329
- });
330
- }
331
-
332
- console.log(colors.accent(` Examples:`));
333
- cmd.example.split('\n').forEach(ex => {
334
- console.log(colors.codeText(` ${ex}`));
335
- });
336
- console.log('');
337
- });
338
- }
339
-
340
- private async handleInit(): Promise<void> {
341
- const spinner = ora('Initializing project...').start();
342
-
343
- try {
344
- await this.memoryManager.initializeProject(process.cwd());
345
- spinner.succeed('Project initialized successfully');
346
- } catch (error: any) {
347
- spinner.fail(`Initialization failed: ${error.message}`);
348
- }
349
- }
350
-
351
- private async handleClear(): Promise<void> {
352
- // Clear local conversation history
353
- this.conversationHistory = [];
354
-
355
- // Clear ConversationManager 中的当前对话
356
- await this.conversationManager.clearCurrentConversation();
357
-
358
- // Call callback to notify InteractiveSession 清空对话
359
- if (this.onClearCallback) {
360
- this.onClearCallback();
361
- }
362
-
363
- logger.success('Conversation history cleared', 'Start a new conversation');
364
- }
365
-
366
- private async handleExit(): Promise<void> {
367
- logger.info('Goodbye!', 'Thank you for using xAgent CLI');
368
- process.exit(0);
369
- }
370
-
371
- private async handleAuth(): Promise<void> {
372
- logger.section('Authentication Management');
373
-
374
- const { action } = await inquirer.prompt([
375
- {
376
- type: 'list',
377
- name: 'action',
378
- message: 'Select action:',
379
- choices: [
380
- { name: 'Change authentication method', value: 'change' },
381
- { name: 'Show current auth config', value: 'show' },
382
- { name: 'Back', value: 'back' }
383
- ]
384
- }
385
- ]);
386
-
387
- if (action === 'back') {
388
- return;
389
- }
390
-
391
- if (action === 'show') {
392
- const authConfig = this.configManager.getAuthConfig();
393
- logger.subsection('Current Authentication Configuration');
394
- console.log(JSON.stringify(authConfig, null, 2));
395
- } else if (action === 'change') {
396
- const { selectAuthType } = await inquirer.prompt([
397
- {
398
- type: 'confirm',
399
- name: 'selectAuthType',
400
- message: 'Do you want to change authentication type?',
401
- default: false
402
- }
403
- ]);
404
-
405
- if (selectAuthType) {
406
- logger.warn('Please restart xAgent CLI and run /auth again', 'Authentication changes require restart');
407
- }
408
- }
409
- }
410
-
411
- private async handleLogin(): Promise<void> {
412
- logger.section('Login to xAgent');
413
-
414
- const authConfig = this.configManager.getAuthConfig();
415
- const currentAuthType = authConfig.type;
416
-
417
- if (currentAuthType !== AuthType.OAUTH_XAGENT) {
418
- console.log(chalk.yellow('\n⚠️ Current authentication type is not OAuth xAgent.'));
419
- const { proceed } = await inquirer.prompt([
420
- {
421
- type: 'confirm',
422
- name: 'proceed',
423
- message: 'Do you want to switch to OAuth xAgent authentication?',
424
- default: false
425
- }
426
- ]);
427
-
428
- if (!proceed) {
429
- return;
430
- }
431
-
432
- // Switch to OAuth xAgent
433
- await this.configManager.setAuthConfig({
434
- selectedAuthType: AuthType.OAUTH_XAGENT,
435
- apiKey: '',
436
- refreshToken: '',
437
- baseUrl: ''
438
- });
439
- await this.configManager.save('global');
440
- console.log(chalk.green('✅ Switched to OAuth xAgent authentication.'));
441
- }
442
-
443
- console.log(chalk.cyan('\n🔐 Starting OAuth xAgent login...'));
444
- console.log(chalk.gray(' A browser will open for you to complete authentication.\n'));
445
-
446
- try {
447
- const authService = new AuthService({
448
- type: AuthType.OAUTH_XAGENT,
449
- apiKey: '',
450
- baseUrl: '',
451
- refreshToken: ''
452
- });
453
-
454
- const success = await authService.authenticate();
455
-
456
- if (success) {
457
- const newConfig = this.configManager.getAuthConfig();
458
- console.log(chalk.green('\n✅ Login successful!'));
459
- console.log(chalk.cyan(` Token saved to: ~/.xagent/settings.json`));
460
- console.log(chalk.gray(' You can now use xAgent CLI with remote AI services.\n'));
461
- } else {
462
- console.log(chalk.red('\n❌ Login failed or was cancelled.'));
463
- }
464
- } catch (error: any) {
465
- console.log(chalk.red(`\n❌ Login error: ${error.message || 'Unknown error'}`));
466
- }
467
- }
468
-
469
- private async handleVlm(): Promise<void> {
470
- logger.section('VLM Configuration for GUI Agent');
471
-
472
- // Show current VLM config
473
- const currentVlmConfig = {
474
- model: this.configManager.get('guiSubagentModel'),
475
- baseUrl: this.configManager.get('guiSubagentBaseUrl'),
476
- apiKey: this.configManager.get('guiSubagentApiKey') ? '***' : ''
477
- };
478
-
479
- console.log(chalk.cyan('\n📊 Current VLM Configuration:\n'));
480
- console.log(` Model: ${chalk.yellow(currentVlmConfig.model || 'Not configured')}`);
481
- console.log(` Base URL: ${chalk.yellow(currentVlmConfig.baseUrl || 'Not configured')}`);
482
- console.log(` API Key: ${chalk.yellow(currentVlmConfig.apiKey || 'Not configured')}`);
483
- console.log();
484
-
485
- const { action } = await inquirer.prompt([
486
- {
487
- type: 'list',
488
- name: 'action',
489
- message: 'Select action:',
490
- choices: [
491
- { name: 'Configure VLM', value: 'configure' },
492
- { name: 'Remove VLM configuration', value: 'remove' },
493
- { name: 'Back', value: 'back' }
494
- ]
495
- }
496
- ]);
497
-
498
- if (action === 'back') {
499
- return;
500
- }
501
-
502
- if (action === 'remove') {
503
- const { confirm } = await inquirer.prompt([
504
- {
505
- type: 'confirm',
506
- name: 'confirm',
507
- message: 'Are you sure you want to remove VLM configuration?',
508
- default: false
509
- }
510
- ]);
511
-
512
- if (confirm) {
513
- await this.configManager.set('guiSubagentModel', '');
514
- await this.configManager.set('guiSubagentBaseUrl', '');
515
- await this.configManager.set('guiSubagentApiKey', '');
516
- await this.configManager.save('global');
517
- console.log(chalk.green('✅ VLM configuration removed successfully!'));
518
- }
519
- return;
520
- }
521
-
522
- if (action === 'configure') {
523
- // Use AuthService to configure VLM
524
- const authService = new AuthService({
525
- type: 'openai_compatible' as any,
526
- apiKey: '',
527
- baseUrl: '',
528
- modelName: ''
529
- });
530
-
531
- const vlmConfig = await authService.configureAndValidateVLM();
532
-
533
- if (vlmConfig) {
534
- // Save VLM configuration
535
- await this.configManager.set('guiSubagentModel', vlmConfig.model);
536
- await this.configManager.set('guiSubagentBaseUrl', vlmConfig.baseUrl);
537
- await this.configManager.set('guiSubagentApiKey', vlmConfig.apiKey);
538
- await this.configManager.save('global');
539
- console.log(chalk.green('✅ VLM configuration saved successfully!'));
540
- console.log(chalk.cyan(` Model: ${vlmConfig.model}`));
541
- console.log(chalk.cyan(` Base URL: ${vlmConfig.baseUrl}`));
542
- } else {
543
- console.log(chalk.red('❌ VLM configuration failed or cancelled'));
544
- }
545
- }
546
- }
547
-
548
- private async handleMode(args: string[]): Promise<void> {
549
- const modes = Object.values(ExecutionMode);
550
- const currentMode = this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
551
-
552
- if (args.length > 0) {
553
- const newMode = args[0].toLowerCase();
554
- if (modes.includes(newMode as ExecutionMode)) {
555
- this.configManager.setApprovalMode(newMode as ExecutionMode);
556
- await this.configManager.save('global');
557
- console.log(chalk.green(`✅ Approval mode changed to: ${newMode}`));
558
- } else {
559
- console.log(chalk.red(`❌ Invalid mode: ${newMode}`));
560
- console.log(chalk.gray(`Available modes: ${modes.join(', ')}`));
561
- }
562
- } else {
563
- console.log(chalk.cyan('\n🎯 Approval Modes:\n'));
564
- console.log(` Current: ${chalk.green(currentMode)}\n`);
565
-
566
- const descriptions = [
567
- { mode: 'yolo', desc: 'Execute commands without confirmation' },
568
- { mode: 'accept_edits', desc: 'Accept all edits automatically' },
569
- { mode: 'plan', desc: 'Plan before executing' },
570
- { mode: 'default', desc: 'Safe execution with confirmations' },
571
- { mode: 'smart', desc: 'Smart approval with intelligent security checks' }
572
- ];
573
-
574
- descriptions.forEach(({ mode, desc }) => {
575
- const current = mode === currentMode ? chalk.green(' [current]') : '';
576
- console.log(` ${chalk.yellow(mode)}${current}`);
577
- console.log(` ${chalk.gray(desc)}`);
578
- });
579
-
580
- console.log();
581
- }
582
- }
583
-
584
- private async handleThink(args: string[]): Promise<void> {
585
- const thinkingConfig = this.configManager.getThinkingConfig();
586
-
587
- if (args.length > 0) {
588
- const action = args[0].toLowerCase();
589
-
590
- if (action === 'on' || action === 'true' || action === '1') {
591
- thinkingConfig.enabled = true;
592
- this.configManager.setThinkingConfig(thinkingConfig);
593
- await this.configManager.save('global');
594
- console.log(chalk.green('✅ Thinking mode enabled'));
595
- } else if (action === 'off' || action === 'false' || action === '0') {
596
- thinkingConfig.enabled = false;
597
- this.configManager.setThinkingConfig(thinkingConfig);
598
- await this.configManager.save('global');
599
- console.log(chalk.green('✅ Thinking mode disabled'));
600
- } else if (action === 'display' && args[1]) {
601
- const displayMode = args[1].toLowerCase();
602
- const validModes = ['full', 'compact', 'indicator'];
603
-
604
- if (validModes.includes(displayMode)) {
605
- thinkingConfig.displayMode = displayMode as 'full' | 'compact' | 'indicator';
606
- thinkingConfig.enabled = true; // Auto-enable when setting display mode
607
- this.configManager.setThinkingConfig(thinkingConfig);
608
- await this.configManager.save('global');
609
- console.log(chalk.green(`✅ Thinking display mode set to: ${displayMode}`));
610
- } else {
611
- console.log(chalk.red(`❌ Invalid display mode: ${displayMode}`));
612
- console.log(chalk.gray(`Valid modes: ${validModes.join(', ')}`));
613
- }
614
- } else {
615
- console.log(chalk.red(`❌ Invalid action: ${action}`));
616
- console.log(chalk.gray('Usage: /think [on|off|display <mode>]'));
617
- }
618
- } else {
619
- console.log(chalk.cyan('\n🧠 Thinking Mode:\n'));
620
- console.log(` Status: ${thinkingConfig.enabled ? chalk.green('Enabled') : chalk.red('Disabled')}`);
621
- console.log(` Mode: ${chalk.yellow(thinkingConfig.mode)}`);
622
- console.log(` Display: ${chalk.yellow(thinkingConfig.displayMode)}\n`);
623
-
624
- console.log(chalk.gray('Usage:'));
625
- console.log(chalk.gray(' /think on - Enable thinking mode'));
626
- console.log(chalk.gray(' /think off - Disable thinking mode'));
627
- console.log(chalk.gray(' /think display full - Show full thinking process'));
628
- console.log(chalk.gray(' /think display compact - Show compact thinking (500 chars)'));
629
- console.log(chalk.gray(' /think display indicator - Show only indicator'));
630
- console.log();
631
- }
632
- }
633
-
634
- private async handleAgents(args: string[]): Promise<void> {
635
- const action = args[0] || 'list';
636
-
637
- switch (action) {
638
- case 'list':
639
- await this.listAgents();
640
- break;
641
- case 'online':
642
- logger.warn('Online marketplace not implemented yet', 'Check back later for updates');
643
- break;
644
- case 'install':
645
- logger.warn('Agent installation wizard not implemented yet', 'Use /agents install in interactive mode');
646
- break;
647
- case 'remove':
648
- logger.warn('Agent removal not implemented yet', 'Use /agents remove in interactive mode');
649
- break;
650
- default:
651
- logger.warn(`Unknown agents action: ${action}`, 'Use /agents list to see available actions');
652
- }
653
- }
654
-
655
- private async listAgents(): Promise<void> {
656
- const agents = this.agentManager.getAllAgents();
657
-
658
- if (agents.length === 0) {
659
- logger.warn('No agents configured', 'Use /agents install to add agents');
660
- return;
661
- }
662
-
663
- logger.section('Available Agents');
664
-
665
- agents.forEach((agent: AgentConfig) => {
666
- const color = agent.color || '#FFFFFF';
667
- logger.info(` ${chalk.hex(color)(agent.name || agent.agentType)}`);
668
- logger.info(` Type: ${agent.agentType}`);
669
- logger.info(` ${agent.whenToUse}`);
670
- });
671
- }
672
-
673
- private async handleMcp(args: string[]): Promise<void> {
674
- const action = args[0] || 'list';
675
-
676
- switch (action) {
677
- case 'list':
678
- await this.listMcpServers();
679
- break;
680
- case 'add':
681
- if (args[1]) {
682
- // Non-interactive mode: use command line arguments
683
- await this.addMcpServerInteractive(args[1]);
684
- } else {
685
- // Interactive mode
686
- await this.addMcpServerInteractive();
687
- }
688
- break;
689
- case 'remove':
690
- if (args[1]) {
691
- // Non-interactive mode
692
- await this.removeMcpServer(args[1]);
693
- } else {
694
- // Interactive mode
695
- await this.removeMcpServerInteractive();
696
- }
697
- break;
698
- case 'refresh':
699
- await this.refreshMcpServers();
700
- break;
701
- default:
702
- logger.warn(`Unknown MCP action: ${action}`, 'Use /mcp list to see available actions');
703
- }
704
- }
705
-
706
- private async listMcpServers(): Promise<void> {
707
- const serverConfigs = this.mcpManager.getAllServerConfigs();
708
-
709
- if (serverConfigs.length === 0) {
710
- logger.section('MCP Servers');
711
- logger.warn('No MCP servers configured');
712
- logger.info('Use /mcp add to add a new MCP server');
713
- return;
714
- }
715
-
716
- logger.section('MCP Servers');
717
-
718
- serverConfigs.forEach(({ name: serverName, config: serverConfig }: { name: string; config: any }) => {
719
- const server = this.mcpManager.getServer(serverName);
720
- const isConnected = server?.isServerConnected() || false;
721
- const status = isConnected ? chalk.green(' Connected') : chalk.red('✗ Disconnected');
722
- const tools = server?.getToolNames() || [];
723
- const transport = serverConfig?.transport || serverConfig?.type || 'unknown';
724
- const command = serverConfig?.command ? `${serverConfig.command} ${(serverConfig.args || []).join(' ')}` : serverConfig?.url || 'N/A';
725
-
726
- console.log('');
727
- console.log(` ${chalk.cyan(serverName)} ${status}`);
728
- console.log(` Transport: ${transport}`);
729
- console.log(` Command: ${command}`);
730
- console.log(` Tools: ${isConnected ? tools.length : 'N/A'} (${isConnected ? tools.join(', ') : 'wait for connection'})`);
731
- });
732
-
733
- console.log('');
734
- logger.info(`Total: ${serverConfigs.length} server(s)`);
735
- }
736
-
737
- private async addMcpServerInteractive(serverName?: string): Promise<void> {
738
- const { name, command, args: serverArgs, transport, url, authToken, headers } = await inquirer.prompt([
739
- {
740
- type: 'input',
741
- name: 'name',
742
- message: 'Enter MCP server name:',
743
- default: serverName,
744
- validate: (input: string) => {
745
- if (!input.trim()) {
746
- return 'Server name is required';
747
- }
748
- const servers = this.mcpManager.getAllServers();
749
- if (servers.some((s: MCPServer) => (s as any).config?.name === input)) {
750
- return 'Server with this name already exists';
751
- }
752
- return true;
753
- }
754
- },
755
- {
756
- type: 'list',
757
- name: 'transport',
758
- message: 'Select transport type:',
759
- choices: [
760
- { name: 'Stdio (stdin/stdout)', value: 'stdio' },
761
- { name: 'HTTP/SSE', value: 'sse' },
762
- { name: 'HTTP (POST)', value: 'http' }
763
- ],
764
- default: 'stdio'
765
- },
766
- {
767
- type: 'input',
768
- name: 'command',
769
- message: 'Enter command (for stdio transport):',
770
- when: (answers: any) => answers.transport === 'stdio',
771
- validate: (input: string) => input.trim() ? true : 'Command is required'
772
- },
773
- {
774
- type: 'input',
775
- name: 'args',
776
- message: 'Enter arguments (comma-separated, for stdio transport):',
777
- when: (answers: any) => answers.transport === 'stdio',
778
- filter: (input: string) => input ? input.split(',').map((a: string) => a.trim()) : []
779
- },
780
- {
781
- type: 'input',
782
- name: 'url',
783
- message: 'Enter server URL (for HTTP/SSE/HTTP transport):',
784
- when: (answers: any) => answers.transport === 'sse' || answers.transport === 'http',
785
- validate: (input: string) => input.trim() ? true : 'URL is required'
786
- },
787
- {
788
- type: 'password',
789
- name: 'authToken',
790
- message: 'Enter authentication token (optional):',
791
- when: (answers: any) => answers.transport === 'sse' || answers.transport === 'http'
792
- },
793
- {
794
- type: 'input',
795
- name: 'headers',
796
- message: 'Enter custom headers as JSON (optional, e.g., {"Authorization": "Bearer token"}):',
797
- when: (answers: any) => answers.transport === 'sse' || answers.transport === 'http',
798
- filter: (input: string) => {
799
- if (!input.trim()) return undefined;
800
- try {
801
- return JSON.parse(input);
802
- } catch {
803
- return undefined;
804
- }
805
- }
806
- }
807
- ]);
808
-
809
- const config: any = {
810
- transport: transport as 'stdio' | 'sse' | 'http'
811
- };
812
-
813
- if (transport === 'stdio') {
814
- config.command = command;
815
- if (serverArgs && serverArgs.length > 0) {
816
- config.args = serverArgs;
817
- }
818
- } else {
819
- config.url = url;
820
- if (authToken) {
821
- config.authToken = authToken;
822
- }
823
- if (headers) {
824
- config.headers = headers;
825
- }
826
- }
827
-
828
- try {
829
- // Save to config file
830
- this.configManager.addMcpServer(name, config);
831
- await this.configManager.save('global');
832
-
833
- // Register to MCP Manager
834
- this.mcpManager.registerServer(name, config);
835
-
836
- // Connect to server (with error handling)
837
- let connected = false;
838
- try {
839
- await this.mcpManager.connectServer(name);
840
- connected = true;
841
- } catch (error: any) {
842
- // Connection failed - cleanup
843
- this.mcpManager.disconnectServer(name);
844
- this.configManager.removeMcpServer(name);
845
- await this.configManager.save('global');
846
- throw new Error(`Connection failed: ${error.message}`);
847
- }
848
-
849
- // Register MCP tools with simple names
850
- const allMcpTools = this.mcpManager.getAllTools();
851
- const toolRegistry = getToolRegistry();
852
- toolRegistry.registerMCPTools(allMcpTools);
853
-
854
- // Update system prompt to include new MCP tools
855
- if (this.onSystemPromptUpdate) {
856
- await this.onSystemPromptUpdate();
857
- }
858
-
859
- console.log(chalk.green(`✅ MCP server '${name}' added and connected successfully`));
860
- } catch (error: any) {
861
- console.log(chalk.red(`❌ Failed to add MCP server: ${error.message}`));
862
- }
863
- }
864
-
865
- private async removeMcpServerInteractive(): Promise<void> {
866
- const servers = this.mcpManager.getAllServers();
867
-
868
- if (servers.length === 0) {
869
- logger.warn('No MCP servers configured', 'Use /mcp add to add servers');
870
- return;
871
- }
872
-
873
- const serverNames = servers.map((s: MCPServer) => {
874
- const tools = s.getToolNames();
875
- const status = s.isServerConnected() ? '✓' : '✗';
876
- return {
877
- name: `${status} ${(s as any).config?.name || 'unknown'} (${tools.length} tools)`,
878
- value: (s as any).config?.name
879
- };
880
- });
881
-
882
- const { serverName } = await inquirer.prompt([
883
- {
884
- type: 'list',
885
- name: 'serverName',
886
- message: 'Select MCP server to remove:',
887
- choices: serverNames
888
- }
889
- ]);
890
-
891
- await this.removeMcpServer(serverName);
892
- }
893
-
894
- private async removeMcpServer(serverName: string): Promise<void> {
895
- try {
896
- // Get server info before disconnecting to notify LLM
897
- const server = this.mcpManager.getServer(serverName);
898
- const removedTools = server ? server.getToolNames() : [];
899
- const removedToolNames = removedTools.map((t: string) => `${serverName}__${t}`).join(', ');
900
-
901
- // Disconnect
902
- this.mcpManager.disconnectServer(serverName);
903
-
904
- // Unregister MCP tools for this server
905
- const toolRegistry = getToolRegistry();
906
- toolRegistry.unregisterMCPTools(serverName);
907
-
908
- // Remove from config
909
- this.configManager.removeMcpServer(serverName);
910
- await this.configManager.save('global');
911
-
912
- // Update system prompt to reflect removed MCP tools
913
- if (this.onSystemPromptUpdate) {
914
- await this.onSystemPromptUpdate();
915
- }
916
-
917
- console.log(chalk.green(`✅ MCP server '${serverName}' removed successfully`));
918
- } catch (error: any) {
919
- console.log(chalk.red(`❌ Failed to remove MCP server: ${error.message}`));
920
- }
921
- }
922
-
923
- private async refreshMcpServers(): Promise<void> {
924
- const spinner = ora({ text: 'Refreshing MCP servers...', interval: 200 }).start();
925
-
926
- try {
927
- // Disconnect all existing connections
928
- this.mcpManager.disconnectAllServers();
929
-
930
- // Reconnect all servers
931
- await this.mcpManager.connectAllServers();
932
-
933
- spinner.succeed('MCP servers refreshed successfully');
934
-
935
- // Show current server status
936
- await this.listMcpServers();
937
- } catch (error: any) {
938
- spinner.fail(`Failed to refresh MCP servers: ${error.message}`);
939
- }
940
- }
941
-
942
- private async handleMemory(args: string[]): Promise<void> {
943
- const action = args[0] || 'show';
944
-
945
- // Load memory files before showing
946
- try {
947
- await this.memoryManager.loadMemory();
948
- } catch (error) {
949
- logger.error('Failed to load memory files', error instanceof Error ? error.message : String(error));
950
- return;
951
- }
952
-
953
- switch (action) {
954
- case 'show':
955
- await this.showMemory();
956
- break;
957
- case 'clear':
958
- await this.clearMemory(args[1]);
959
- break;
960
- default:
961
- logger.warn(`Unknown memory action: ${action}`, 'Use /memory show to see available actions');
962
- }
963
- }
964
-
965
- private async clearMemory(target?: string): Promise<void> {
966
- const memoryFiles = this.memoryManager.getMemoryFiles();
967
-
968
- // Handle different clear targets
969
- if (!target || target === '.' || target === 'current') {
970
- // Clear current project's memory
971
- const currentProjectMemory = memoryFiles.find((m: MemoryFile) => m.level === 'project');
972
- if (currentProjectMemory) {
973
- await fs.unlink(currentProjectMemory.path);
974
- logger.success('Project memory cleared');
975
- logger.info('Use /init to initialize if needed');
976
- } else {
977
- logger.warn('No project memory found for current directory');
978
- }
979
- return;
980
- }
981
-
982
- if (target === 'all') {
983
- // Clear all memories including global
984
- let cleared = 0;
985
- for (const file of memoryFiles) {
986
- await fs.unlink(file.path);
987
- cleared++;
988
- }
989
- logger.success(`Cleared ${cleared} memory file(s)`);
990
-
991
- // Recreate global memory
992
- await this.memoryManager.saveMemory('# Global Context\n\nGlobal preferences and settings will be added here.', 'global');
993
- logger.info('Recreated global memory');
994
- logger.info('Use /init to initialize project memory if needed');
995
- return;
996
- }
997
-
998
- if (target === 'global') {
999
- // Clear global memory
1000
- const globalMemory = memoryFiles.find((m: MemoryFile) => m.level === 'global');
1001
- if (globalMemory) {
1002
- await fs.unlink(globalMemory.path);
1003
- logger.success('Global memory cleared');
1004
-
1005
- // Recreate global memory
1006
- await this.memoryManager.saveMemory('# Global Context\n\nGlobal preferences and settings will be added here.', 'global');
1007
- logger.info('Recreated with default content');
1008
- } else {
1009
- logger.warn('No global memory found');
1010
- }
1011
- return;
1012
- }
1013
-
1014
- // Clear specific file by filename or path
1015
- const targetMemory = memoryFiles.find((m: MemoryFile) =>
1016
- path.basename(m.path) === target ||
1017
- m.path === target
1018
- );
1019
-
1020
- if (targetMemory) {
1021
- try {
1022
- await fs.unlink(targetMemory.path);
1023
- const levelLabel = targetMemory.level === 'global' ? 'Global' : 'Project';
1024
- logger.success(`${levelLabel} memory cleared: ${path.basename(targetMemory.path)}`);
1025
-
1026
- // Recreate global memory, not project memory
1027
- if (targetMemory.level === 'global') {
1028
- await this.memoryManager.saveMemory('# Global Context\n\nGlobal preferences and settings will be added here.', 'global');
1029
- logger.info('Recreated with default content');
1030
- } else {
1031
- logger.info('Use /init to initialize if needed');
1032
- }
1033
- } catch (error) {
1034
- logger.error('Failed to clear memory', error instanceof Error ? error.message : String(error));
1035
- }
1036
- } else {
1037
- logger.warn(`Memory file not found: ${target}`, 'Use /memory show to see available files');
1038
- }
1039
- }
1040
-
1041
- private async showMemory(): Promise<void> {
1042
- const memoryFiles = this.memoryManager.getMemoryFiles();
1043
-
1044
- if (memoryFiles.length === 0) {
1045
- logger.warn('No memory files loaded', 'Use /init to initialize project context');
1046
- return;
1047
- }
1048
-
1049
- const memoriesDir = this.memoryManager.getMemoriesDir();
1050
- logger.section('Memory Files');
1051
- logger.info(`Directory: ${memoriesDir}`);
1052
- console.log('');
1053
-
1054
- memoryFiles.forEach((file: MemoryFile) => {
1055
- const level = file.level === 'global' ? chalk.blue('[global]') :
1056
- file.level === 'project' ? chalk.green('[project]') :
1057
- chalk.yellow('[subdirectory]');
1058
- logger.info(` ${level} ${path.basename(file.path)}`);
1059
- });
1060
-
1061
- console.log('');
1062
- // logger.info('Usage: /memory clear [global|current|all|<filename>]');
1063
- }
1064
-
1065
- private async addMemory(): Promise<void> {
1066
- const { entry } = await inquirer.prompt([
1067
- {
1068
- type: 'editor',
1069
- name: 'entry',
1070
- message: 'Enter memory entry (opens editor):'
1071
- }
1072
- ]);
1073
-
1074
- if (entry && entry.trim()) {
1075
- await this.memoryManager.addMemoryEntry(entry.trim());
1076
- console.log(chalk.green('✅ Memory entry added'));
1077
- }
1078
- }
1079
-
1080
- private async refreshMemory(): Promise<void> {
1081
- const spinner = ora({ text: 'Refreshing memory...', interval: 200 }).start();
1082
-
1083
- try {
1084
- await this.memoryManager.loadMemory();
1085
- spinner.succeed('Memory refreshed successfully');
1086
- } catch (error: any) {
1087
- spinner.fail(`Failed to refresh memory: ${error.message}`);
1088
- }
1089
- }
1090
-
1091
- private async handleRestore(args: string[]): Promise<void> {
1092
- if (!this.checkpointManager.isEnabled()) {
1093
- logger.warn('Checkpointing is not enabled', 'Enable it with /mode or in settings');
1094
- return;
1095
- }
1096
-
1097
- const checkpoints = this.checkpointManager.listCheckpoints();
1098
-
1099
- if (checkpoints.length === 0) {
1100
- logger.warn('No checkpoints available', 'Create checkpoints during your session');
1101
- return;
1102
- }
1103
-
1104
- if (args.length > 0) {
1105
- const checkpointId = args[0];
1106
- try {
1107
- await this.checkpointManager.restoreCheckpoint(checkpointId);
1108
- logger.success(`Checkpoint ${checkpointId} restored successfully!`);
1109
- } catch (error: any) {
1110
- logger.error(error.message, 'Check if checkpoint ID is valid');
1111
- }
1112
- } else {
1113
- const choices = checkpoints.map((cp: Checkpoint) => ({
1114
- name: `${new Date(cp.timestamp).toLocaleString()} - ${cp.description}`,
1115
- value: cp.id
1116
- }));
1117
-
1118
- const { checkpointId } = await inquirer.prompt([
1119
- {
1120
- type: 'list',
1121
- name: 'checkpointId',
1122
- message: 'Select checkpoint to restore:',
1123
- choices
1124
- }
1125
- ]);
1126
-
1127
- try {
1128
- await this.checkpointManager.restoreCheckpoint(checkpointId);
1129
- logger.success(`Checkpoint ${checkpointId} restored successfully!`);
1130
- } catch (error: any) {
1131
- logger.error(error.message, 'Check if checkpoint ID is valid');
1132
- }
1133
- }
1134
- }
1135
-
1136
- private async handleTools(): Promise<void> {
1137
- const toolRegistry = getToolRegistry();
1138
- const tools = toolRegistry.getAll();
1139
-
1140
- logger.section('Available Tools');
1141
-
1142
- tools.forEach(tool => {
1143
- logger.info(` ${tool.name}`);
1144
- logger.info(` ${tool.description}`);
1145
- });
1146
-
1147
- console.log('');
1148
- const currentSetting = this.configManager.get('showToolDetails') ? 'verbose' : 'simple';
1149
- logger.info(`Current tool display mode: ${currentSetting}`);
1150
- logger.info('Use /tools verbose to switch to verbose mode');
1151
- logger.info('Use /tools simple to switch to simple mode');
1152
- }
1153
-
1154
- private async handleToolsVerbose(args: string[]): Promise<void> {
1155
- if (args.length === 0) {
1156
- const currentSetting = this.configManager.get('showToolDetails') ? 'verbose' : 'simple';
1157
- logger.info(`Current tool display mode: ${currentSetting}`);
1158
- return;
1159
- }
1160
-
1161
- const mode = args[0].toLowerCase();
1162
-
1163
- if (mode === 'verbose' || mode === 'detail' || mode === 'true' || mode === 'on') {
1164
- this.configManager.set('showToolDetails', true);
1165
- await this.configManager.save('global');
1166
- logger.success('Tool display mode switched to verbose mode', 'Will show complete tool call information');
1167
- } else if (mode === 'simple' || mode === 'concise' || mode === 'false' || mode === 'off') {
1168
- this.configManager.set('showToolDetails', false);
1169
- await this.configManager.save('global');
1170
- logger.success('Tool display mode switched to simple mode', 'Only show tool execution status');
1171
- } else {
1172
- logger.warn('Invalid mode', 'Use verbose or simple');
1173
- }
1174
- }
1175
-
1176
- private async handleStats(): Promise<void> {
1177
- logger.section('Session Statistics');
1178
- logger.info(` Execution Mode: ${this.configManager.getExecutionMode()}`);
1179
- logger.info(` Language: ${this.configManager.getLanguage()}`);
1180
- logger.info(` Checkpointing: ${this.checkpointManager.isEnabled() ? 'Enabled' : 'Disabled'}`);
1181
- logger.info(` MCP Servers: ${this.mcpManager.getAllServers().length}`);
1182
- logger.info(` Agents: ${this.agentManager.getAllAgents().length}`);
1183
- }
1184
-
1185
- private async handleTheme(): Promise<void> {
1186
- logger.warn('Theme switching not implemented yet', 'Check back later for updates');
1187
- }
1188
-
1189
- private async handleLanguage(): Promise<void> {
1190
- const { language } = await inquirer.prompt([
1191
- {
1192
- type: 'list',
1193
- name: 'language',
1194
- message: 'Select language:',
1195
- choices: [
1196
- { name: 'Chinese', value: 'zh' },
1197
- { name: 'English', value: 'en' }
1198
- ]
1199
- }
1200
- ]);
1201
-
1202
- this.configManager.setLanguage(language);
1203
- logger.success(`Language changed to: ${language === 'zh' ? 'Chinese' : 'English'}`, 'Restart CLI to apply changes');
1204
- }
1205
-
1206
- private async handleAbout(): Promise<void> {
1207
- logger.section('xAgent CLI');
1208
- logger.info('Version: 1.0.0');
1209
- logger.info('A powerful AI-powered command-line assistant');
1210
- logger.blank();
1211
- logger.link('Documentation', 'https://platform.xagent.cn/');
1212
- logger.link('GitHub', 'https://github.com/xagent-ai/xagent-cli');
1213
- }
1214
-
1215
- private async handleCompress(args: string[]): Promise<void> {
1216
- const config = this.configManager.getContextCompressionConfig();
1217
-
1218
- // If there are arguments, process config or execute
1219
- if (args.length > 0) {
1220
- const action = args[0].toLowerCase();
1221
-
1222
- if (action === 'exec' || action === 'run' || action === 'now') {
1223
- await this.executeCompression(config);
1224
- return;
1225
- }
1226
-
1227
- await this.setCompressConfig(args);
1228
- return;
1229
- }
1230
-
1231
- // Display current configuration
1232
- console.log(chalk.cyan('\n📦 Context Compression:\n'));
1233
-
1234
- console.log(` Status: ${config.enabled ? chalk.green('Enabled') : chalk.red('Disabled')}`);
1235
- console.log(` Max Messages: ${chalk.yellow(config.maxMessages.toString())}`);
1236
- console.log(` Max Tokens: ${chalk.yellow(config.maxContextSize.toString())}`);
1237
-
1238
- console.log('');
1239
- console.log(chalk.gray('Usage:'));
1240
- console.log(chalk.gray(' /compress - Show current configuration'));
1241
- console.log(chalk.gray(' /compress exec - Execute compression now'));
1242
- console.log(chalk.gray(' /compress on|off - Enable/disable compression'));
1243
- console.log(chalk.gray(' /compress max_message <n> - Set max messages before compression'));
1244
- console.log(chalk.gray(' /compress max_token <n> - Set max tokens before compression'));
1245
- console.log('');
1246
- }
1247
-
1248
- private async executeCompression(config: CompressionConfig): Promise<void> {
1249
- const messages = this.conversationHistory;
1250
-
1251
- if (!messages || messages.length === 0) {
1252
- console.log(chalk.yellow('⚠️ No conversation to compress'));
1253
- return;
1254
- }
1255
-
1256
- const { needsCompression, reason } = this.contextCompressor.needsCompression(
1257
- messages,
1258
- config
1259
- );
1260
-
1261
- if (!needsCompression) {
1262
- console.log(chalk.green('✅ No compression needed'));
1263
- console.log(chalk.gray(` ${reason}`));
1264
- return;
1265
- }
1266
-
1267
- console.log(chalk.cyan('\n🚀 Executing context compression...\n'));
1268
-
1269
- const spinner = ora({
1270
- text: 'Compressing context...',
1271
- spinner: 'dots',
1272
- color: 'cyan'
1273
- }).start();
1274
-
1275
- try {
1276
- const result: CompressionResult = await this.contextCompressor.compressContext(
1277
- messages,
1278
- 'You are a helpful AI assistant.',
1279
- config
1280
- );
1281
-
1282
- spinner.succeed(chalk.green('✅ Compression complete'));
1283
-
1284
- console.log('');
1285
- console.log(` ${chalk.cyan('Original:')} ${chalk.yellow(result.originalMessageCount.toString())} messages (${result.originalSize} chars)`);
1286
- console.log(` ${chalk.cyan('Compressed:')} ${chalk.yellow(result.compressedMessageCount.toString())} messages (${result.compressedSize} chars)`);
1287
- console.log(` ${chalk.cyan('Reduction:')} ${chalk.green(Math.round((1 - result.compressedSize / result.originalSize) * 100) + '%')}`);
1288
- console.log(` ${chalk.cyan('Method:')} ${chalk.yellow(result.compressionMethod)}`);
1289
-
1290
- console.log('');
1291
- console.log(chalk.gray('Use /clear to start a new conversation, or continue chatting to see the compressed summary.'));
1292
- console.log('');
1293
- } catch (error: any) {
1294
- spinner.fail(chalk.red('Compression failed'));
1295
- console.log(chalk.red(` ${error.message}`));
1296
- }
1297
- }
1298
-
1299
- private async setCompressConfig(args: string[]): Promise<void> {
1300
- const config = this.configManager.getContextCompressionConfig();
1301
- const action = args[0].toLowerCase();
1302
-
1303
- switch (action) {
1304
- case 'on':
1305
- config.enabled = true;
1306
- this.configManager.setContextCompressionConfig(config);
1307
- await this.configManager.save('global');
1308
- console.log(chalk.green(' Context compression enabled'));
1309
- break;
1310
-
1311
- case 'off':
1312
- config.enabled = false;
1313
- this.configManager.setContextCompressionConfig(config);
1314
- await this.configManager.save('global');
1315
- console.log(chalk.green('✅ Context compression disabled'));
1316
- break;
1317
-
1318
- case 'max_message':
1319
- if (args[1]) {
1320
- const maxMessages = parseInt(args[1], 10);
1321
- if (isNaN(maxMessages) || maxMessages < 1) {
1322
- console.log(chalk.red('❌ Invalid value for max_message. Must be a positive number.'));
1323
- return;
1324
- }
1325
- config.maxMessages = maxMessages;
1326
- this.configManager.setContextCompressionConfig(config);
1327
- await this.configManager.save('global');
1328
- console.log(chalk.green(`✅ Max messages set to: ${maxMessages}`));
1329
- } else {
1330
- console.log(chalk.gray('Usage: /compress max_message <number>'));
1331
- }
1332
- break;
1333
-
1334
- case 'max_token':
1335
- if (args[1]) {
1336
- const maxContextSize = parseInt(args[1], 10);
1337
- if (isNaN(maxContextSize) || maxContextSize < 1000) {
1338
- console.log(chalk.red('❌ Invalid value for max_token. Must be at least 1000.'));
1339
- return;
1340
- }
1341
- config.maxContextSize = maxContextSize;
1342
- this.configManager.setContextCompressionConfig(config);
1343
- await this.configManager.save('global');
1344
- console.log(chalk.green(`✅ Max tokens set to: ${maxContextSize}`));
1345
- } else {
1346
- console.log(chalk.gray('Usage: /compress max_token <number>'));
1347
- }
1348
- break;
1349
-
1350
- default:
1351
- console.log(chalk.red(`❌ Unknown action: ${action}`));
1352
- console.log(chalk.gray('Available actions: on, off, max_message, max_token, exec'));
1353
- }
1354
- }
1355
- }
1356
-
1357
- export function parseInput(input: string): InputType[] {
1358
- const inputs: InputType[] = [];
1359
- let remaining = input;
1360
-
1361
- const fileRefRegex = /@([^\s]+)/g;
1362
- let match;
1363
- while ((match = fileRefRegex.exec(remaining)) !== null) {
1364
- const filePath = match[1];
1365
- const beforeMatch = remaining.substring(0, match.index);
1366
- const afterMatch = remaining.substring(match.index + match[0].length);
1367
-
1368
- if (beforeMatch.trim()) {
1369
- inputs.push({ type: 'text', content: beforeMatch.trim() });
1370
- }
1371
-
1372
- inputs.push({ type: 'file', content: filePath });
1373
- remaining = afterMatch;
1374
- }
1375
-
1376
- if (remaining.trim()) {
1377
- if (remaining.startsWith('!')) {
1378
- inputs.push({ type: 'command', content: remaining.slice(1).trim() });
1379
- } else {
1380
- inputs.push({ type: 'text', content: remaining.trim() });
1381
- }
1382
- }
1383
-
1384
- return inputs;
1385
- }
1386
-
1387
- export function detectImageInput(input: string): boolean {
1388
- return input.includes('[Pasted image') || input.includes('<image');
1389
- }
1
+ import readline from 'readline';
2
+ import { select, confirm, text } from '@clack/prompts';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { ExecutionMode, ChatMessage, InputType, ToolCall, Checkpoint, AgentConfig, CompressionConfig, AuthType } from './types.js';
8
+ import { Message, detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
9
+ import { fetchDefaultModels } from './ai-client/providers/remote.js';
10
+ import { getToolRegistry } from './tools.js';
11
+ import { getAgentManager } from './agents.js';
12
+ import { getMemoryManager, MemoryFile } from './memory.js';
13
+ import { getMCPManager, MCPServer } from './mcp.js';
14
+ import { getCheckpointManager } from './checkpoint.js';
15
+ import { getConfigManager, ConfigManager } from './config.js';
16
+ import { getLogger } from './logger.js';
17
+ import {
18
+ getContextCompressor,
19
+ ContextCompressor,
20
+ CompressionResult,
21
+ } from './context-compressor.js';
22
+ import { getConversationManager, ConversationManager } from './conversation.js';
23
+ import { icons, colors } from './theme.js';
24
+ import { SystemPromptGenerator } from './system-prompt-generator.js';
25
+ import { ensureTtySane } from './terminal.js';
26
+ import { AuthService, selectAuthType, ThirdPartyProvider, THIRD_PARTY_PROVIDERS, VLM_PROVIDERS, VLMProviderInfo } from './auth.js';
27
+
28
+ const logger = getLogger();
29
+
30
+ export class SlashCommandHandler {
31
+ private configManager: ConfigManager;
32
+ private agentManager: any;
33
+ private memoryManager: any;
34
+ private mcpManager: any;
35
+ private checkpointManager: any;
36
+ private contextCompressor: ContextCompressor;
37
+ private conversationManager: ConversationManager;
38
+ private conversationHistory: ChatMessage[] = [];
39
+ private onClearCallback: (() => void) | null = null;
40
+ private onSystemPromptUpdate: (() => Promise<void>) | null = null;
41
+ private onConfigUpdate: (() => void) | null = null;
42
+ private remoteAIClient: any = null; // Reference to InteractiveSession's remoteAIClient
43
+
44
+ constructor() {
45
+ this.configManager = getConfigManager(process.cwd());
46
+ this.agentManager = getAgentManager(process.cwd());
47
+ this.memoryManager = getMemoryManager(process.cwd());
48
+ this.mcpManager = getMCPManager();
49
+ this.checkpointManager = getCheckpointManager(process.cwd());
50
+ this.contextCompressor = getContextCompressor();
51
+ this.conversationManager = getConversationManager();
52
+ }
53
+
54
+ /**
55
+ * Set remote AI client reference (called from InteractiveSession)
56
+ */
57
+ setRemoteAIClient(client: any): void {
58
+ this.remoteAIClient = client;
59
+ }
60
+
61
+ /**
62
+ * Set callback for clearing conversation
63
+ */
64
+ setClearCallback(callback: () => void): void {
65
+ this.onClearCallback = callback;
66
+ }
67
+
68
+ /**
69
+ * Set callback for system prompt update
70
+ */
71
+ setSystemPromptUpdateCallback(callback: () => Promise<void>): void {
72
+ this.onSystemPromptUpdate = callback;
73
+ }
74
+
75
+ /**
76
+ * Set callback for config update (called after /auth changes config)
77
+ */
78
+ setConfigUpdateCallback(callback: () => void): void {
79
+ this.onConfigUpdate = callback;
80
+ }
81
+
82
+ /**
83
+ * Set current conversation history (includes all user/assistant/tool messages)
84
+ */
85
+ setConversationHistory(messages: ChatMessage[]): void {
86
+ this.conversationHistory = messages;
87
+ }
88
+
89
+ async handleCommand(input: string): Promise<boolean> {
90
+ if (!input.startsWith('/')) {
91
+ return false;
92
+ }
93
+
94
+ const [command, ...args] = input.slice(1).split(' ');
95
+
96
+ switch (command.toLowerCase()) {
97
+ case 'help':
98
+ await this.showHelp();
99
+ break;
100
+ case 'init':
101
+ await this.handleInit();
102
+ break;
103
+ case 'clear':
104
+ await this.handleClear();
105
+ break;
106
+ case 'exit':
107
+ case 'quit':
108
+ await this.handleExit();
109
+ break;
110
+ case 'auth':
111
+ await this.handleAuth();
112
+ break;
113
+ case 'login':
114
+ await this.handleLogin();
115
+ break;
116
+ case 'mode':
117
+ await this.handleMode(args);
118
+ break;
119
+ case 'think':
120
+ await this.handleThink(args);
121
+ break;
122
+ case 'agents':
123
+ await this.handleAgents(args);
124
+ break;
125
+ case 'mcp':
126
+ await this.handleMcp(args);
127
+ break;
128
+ case 'skill':
129
+ await this.handleSkill(args);
130
+ break;
131
+ case 'model':
132
+ await this.handleModel();
133
+ break;
134
+ case 'memory':
135
+ await this.handleMemory(args);
136
+ break;
137
+ case 'restore':
138
+ await this.handleRestore(args);
139
+ break;
140
+ case 'tools':
141
+ await this.handleToolsVerbose(args);
142
+ break;
143
+ case 'stats':
144
+ await this.handleStats();
145
+ break;
146
+ case 'theme':
147
+ await this.handleTheme();
148
+ break;
149
+ // case 'language':
150
+ // await this.handleLanguage();
151
+ // break;
152
+ case 'about':
153
+ await this.handleAbout();
154
+ break;
155
+ case 'compress':
156
+ await this.handleCompress(args);
157
+ break;
158
+ case 'update':
159
+ await this.handleUpdate();
160
+ break;
161
+ default:
162
+ logger.warn(`Unknown command: /${command}`, 'Type /help for available commands');
163
+ }
164
+
165
+ // Ensure stdin is in raw mode for proper input handling after @clack/prompts usage
166
+ ensureTtySane();
167
+
168
+ return true;
169
+ }
170
+
171
+ private async showHelp(): Promise<void> {
172
+ const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80));
173
+
174
+ console.log('');
175
+ console.log(
176
+ colors.primaryBright('╔════════════════════════════════════════════════════════════╗')
177
+ );
178
+ console.log(colors.primaryBright('║') + ' '.repeat(56) + colors.primaryBright('║'));
179
+ console.log(
180
+ ' '.repeat(14) +
181
+ colors.gradient('📚 XAGENT CLI Help') +
182
+ ' '.repeat(31) +
183
+ colors.primaryBright('║')
184
+ );
185
+ console.log(colors.primaryBright('║') + ' '.repeat(56) + colors.primaryBright('║'));
186
+ console.log(
187
+ colors.primaryBright('╚════════════════════════════════════════════════════════════╝')
188
+ );
189
+ console.log('');
190
+
191
+ // Shortcuts
192
+ console.log(colors.accent('Shortcuts'));
193
+ console.log(colors.border(separator));
194
+ console.log('');
195
+ console.log(
196
+ colors.textDim(` ${colors.accent('!')} - ${colors.textMuted('Enter bash mode')}`)
197
+ );
198
+ console.log(colors.textDim(` ${colors.accent('/')} - ${colors.textMuted('Commands')}`));
199
+ console.log(colors.textDim(` ${colors.accent('@')} - ${colors.textMuted('File paths')}`));
200
+ console.log('');
201
+
202
+ // Basic Commands
203
+ this.showHelpCategory('Basic Commands', [
204
+ {
205
+ cmd: '/help [command]',
206
+ desc: 'Show help information',
207
+ detail: 'View all available commands or detailed description of specific command',
208
+ example: '/help\n/help mode',
209
+ },
210
+ {
211
+ cmd: '/clear',
212
+ desc: 'Clear conversation history',
213
+ detail: 'Clear all conversation records of current session, start new conversation',
214
+ example: '/clear',
215
+ },
216
+ {
217
+ cmd: '/exit',
218
+ desc: 'Exit program',
219
+ detail: 'Safely exit XAGENT CLI',
220
+ example: '/exit',
221
+ },
222
+ ]);
223
+
224
+ // Project Management
225
+ this.showHelpCategory('Project Management', [
226
+ {
227
+ cmd: '/init',
228
+ desc: 'Initialize project context',
229
+ detail:
230
+ 'Create XAGENT.md file in current directory, used to store project context information',
231
+ example: '/init',
232
+ },
233
+ {
234
+ cmd: '/memory [show|clear]',
235
+ desc: 'Manage project memory',
236
+ detail: 'View or clear memory (global, current, all, or filename)',
237
+ example: '/memory show\n/memory clear\n/memory clear global\n/memory clear all',
238
+ },
239
+ ]);
240
+
241
+ // Authentication & Configuration
242
+ this.showHelpCategory('Authentication & Configuration', [
243
+ {
244
+ cmd: '/auth',
245
+ desc: 'Configure authentication information',
246
+ detail: 'Change or view current authentication configuration',
247
+ example: '/auth',
248
+ },
249
+ {
250
+ cmd: '/mode [mode]',
251
+ desc: 'Switch approval mode',
252
+ detail: 'Switch security approval mode for tool execution',
253
+ example: '/mode\n/mode smart\n/mode yolo',
254
+ modes: [
255
+ 'yolo - Execute all operations without restriction',
256
+ 'accept_edits - Automatically accept edit operations',
257
+ 'plan - Plan before executing',
258
+ 'default - Safe execution, requires confirmation',
259
+ 'smart - Smart approval (recommended)',
260
+ ],
261
+ },
262
+ {
263
+ cmd: '/think [on|off|display]',
264
+ desc: 'Control thinking mode',
265
+ detail: 'Enable/disable AI thinking process display',
266
+ example: '/think on\n/think off\n/think display compact',
267
+ },
268
+ // {
269
+ // cmd: '/language [zh|en]',
270
+ // desc: 'Switch language',
271
+ // detail: 'Switch between Chinese and English interface',
272
+ // example: '/language zh\n/language en'
273
+ // },
274
+ {
275
+ cmd: '/theme',
276
+ desc: 'Switch theme',
277
+ detail: 'Change UI theme style',
278
+ example: '/theme',
279
+ },
280
+ ]);
281
+
282
+ // Feature Extensions
283
+ this.showHelpCategory('Feature Extensions', [
284
+ {
285
+ cmd: '/agents [list|online|install|remove]',
286
+ desc: 'Manage sub-agents',
287
+ detail: 'View, install or remove specialized AI sub-agents',
288
+ example: '/agents list\n/agents online\n/agents install explore-agent',
289
+ },
290
+ {
291
+ cmd: '/mcp [list|add|remove|refresh]',
292
+ desc: 'Manage MCP servers',
293
+ detail: 'Manage Model Context Protocol servers',
294
+ example: '/mcp list\n/mcp add server-name',
295
+ },
296
+ {
297
+ cmd: '/skill [list|add|remove]',
298
+ desc: 'Manage skills',
299
+ detail: 'Install, list, or remove skills from ~/.xagent/skills',
300
+ example: '/skill list\n/skill add ./my-skill\n/skill add owner/repo\n/skill remove my-skill'
301
+ },
302
+ {
303
+ cmd: '/vlm',
304
+ desc: 'Configure VLM for GUI Agent',
305
+ detail: 'Configure Vision-Language Model for browser/desktop automation',
306
+ example: '/vlm'
307
+ },
308
+ {
309
+ cmd: '/model',
310
+ desc: 'Configure LLM/VLM models',
311
+ detail: 'Configure or switch LLM and VLM models for remote mode',
312
+ example: '/model',
313
+ },
314
+ {
315
+ cmd: '/tools [verbose|simple]',
316
+ desc: 'Manage tool display',
317
+ detail: 'View available tools or switch tool call display mode',
318
+ example: '/tools\n/tools verbose\n/tools simple',
319
+ },
320
+ ]);
321
+
322
+ // Advanced Features
323
+ this.showHelpCategory('Advanced Features', [
324
+ {
325
+ cmd: '/restore',
326
+ desc: 'Restore from checkpoint',
327
+ detail: 'Restore conversation state from historical checkpoints',
328
+ example: '/restore',
329
+ },
330
+ {
331
+ cmd: '/compress [on|off|max_message|max_token|exec]',
332
+ desc: 'Manage context compression',
333
+ detail: 'Configure compression settings or execute compression manually',
334
+ example:
335
+ '/compress\n/compress exec\n/compress on\n/compress max_message 50\n/compress max_token 1500000',
336
+ },
337
+ {
338
+ cmd: '/stats',
339
+ desc: 'Show session statistics',
340
+ detail: 'View statistics information of current session',
341
+ example: '/stats',
342
+ },
343
+ {
344
+ cmd: '/about',
345
+ desc: 'Show version information',
346
+ detail: 'View version and related information of XAGENT CLI',
347
+ example: '/about',
348
+ },
349
+ {
350
+ cmd: '/update',
351
+ desc: 'Check for updates',
352
+ detail: 'Check for new versions and update xAgent CLI',
353
+ example: '/update',
354
+ },
355
+ ]);
356
+
357
+ // Keyboard Shortcuts
358
+ console.log('');
359
+ console.log(colors.border(separator));
360
+ console.log(colors.primaryBright('Keyboard Shortcuts'));
361
+ console.log(colors.border(separator));
362
+ console.log('');
363
+ console.log(colors.textMuted(' ESC - Cancel current operation'));
364
+ console.log(colors.textMuted(' Ctrl+C - Exit program'));
365
+ console.log('');
366
+ }
367
+
368
+ private showHelpCategory(
369
+ title: string,
370
+ commands: Array<{
371
+ cmd: string;
372
+ desc: string;
373
+ detail: string;
374
+ example: string;
375
+ modes?: string[];
376
+ }>
377
+ ): void {
378
+ const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80));
379
+
380
+ console.log('');
381
+ console.log(colors.border(separator));
382
+ console.log(colors.primaryBright(title));
383
+ console.log(colors.border(separator));
384
+ console.log('');
385
+
386
+ commands.forEach((cmd) => {
387
+ console.log(colors.primaryBright(` ${cmd.cmd}`));
388
+ console.log(colors.textDim(` ${cmd.desc}`));
389
+ console.log(colors.textMuted(` ${cmd.detail}`));
390
+
391
+ if (cmd.modes) {
392
+ console.log(colors.textDim(` Available modes:`));
393
+ cmd.modes.forEach((mode) => {
394
+ console.log(colors.textDim(` • ${mode}`));
395
+ });
396
+ }
397
+
398
+ console.log(colors.accent(` Examples:`));
399
+ cmd.example.split('\n').forEach((ex) => {
400
+ console.log(colors.codeText(` ${ex}`));
401
+ });
402
+ console.log('');
403
+ });
404
+ }
405
+
406
+ private async handleInit(): Promise<void> {
407
+ const spinner = ora('Initializing project...').start();
408
+
409
+ try {
410
+ await this.memoryManager.initializeProject(process.cwd());
411
+ spinner.succeed('Project initialized successfully');
412
+ } catch (error: any) {
413
+ spinner.fail(`Initialization failed: ${error.message}`);
414
+ }
415
+ }
416
+
417
+ private async handleClear(): Promise<void> {
418
+ // Clear local conversation history
419
+ this.conversationHistory = [];
420
+
421
+ // Clear current conversation in ConversationManager
422
+ await this.conversationManager.clearCurrentConversation();
423
+
424
+ // Call callback to notify InteractiveSession to clear conversation
425
+ if (this.onClearCallback) {
426
+ this.onClearCallback();
427
+ }
428
+
429
+ logger.success('Conversation history cleared', 'Start a new conversation');
430
+ }
431
+
432
+ private async handleExit(): Promise<void> {
433
+ logger.info('Goodbye!', 'Thank you for using xAgent CLI');
434
+ process.exit(0);
435
+ }
436
+
437
+ private async handleAuth(): Promise<void> {
438
+ logger.section('Authentication Management');
439
+
440
+ // Show current authentication configuration
441
+ const authConfig = this.configManager.getAuthConfig();
442
+ const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
443
+ const currentType = isRemote ? 'xAgent (Remote)' : 'Third-party API (Local)';
444
+
445
+ console.log(chalk.cyan('\n📋 Current Authentication Configuration:\n'));
446
+ console.log(` ${chalk.yellow('Mode:')} ${currentType}`);
447
+
448
+ if (isRemote) {
449
+ // Remote mode: show remote_llmModelName and remote_vlmModelName
450
+ const llmModel = authConfig.remote_llmModelName || 'Not set';
451
+ const vlmModel = authConfig.remote_vlmModelName || 'Not set';
452
+ console.log(` ${chalk.yellow('LLM Model:')} ${llmModel}`);
453
+ console.log(` ${chalk.yellow('VLM Model:')} ${vlmModel}`);
454
+ } else {
455
+ // Local mode: show modelName (LLM) and guiSubagentModel (VLM)
456
+ const llmModel = authConfig.modelName || 'Not set';
457
+ const vlmModel = this.configManager.get('guiSubagentModel') || 'Not set';
458
+ console.log(` ${chalk.yellow('LLM Model:')} ${llmModel}`);
459
+ console.log(` ${chalk.yellow('VLM Model:')} ${vlmModel}`);
460
+ }
461
+ console.log('');
462
+
463
+ const action = await select({
464
+ message: 'Select action:',
465
+ options: [
466
+ { value: 'switch', label: 'Switch authentication method' },
467
+ { value: 'back', label: 'Back' },
468
+ ],
469
+ });
470
+
471
+ if (action === 'back') {
472
+ return;
473
+ }
474
+
475
+ if (action === 'switch') {
476
+ // Use the same selection UI as initial setup
477
+ const confirmSwitch = await confirm({
478
+ message: `Switch from "${currentType}" to another authentication method?`,
479
+ });
480
+
481
+ if (confirmSwitch === false || confirmSwitch === undefined) {
482
+ return;
483
+ }
484
+
485
+ // Select authentication type (same as initial setup)
486
+ const authType = await selectAuthType();
487
+
488
+ if (authType === AuthType.OAUTH_XAGENT) {
489
+ // Switch to xAgent (Remote mode)
490
+ const authService = new AuthService({
491
+ type: AuthType.OAUTH_XAGENT,
492
+ apiKey: '',
493
+ baseUrl: '',
494
+ xagentApiBaseUrl: authConfig.xagentApiBaseUrl,
495
+ });
496
+
497
+ const success = await authService.authenticate();
498
+ if (success) {
499
+ const newAuthConfig = authService.getAuthConfig();
500
+ this.configManager.setAuthConfig({
501
+ selectedAuthType: newAuthConfig.type,
502
+ apiKey: newAuthConfig.apiKey,
503
+ refreshToken: newAuthConfig.refreshToken,
504
+ baseUrl: newAuthConfig.baseUrl,
505
+ modelName: '',
506
+ xagentApiBaseUrl: newAuthConfig.xagentApiBaseUrl,
507
+ guiSubagentModel: '',
508
+ guiSubagentBaseUrl: 'https://www.xagent-colife.net/v3',
509
+ guiSubagentApiKey: '',
510
+ });
511
+ // Set default remote model settings if not already set
512
+ // Fetch default models from /models/default endpoint
513
+ const webBaseUrl = newAuthConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
514
+ let defaultLlmName = '';
515
+ let defaultVlmName = '';
516
+
517
+ try {
518
+ console.log(chalk.cyan('\n📊 Fetching default models from remote server...'));
519
+ const defaults = await fetchDefaultModels(newAuthConfig.apiKey || '', webBaseUrl);
520
+
521
+ if (defaults.llm?.name) {
522
+ defaultLlmName = defaults.llm.name;
523
+ console.log(chalk.cyan(` LLM Model: ${defaults.llm.name}`));
524
+ }
525
+ if (defaults.vlm?.name) {
526
+ defaultVlmName = defaults.vlm.name;
527
+ console.log(chalk.cyan(` VLM Model: ${defaults.vlm.name}`));
528
+ }
529
+ } catch (error: any) {
530
+ console.log(chalk.yellow(` ⚠️ Failed to fetch default models: ${error.message}`));
531
+ console.log(chalk.yellow(' ⚠️ Use /model command to select models manually.'));
532
+ }
533
+
534
+ console.log('');
535
+
536
+ this.configManager.set('remote_llmModelName', defaultLlmName);
537
+ this.configManager.set('remote_vlmModelName', defaultVlmName);
538
+ this.configManager.save('global');
539
+
540
+ // Notify InteractiveSession to update aiClient config
541
+ if (this.onConfigUpdate) {
542
+ this.onConfigUpdate();
543
+ }
544
+
545
+ console.log(chalk.green('\n✅ Authentication switched to xAgent (Remote mode)!'));
546
+ // Removed: logging partial token for security
547
+ }
548
+ } else {
549
+ // Switch to Third-party API (Local mode)
550
+ const authService = new AuthService({
551
+ type: AuthType.OPENAI_COMPATIBLE,
552
+ apiKey: '',
553
+ baseUrl: '',
554
+ modelName: '',
555
+ });
556
+
557
+ const success = await authService.authenticate();
558
+ if (success) {
559
+ const newAuthConfig = authService.getAuthConfig();
560
+ this.configManager.setAuthConfig({
561
+ selectedAuthType: newAuthConfig.type,
562
+ apiKey: newAuthConfig.apiKey,
563
+ baseUrl: newAuthConfig.baseUrl,
564
+ modelName: newAuthConfig.modelName,
565
+ xagentApiBaseUrl: '',
566
+ refreshToken: '',
567
+ guiSubagentModel: '',
568
+ guiSubagentBaseUrl: '',
569
+ guiSubagentApiKey: '',
570
+ });
571
+ this.configManager.save('global');
572
+
573
+ // Notify InteractiveSession to update aiClient config
574
+ if (this.onConfigUpdate) {
575
+ this.onConfigUpdate();
576
+ }
577
+
578
+ console.log(chalk.green('\n✅ Authentication switched to Third-party API (Local mode)!'));
579
+ }
580
+ }
581
+ }
582
+ }
583
+
584
+ private async handleLogin(): Promise<void> {
585
+ logger.section('Login to xAgent');
586
+
587
+ const authConfig = this.configManager.getAuthConfig();
588
+ const currentAuthType = authConfig.type;
589
+
590
+ if (currentAuthType !== AuthType.OAUTH_XAGENT) {
591
+ console.log(chalk.yellow('\n⚠️ Current authentication type is not OAuth xAgent.'));
592
+ const proceed = await confirm({
593
+ message: 'Do you want to switch to OAuth xAgent authentication?',
594
+ });
595
+
596
+ if (proceed === false || proceed === undefined) {
597
+ return;
598
+ }
599
+
600
+ // Switch to OAuth xAgent
601
+ this.configManager.setAuthConfig({
602
+ selectedAuthType: AuthType.OAUTH_XAGENT,
603
+ apiKey: '',
604
+ refreshToken: '',
605
+ baseUrl: '',
606
+ });
607
+ this.configManager.save('global');
608
+ console.log(chalk.green('✅ Switched to OAuth xAgent authentication.'));
609
+ }
610
+
611
+ console.log(chalk.cyan('\n🔐 Starting OAuth xAgent login...'));
612
+ console.log(chalk.gray(' A browser will open for you to complete authentication.\n'));
613
+
614
+ try {
615
+ // Get xagentApiBaseUrl from config (respects XAGENT_BASE_URL env var)
616
+ const config = this.configManager.getAuthConfig();
617
+
618
+ const authService = new AuthService({
619
+ type: AuthType.OAUTH_XAGENT,
620
+ apiKey: '',
621
+ baseUrl: '',
622
+ refreshToken: '',
623
+ xagentApiBaseUrl: config.xagentApiBaseUrl,
624
+ });
625
+
626
+ const success = await authService.authenticate();
627
+
628
+ if (success) {
629
+ const newConfig = this.configManager.getAuthConfig();
630
+ console.log(chalk.green('\n✅ Login successful!'));
631
+ console.log(chalk.cyan(` Token saved to: ~/.xagent/settings.json`));
632
+ console.log(chalk.gray(' You can now use xAgent CLI with remote AI services.\n'));
633
+ } else {
634
+ console.log(chalk.red('\n❌ Login failed or was cancelled.'));
635
+ }
636
+ } catch (error: any) {
637
+ console.log(chalk.red(`\n❌ Login error: ${error.message || 'Unknown error'}`));
638
+ }
639
+ }
640
+
641
+ /**
642
+ * Handle /model command - Configure LLM/VLM models
643
+ * Supports both remote mode (xAgent) and local mode (third-party API)
644
+ */
645
+ private async handleModel(): Promise<void> {
646
+ const authConfig = this.configManager.getAuthConfig();
647
+
648
+ // Determine mode and show appropriate UI
649
+ if (authConfig.type === AuthType.OAUTH_XAGENT) {
650
+ // Remote mode - use backend models
651
+ await this.handleRemoteModel();
652
+ } else {
653
+ // Local mode - configure third-party API directly
654
+ await this.handleLocalModel();
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Handle /model command for remote mode - Configure models from backend
660
+ */
661
+ private async handleRemoteModel(): Promise<void> {
662
+ const authConfig = this.configManager.getAuthConfig();
663
+
664
+ // Auto-fetch default models if not set
665
+ let currentLlm = authConfig.remote_llmModelName;
666
+ let currentVlm = authConfig.remote_vlmModelName;
667
+
668
+ if (!currentLlm || !currentVlm) {
669
+ console.log(chalk.cyan('\n📊 Fetching default models from remote server...'));
670
+
671
+ // Try to use RemoteAIClient first, otherwise fetch directly
672
+ let defaults: { llm?: { name: string; displayName?: string }; vlm?: { name: string; displayName?: string } } | null = null;
673
+
674
+ if (this.remoteAIClient) {
675
+ try {
676
+ defaults = await this.remoteAIClient.getDefaultModels();
677
+ } catch (error: any) {
678
+ console.log(chalk.yellow(` ⚠️ Failed to get defaults from RemoteAIClient: ${error.message}`));
679
+ }
680
+ }
681
+
682
+ // If RemoteAIClient failed or not available, fetch directly
683
+ if (!defaults) {
684
+ try {
685
+ const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
686
+ defaults = await fetchDefaultModels(authConfig.apiKey || '', webBaseUrl);
687
+ } catch (error: any) {
688
+ console.log(chalk.yellow(` ⚠️ Failed to fetch default models: ${error.message}`));
689
+ }
690
+ }
691
+
692
+ if (defaults) {
693
+ if (!currentLlm && defaults.llm?.name) {
694
+ currentLlm = defaults.llm.name;
695
+ this.configManager.set('remote_llmModelName', currentLlm);
696
+ console.log(chalk.cyan(` Default LLM: ${defaults.llm.displayName || currentLlm}`));
697
+ }
698
+ if (!currentVlm && defaults.vlm?.name) {
699
+ currentVlm = defaults.vlm.name;
700
+ this.configManager.set('remote_vlmModelName', currentVlm);
701
+ console.log(chalk.cyan(` Default VLM: ${defaults.vlm.displayName || currentVlm}`));
702
+ }
703
+ this.configManager.save('global');
704
+ } else {
705
+ console.log(chalk.yellow(' ⚠️ Use /auth to configure remote mode first.'));
706
+ return;
707
+ }
708
+ }
709
+
710
+ // Get RemoteAIClient instance (from InteractiveSession) for model selection
711
+ const remoteClient = this.remoteAIClient;
712
+
713
+ // Display current configuration
714
+ console.log(chalk.cyan('\n📊 Current Model Configuration:\n'));
715
+ console.log(` ${chalk.yellow('LLM Model:')} ${currentLlm || 'Not set'}`);
716
+ console.log(` ${chalk.yellow('VLM Model:')} ${currentVlm || 'Not set'}`);
717
+ console.log('');
718
+
719
+ // Main menu
720
+ const action = await select({
721
+ message: 'Select action:',
722
+ options: [
723
+ { value: 'llm', label: 'Change LLM model' },
724
+ { value: 'vlm', label: 'Change VLM model' },
725
+ { value: 'back', label: 'Back' },
726
+ ],
727
+ }) as string | symbol;
728
+
729
+ if (action === 'back' || typeof action === 'symbol') {
730
+ return;
731
+ }
732
+
733
+ // Restore stdin raw mode after @clack/prompts interaction
734
+ ensureTtySane();
735
+
736
+ // Get and display provider list
737
+ try {
738
+ const models = await remoteClient.getRemoteModels();
739
+ const modelList = action === 'llm' ? models.llm : models.vlm;
740
+
741
+ if (modelList.length === 0) {
742
+ console.log(chalk.yellow('\n⚠️ No models available.'));
743
+ return;
744
+ }
745
+
746
+ // Build choice list
747
+ const choices = modelList.map((m: any) => ({
748
+ name: `${m.displayName} (${m.name})`,
749
+ value: m.name
750
+ }));
751
+
752
+ const selectedModel = await select({
753
+ message: action === 'llm' ? 'Select LLM Model:' : 'Select VLM Model:',
754
+ options: choices
755
+ });
756
+
757
+ if (typeof selectedModel !== 'string') {
758
+ return;
759
+ }
760
+
761
+ const configKey = action === 'llm' ? 'remote_llmModelName' : 'remote_vlmModelName';
762
+ this.configManager.set(configKey, selectedModel);
763
+ this.configManager.save('global');
764
+
765
+ // Clear conversation history to avoid tool call ID conflicts between providers
766
+ if (this.onClearCallback) {
767
+ this.onClearCallback();
768
+ console.log(
769
+ chalk.cyan(' Conversation cleared to avoid tool call ID conflicts between models.')
770
+ );
771
+ }
772
+
773
+ // Notify InteractiveSession to update aiClient config
774
+ if (this.onConfigUpdate) {
775
+ this.onConfigUpdate();
776
+ }
777
+
778
+ console.log(chalk.green('\n✅ Model updated successfully!'));
779
+ console.log(` ${action === 'llm' ? 'LLM' : 'VLM'}: ${selectedModel}`);
780
+ } catch (error: any) {
781
+ console.log(chalk.red(`\n❌ Failed to get models: ${error.message}`));
782
+ }
783
+ }
784
+
785
+ /**
786
+ * Handle /model command for local mode - Configure third-party API directly
787
+ * Reuses code from initial setup flow (authenticateWithOpenAICompatible and configureAndValidateVLM)
788
+ */
789
+ private async handleLocalModel(): Promise<void> {
790
+ // Display current configuration
791
+ const currentLlmModel = this.configManager.get('modelName') || this.configManager.getAuthConfig().modelName;
792
+ const currentVlmModel = this.configManager.get('guiSubagentModel');
793
+
794
+ console.log(chalk.cyan('\n📊 Current Local Model Configuration:\n'));
795
+ console.log(` ${chalk.yellow('LLM Model:')} ${currentLlmModel || 'Not set'}`);
796
+ console.log(` ${chalk.yellow('VLM Model:')} ${currentVlmModel || 'Not set'}`);
797
+ console.log('');
798
+
799
+ // Main menu - choose to configure LLM or VLM
800
+ const action = await select({
801
+ message: 'Select action:',
802
+ options: [
803
+ { value: 'llm', label: 'Change LLM (select provider and API)' },
804
+ { value: 'vlm', label: 'Change VLM (for GUI automation)' },
805
+ { value: 'back', label: 'Back' },
806
+ ],
807
+ }) as string | symbol;
808
+
809
+ if (action === 'back' || typeof action === 'symbol') {
810
+ return;
811
+ }
812
+
813
+ if (action === 'llm') {
814
+ await this.configureLocalLLM();
815
+ } else if (action === 'vlm') {
816
+ await this.configureLocalVLM();
817
+ }
818
+ }
819
+
820
+ /**
821
+ * Configure LLM for local mode - Reuses authenticateWithOpenAICompatible logic
822
+ */
823
+ private async configureLocalLLM(): Promise<void> {
824
+ // THIRD_PARTY_PROVIDERS already imported at top level
825
+ const provider = await select({
826
+ message: 'Select third-party model provider:',
827
+ options: THIRD_PARTY_PROVIDERS.map((p) => ({
828
+ value: p,
829
+ label: `${p.name} - ${p.description}`,
830
+ })),
831
+ }) as ThirdPartyProvider | symbol;
832
+
833
+ if (typeof provider === 'symbol') {
834
+ return;
835
+ }
836
+
837
+ const selectedProvider = provider;
838
+
839
+ let baseUrl = selectedProvider.baseUrl;
840
+ let modelName = selectedProvider.defaultModel;
841
+
842
+ if (selectedProvider.name === 'Custom') {
843
+ baseUrl = (await import('@clack/prompts').then(m =>
844
+ m.text({
845
+ message: 'Enter API Base URL:',
846
+ defaultValue: 'https://api.openai.com/v1',
847
+ validate: (value: string | undefined) => {
848
+ if (!value || value.trim().length === 0) {
849
+ return 'Base URL cannot be empty';
850
+ }
851
+ return undefined;
852
+ },
853
+ })
854
+ )) as string;
855
+
856
+ modelName = (await import('@clack/prompts').then(m =>
857
+ m.text({
858
+ message: 'Enter model name:',
859
+ defaultValue: 'gpt-4',
860
+ validate: (value: string | undefined) => {
861
+ if (!value || value.trim().length === 0) {
862
+ return 'Model name cannot be empty';
863
+ }
864
+ return undefined;
865
+ },
866
+ })
867
+ )) as string;
868
+
869
+ baseUrl = baseUrl.trim();
870
+ modelName = modelName.trim();
871
+ } else {
872
+ console.log(chalk.cyan(`\nSelected: ${selectedProvider.name}`));
873
+ console.log(chalk.cyan(`API URL: ${baseUrl}`));
874
+
875
+ if (selectedProvider.models && selectedProvider.models.length > 0) {
876
+ console.log(chalk.cyan(`Available models: ${selectedProvider.models.join(', ')}`));
877
+
878
+ const selectedModel = await select({
879
+ message: 'Select model:',
880
+ options: selectedProvider.models.map((model) => ({
881
+ value: model,
882
+ label: model === selectedProvider.defaultModel ? `${model} (default)` : model,
883
+ })),
884
+ }) as string | symbol;
885
+
886
+ if (typeof selectedModel === 'symbol') {
887
+ return;
888
+ }
889
+
890
+ modelName = selectedModel as string;
891
+ } else {
892
+ console.log(chalk.cyan(`Default model: ${modelName}`));
893
+
894
+ const confirmModel = (await import('@clack/prompts').then(m =>
895
+ m.text({
896
+ message: `Enter model name (press Enter to use default value ${modelName}):`,
897
+ defaultValue: modelName,
898
+ validate: (value: string | undefined) => {
899
+ if (!value || value.trim().length === 0) {
900
+ return 'Model name cannot be empty';
901
+ }
902
+ return undefined;
903
+ },
904
+ })
905
+ )) as string;
906
+
907
+ modelName = confirmModel.trim();
908
+ }
909
+ }
910
+
911
+ const apiKey = (await import('@clack/prompts').then(m =>
912
+ m.password({
913
+ message: `Enter ${selectedProvider.name} API Key:`,
914
+ validate: (value: string | undefined) => {
915
+ if (!value || value.trim().length === 0) {
916
+ return 'API Key cannot be empty';
917
+ }
918
+ return undefined;
919
+ },
920
+ })
921
+ )) as string;
922
+
923
+ // Update config
924
+ this.configManager.set('baseUrl', baseUrl);
925
+ this.configManager.set('apiKey', apiKey.trim());
926
+ this.configManager.set('modelName', modelName);
927
+ this.configManager.save('global');
928
+
929
+ console.log(chalk.green('\n✅ LLM configuration updated successfully!'));
930
+ console.log(chalk.cyan(` Provider: ${selectedProvider.name}`));
931
+ console.log(chalk.cyan(` Model: ${modelName}`));
932
+ console.log(chalk.cyan(` API URL: ${baseUrl}`));
933
+
934
+ // Clear conversation and notify config update
935
+ if (this.onClearCallback) {
936
+ this.onClearCallback();
937
+ }
938
+ if (this.onConfigUpdate) {
939
+ this.onConfigUpdate();
940
+ }
941
+ }
942
+
943
+ /**
944
+ * Configure VLM for local mode - Select provider and modelName (baseUrl from VLM_PROVIDERS)
945
+ */
946
+ private async configureLocalVLM(): Promise<void> {
947
+ // Get current VLM config
948
+ const currentModel = this.configManager.get('guiSubagentModel');
949
+ const currentBaseUrl = this.configManager.get('guiSubagentBaseUrl');
950
+
951
+ console.log(chalk.cyan('\n📊 Current VLM Configuration:\n'));
952
+ console.log(` ${chalk.yellow('Model:')} ${currentModel || 'Not set'}`);
953
+ console.log(` ${chalk.yellow('Base URL:')} ${currentBaseUrl || 'Not set'}`);
954
+ console.log('');
955
+
956
+ // Step 1: Select VLM provider
957
+ const provider = await select({
958
+ message: 'Select VLM provider:',
959
+ options: VLM_PROVIDERS.map((p) => ({
960
+ value: p,
961
+ label: `${p.name} - ${p.defaultModel}`,
962
+ })),
963
+ }) as VLMProviderInfo | symbol;
964
+
965
+ // User cancelled
966
+ if (typeof provider === 'symbol') {
967
+ return;
968
+ }
969
+
970
+ const selectedProvider = provider;
971
+
972
+ // Step 2: Select model from provider's models list
973
+ const model = await select({
974
+ message: 'Select VLM Model:',
975
+ options: selectedProvider.models.map((m) => ({
976
+ value: m,
977
+ label: m,
978
+ })),
979
+ }) as string | symbol;
980
+
981
+ // User cancelled
982
+ if (typeof model === 'symbol') {
983
+ return;
984
+ }
985
+
986
+ // Step 3: Get API Key
987
+ const apiKey = (await text({
988
+ message: `Enter ${selectedProvider.name} API Key:`,
989
+ validate: (value: string | undefined) => {
990
+ if (!value || value.trim().length === 0) {
991
+ return 'API Key cannot be empty';
992
+ }
993
+ return undefined;
994
+ },
995
+ })) as string;
996
+
997
+ // Save configuration
998
+ this.configManager.set('guiSubagentModel', model);
999
+ this.configManager.set('guiSubagentBaseUrl', selectedProvider.baseUrl);
1000
+ this.configManager.set('guiSubagentApiKey', apiKey);
1001
+ this.configManager.save('global');
1002
+
1003
+ console.log(chalk.green('\n✅ VLM configuration updated successfully!'));
1004
+ console.log(chalk.cyan(` Provider: ${selectedProvider.name}`));
1005
+ console.log(chalk.cyan(` Model: ${model}`));
1006
+ console.log(chalk.cyan(` Base URL: ${selectedProvider.baseUrl}`));
1007
+
1008
+ // Notify config update
1009
+ if (this.onConfigUpdate) {
1010
+ this.onConfigUpdate();
1011
+ }
1012
+ }
1013
+
1014
+ private async handleMode(args: string[]): Promise<void> {
1015
+ const modes = Object.values(ExecutionMode);
1016
+ const currentMode =
1017
+ this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
1018
+
1019
+ if (args.length > 0) {
1020
+ const newMode = args[0].toLowerCase();
1021
+ if (modes.includes(newMode as ExecutionMode)) {
1022
+ this.configManager.setApprovalMode(newMode as ExecutionMode);
1023
+ this.configManager.save('global');
1024
+ console.log(chalk.green(`✅ Approval mode changed to: ${newMode}`));
1025
+ } else {
1026
+ console.log(chalk.red(`❌ Invalid mode: ${newMode}`));
1027
+ console.log(chalk.gray(`Available modes: ${modes.join(', ')}`));
1028
+ }
1029
+ } else {
1030
+ console.log(chalk.cyan('\n🎯 Approval Modes:\n'));
1031
+ console.log(` Current: ${chalk.green(currentMode)}\n`);
1032
+
1033
+ const descriptions = [
1034
+ { mode: 'yolo', desc: 'Execute commands without confirmation' },
1035
+ { mode: 'accept_edits', desc: 'Accept all edits automatically' },
1036
+ { mode: 'plan', desc: 'Plan before executing' },
1037
+ { mode: 'default', desc: 'Safe execution with confirmations' },
1038
+ { mode: 'smart', desc: 'Smart approval with intelligent security checks' },
1039
+ ];
1040
+
1041
+ descriptions.forEach(({ mode, desc }) => {
1042
+ const current = mode === currentMode ? chalk.green(' [current]') : '';
1043
+ console.log(` ${chalk.yellow(mode)}${current}`);
1044
+ console.log(` ${chalk.gray(desc)}`);
1045
+ });
1046
+
1047
+ console.log();
1048
+ }
1049
+ }
1050
+
1051
+ private async handleThink(args: string[]): Promise<void> {
1052
+ const thinkingConfig = this.configManager.getThinkingConfig();
1053
+
1054
+ if (args.length > 0) {
1055
+ const action = args[0].toLowerCase();
1056
+
1057
+ if (action === 'on' || action === 'true' || action === '1') {
1058
+ thinkingConfig.enabled = true;
1059
+ this.configManager.setThinkingConfig(thinkingConfig);
1060
+ this.configManager.save('global');
1061
+ console.log(chalk.green('✅ Thinking mode enabled'));
1062
+ } else if (action === 'off' || action === 'false' || action === '0') {
1063
+ thinkingConfig.enabled = false;
1064
+ this.configManager.setThinkingConfig(thinkingConfig);
1065
+ this.configManager.save('global');
1066
+ console.log(chalk.green('✅ Thinking mode disabled'));
1067
+ } else if (action === 'display' && args[1]) {
1068
+ const displayMode = args[1].toLowerCase();
1069
+ const validModes = ['full', 'compact', 'indicator'];
1070
+
1071
+ if (validModes.includes(displayMode)) {
1072
+ thinkingConfig.displayMode = displayMode as 'full' | 'compact' | 'indicator';
1073
+ thinkingConfig.enabled = true;
1074
+ this.configManager.setThinkingConfig(thinkingConfig);
1075
+ this.configManager.save('global');
1076
+ console.log(chalk.green(`✅ Thinking display mode set to: ${displayMode}`));
1077
+ } else {
1078
+ console.log(chalk.red(`❌ Invalid display mode: ${displayMode}`));
1079
+ console.log(chalk.gray(`Valid modes: ${validModes.join(', ')}`));
1080
+ }
1081
+ } else {
1082
+ console.log(chalk.red(`❌ Invalid action: ${action}`));
1083
+ console.log(chalk.gray('Usage: /think [on|off|display <mode>]'));
1084
+ }
1085
+ } else {
1086
+ console.log(chalk.cyan('\n🧠 Thinking Mode:\n'));
1087
+ console.log(
1088
+ ` Status: ${thinkingConfig.enabled ? chalk.green('Enabled') : chalk.red('Disabled')}`
1089
+ );
1090
+ console.log(` Mode: ${chalk.yellow(thinkingConfig.mode)}`);
1091
+ console.log(` Display: ${chalk.yellow(thinkingConfig.displayMode)}\n`);
1092
+
1093
+ console.log(chalk.gray('Usage:'));
1094
+ console.log(chalk.gray(' /think on - Enable thinking mode'));
1095
+ console.log(chalk.gray(' /think off - Disable thinking mode'));
1096
+ console.log(chalk.gray(' /think display full - Show full thinking process'));
1097
+ console.log(chalk.gray(' /think display compact - Show compact thinking (500 chars)'));
1098
+ console.log(chalk.gray(' /think display indicator - Show only indicator'));
1099
+ console.log();
1100
+ }
1101
+ }
1102
+
1103
+ private async handleAgents(args: string[]): Promise<void> {
1104
+ const action = args[0] || 'list';
1105
+
1106
+ switch (action) {
1107
+ case 'list':
1108
+ await this.listAgents();
1109
+ break;
1110
+ case 'online':
1111
+ logger.warn('Online marketplace not implemented yet', 'Check back later for updates');
1112
+ break;
1113
+ case 'install':
1114
+ logger.warn(
1115
+ 'Agent installation wizard not implemented yet',
1116
+ 'Use /agents install in interactive mode'
1117
+ );
1118
+ break;
1119
+ case 'remove':
1120
+ logger.warn('Agent removal not implemented yet', 'Use /agents remove in interactive mode');
1121
+ break;
1122
+ default:
1123
+ logger.warn(
1124
+ `Unknown agents action: ${action}`,
1125
+ 'Use /agents list to see available actions'
1126
+ );
1127
+ }
1128
+ }
1129
+
1130
+ private async listAgents(): Promise<void> {
1131
+ const agents = this.agentManager.getAllAgents();
1132
+
1133
+ if (agents.length === 0) {
1134
+ logger.warn('No agents configured', 'Use /agents install to add agents');
1135
+ return;
1136
+ }
1137
+
1138
+ logger.section('Available Agents');
1139
+
1140
+ agents.forEach((agent: AgentConfig) => {
1141
+ const color = agent.color || '#FFFFFF';
1142
+ logger.info(` ${chalk.hex(color)(agent.name || agent.agentType)}`);
1143
+ logger.info(` Type: ${agent.agentType}`);
1144
+ logger.info(` ${agent.whenToUse}`);
1145
+ });
1146
+ }
1147
+
1148
+ private async handleMcp(args: string[]): Promise<void> {
1149
+ const action = args[0] || 'list';
1150
+
1151
+ switch (action) {
1152
+ case 'list':
1153
+ await this.listMcpServers();
1154
+ break;
1155
+ case 'add':
1156
+ if (args[1]) {
1157
+ // Non-interactive mode: use command line arguments
1158
+ await this.addMcpServerInteractive(args[1]);
1159
+ } else {
1160
+ // Interactive mode
1161
+ await this.addMcpServerInteractive();
1162
+ }
1163
+ break;
1164
+ case 'remove':
1165
+ if (args[1]) {
1166
+ // Non-interactive mode
1167
+ await this.removeMcpServer(args[1]);
1168
+ } else {
1169
+ // Interactive mode
1170
+ await this.removeMcpServerInteractive();
1171
+ }
1172
+ break;
1173
+ case 'refresh':
1174
+ await this.refreshMcpServers();
1175
+ break;
1176
+ default:
1177
+ logger.warn(`Unknown MCP action: ${action}`, 'Use /mcp list to see available actions');
1178
+ }
1179
+ }
1180
+
1181
+ private async listMcpServers(): Promise<void> {
1182
+ const serverConfigs = this.mcpManager.getAllServerConfigs();
1183
+
1184
+ if (serverConfigs.length === 0) {
1185
+ logger.section('MCP Servers');
1186
+ logger.warn('No MCP servers configured');
1187
+ logger.info('Use /mcp add to add a new MCP server');
1188
+ return;
1189
+ }
1190
+
1191
+ logger.section('MCP Servers');
1192
+
1193
+ serverConfigs.forEach(
1194
+ ({ name: serverName, config: serverConfig }: { name: string; config: any }) => {
1195
+ const server = this.mcpManager.getServer(serverName);
1196
+ const isConnected = server?.isServerConnected() || false;
1197
+ const status = isConnected ? chalk.green('✓ Connected') : chalk.red('✗ Disconnected');
1198
+ const tools = server?.getToolNames() || [];
1199
+ const transport = serverConfig?.transport || serverConfig?.type || 'unknown';
1200
+ const command = serverConfig?.command
1201
+ ? `${serverConfig.command} ${(serverConfig.args || []).join(' ')}`
1202
+ : serverConfig?.url || 'N/A';
1203
+
1204
+ console.log('');
1205
+ console.log(` ${chalk.cyan(serverName)} ${status}`);
1206
+ console.log(` Transport: ${transport}`);
1207
+ console.log(` Command: ${command}`);
1208
+ console.log(
1209
+ ` Tools: ${isConnected ? tools.length : 'N/A'} (${isConnected ? tools.join(', ') : 'wait for connection'})`
1210
+ );
1211
+ }
1212
+ );
1213
+
1214
+ console.log('');
1215
+ logger.info(`Total: ${serverConfigs.length} server(s)`);
1216
+ }
1217
+
1218
+ private async addMcpServerInteractive(serverName?: string): Promise<void> {
1219
+ let name = (await text({
1220
+ message: 'Enter MCP server name:',
1221
+ defaultValue: serverName,
1222
+ validate: (value: string | undefined) => {
1223
+ if (!value || !value.trim()) {
1224
+ return 'Server name is required';
1225
+ }
1226
+ if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
1227
+ return 'Server name must contain only alphanumeric characters, hyphens, and underscores';
1228
+ }
1229
+ const servers = this.mcpManager.getAllServers();
1230
+ if (servers.some((s: MCPServer) => (s as any).config?.name === value)) {
1231
+ return 'Server with this name already exists';
1232
+ }
1233
+ return undefined;
1234
+ },
1235
+ })) as string;
1236
+
1237
+ const transport = await select({
1238
+ message: 'Select transport type:',
1239
+ options: [
1240
+ { value: 'stdio', label: 'Stdio (stdin/stdout)' },
1241
+ { value: 'sse', label: 'HTTP/SSE' },
1242
+ { value: 'http', label: 'HTTP (POST)' },
1243
+ ],
1244
+ }) as string | symbol;
1245
+
1246
+ if (typeof transport === 'symbol') {
1247
+ return;
1248
+ }
1249
+
1250
+ let command = '';
1251
+ let serverArgs: string[] = [];
1252
+ let url = '';
1253
+ let authToken = '';
1254
+ let headers: Record<string, string> | string | undefined;
1255
+
1256
+ if (transport === 'stdio') {
1257
+ command = (await text({
1258
+ message: 'Enter command (for stdio transport):',
1259
+ validate: (value: string | undefined) =>
1260
+ value && value.trim() ? undefined : 'Command is required',
1261
+ })) as string;
1262
+
1263
+ const argsInput = (await text({
1264
+ message: 'Enter arguments (comma-separated, for stdio transport):',
1265
+ defaultValue: '',
1266
+ })) as string;
1267
+
1268
+ if (argsInput.trim()) {
1269
+ serverArgs = argsInput.split(',').map((a: string) => a.trim());
1270
+ }
1271
+ } else {
1272
+ url = (await text({
1273
+ message: 'Enter server URL (for HTTP/SSE/HTTP transport):',
1274
+ validate: (value: string | undefined) => {
1275
+ if (!value || !value.trim()) {
1276
+ return 'URL is required';
1277
+ }
1278
+ try {
1279
+ new URL(value);
1280
+ return undefined;
1281
+ } catch {
1282
+ return 'Invalid URL format (e.g., https://example.com)';
1283
+ }
1284
+ },
1285
+ })) as string;
1286
+
1287
+ authToken = (await text({
1288
+ message: 'Enter authentication token (optional):',
1289
+ defaultValue: '',
1290
+ })) as string;
1291
+
1292
+ const headersInput = (await text({
1293
+ message:
1294
+ 'Enter custom headers as JSON (optional, e.g., {"Authorization": "Bearer token"}):',
1295
+ defaultValue: '',
1296
+ })) as string;
1297
+
1298
+ if (headersInput.trim()) {
1299
+ try {
1300
+ headers = JSON.parse(headersInput);
1301
+ } catch {
1302
+ headers = undefined;
1303
+ }
1304
+ }
1305
+ }
1306
+
1307
+ const config: any = {
1308
+ transport: transport as 'stdio' | 'sse' | 'http',
1309
+ };
1310
+
1311
+ if (transport === 'stdio') {
1312
+ config.command = command;
1313
+ if (serverArgs && serverArgs.length > 0) {
1314
+ config.args = serverArgs;
1315
+ }
1316
+ } else {
1317
+ config.url = url;
1318
+
1319
+ // Handle user input that mistakenly puts Bearer token in headers field
1320
+ // Detect pattern: headers looks like "Bearer xxx.yyy.zzz" (JWT token)
1321
+ let resolvedAuthToken = authToken;
1322
+ let resolvedHeaders = headers;
1323
+
1324
+ if (headers && typeof headers === 'string' && !authToken) {
1325
+ const trimmedHeaders = headers.trim();
1326
+ if (trimmedHeaders.startsWith('Bearer ') && trimmedHeaders.split('.').length === 3) {
1327
+ // User mistakenly put token in headers field - extract it
1328
+ resolvedAuthToken = trimmedHeaders;
1329
+ resolvedHeaders = undefined;
1330
+ console.log('[MCP] Note: Detected Bearer token in headers field, moved to authToken');
1331
+ }
1332
+ }
1333
+
1334
+ if (resolvedAuthToken) {
1335
+ config.authToken = resolvedAuthToken;
1336
+ }
1337
+ if (resolvedHeaders) {
1338
+ config.headers = resolvedHeaders;
1339
+ }
1340
+ }
1341
+
1342
+ try {
1343
+ this.configManager.addMcpServer(name, config);
1344
+ this.configManager.save('global');
1345
+
1346
+ this.mcpManager.registerServer(name, config);
1347
+
1348
+ let connected = false;
1349
+ try {
1350
+ await this.mcpManager.connectServer(name);
1351
+ connected = true;
1352
+ } catch (error: any) {
1353
+ this.mcpManager.disconnectServer(name);
1354
+ this.configManager.removeMcpServer(name);
1355
+ this.configManager.save('global');
1356
+ throw new Error(`Connection failed: ${error.message}`);
1357
+ }
1358
+
1359
+ // Register MCP tools with simple names
1360
+ const allMcpTools = this.mcpManager.getAllTools();
1361
+ const toolRegistry = getToolRegistry();
1362
+ toolRegistry.registerMCPTools(allMcpTools);
1363
+
1364
+ // Update system prompt to include new MCP tools
1365
+ if (this.onSystemPromptUpdate) {
1366
+ await this.onSystemPromptUpdate();
1367
+ }
1368
+
1369
+ console.log(chalk.green(`✅ MCP server '${name}' added and connected successfully`));
1370
+ } catch (error: any) {
1371
+ console.log(chalk.red(`❌ Failed to add MCP server: ${error.message}`));
1372
+ }
1373
+ }
1374
+
1375
+ private async removeMcpServerInteractive(): Promise<void> {
1376
+ const servers = this.mcpManager.getAllServers();
1377
+
1378
+ if (servers.length === 0) {
1379
+ logger.warn('No MCP servers configured', 'Use /mcp add to add servers');
1380
+ return;
1381
+ }
1382
+
1383
+ const serverOptions = servers.map((s: MCPServer) => {
1384
+ const tools = s.getToolNames();
1385
+ const status = s.isServerConnected() ? '✓' : '✗';
1386
+ return {
1387
+ value: (s as any).config?.name,
1388
+ label: `${status} ${(s as any).config?.name || 'unknown'} (${tools.length} tools)`,
1389
+ };
1390
+ });
1391
+
1392
+ const serverName = (await select({
1393
+ message: 'Select MCP server to remove:',
1394
+ options: serverOptions,
1395
+ })) as string | symbol;
1396
+
1397
+ if (typeof serverName === 'symbol') {
1398
+ return;
1399
+ }
1400
+
1401
+ await this.removeMcpServer(serverName);
1402
+ }
1403
+
1404
+ private async removeMcpServer(serverName: string): Promise<void> {
1405
+ try {
1406
+ // Get server info before disconnecting to notify LLM
1407
+ const server = this.mcpManager.getServer(serverName);
1408
+ const removedTools = server ? server.getToolNames() : [];
1409
+ const removedToolNames = removedTools.map((t: string) => `${serverName}__${t}`).join(', ');
1410
+
1411
+ // Disconnect
1412
+ this.mcpManager.disconnectServer(serverName);
1413
+
1414
+ // Unregister MCP tools for this server
1415
+ const toolRegistry = getToolRegistry();
1416
+ toolRegistry.unregisterMCPTools(serverName);
1417
+
1418
+ // Remove from config
1419
+ this.configManager.removeMcpServer(serverName);
1420
+ this.configManager.save('global');
1421
+
1422
+ // Update system prompt to reflect removed MCP tools
1423
+ if (this.onSystemPromptUpdate) {
1424
+ await this.onSystemPromptUpdate();
1425
+ }
1426
+
1427
+ console.log(chalk.green(`✅ MCP server '${serverName}' removed successfully`));
1428
+ } catch (error: any) {
1429
+ console.log(chalk.red(`❌ Failed to remove MCP server: ${error.message}`));
1430
+ }
1431
+ }
1432
+
1433
+ private async refreshMcpServers(): Promise<void> {
1434
+ const spinner = ora({ text: 'Refreshing MCP servers...', interval: 200 }).start();
1435
+
1436
+ try {
1437
+ // Disconnect all existing connections
1438
+ this.mcpManager.disconnectAllServers();
1439
+
1440
+ // Reconnect all servers
1441
+ await this.mcpManager.connectAllServers();
1442
+
1443
+ spinner.succeed('MCP servers refreshed successfully');
1444
+
1445
+ // Show current server status
1446
+ await this.listMcpServers();
1447
+ } catch (error: any) {
1448
+ spinner.fail(`Failed to refresh MCP servers: ${error.message}`);
1449
+ }
1450
+ }
1451
+
1452
+ private async handleMemory(args: string[]): Promise<void> {
1453
+ const action = args[0] || 'show';
1454
+
1455
+ // Load memory files before showing
1456
+ try {
1457
+ await this.memoryManager.loadMemory();
1458
+ } catch (error) {
1459
+ logger.error(
1460
+ 'Failed to load memory files',
1461
+ error instanceof Error ? error.message : String(error)
1462
+ );
1463
+ return;
1464
+ }
1465
+
1466
+ switch (action) {
1467
+ case 'show':
1468
+ await this.showMemory();
1469
+ break;
1470
+ case 'clear':
1471
+ await this.clearMemory(args[1]);
1472
+ break;
1473
+ default:
1474
+ logger.warn(
1475
+ `Unknown memory action: ${action}`,
1476
+ 'Use /memory show to see available actions'
1477
+ );
1478
+ }
1479
+ }
1480
+
1481
+ private async clearMemory(target?: string): Promise<void> {
1482
+ const memoryFiles = this.memoryManager.getMemoryFiles();
1483
+
1484
+ // Handle different clear targets
1485
+ if (!target || target === '.' || target === 'current') {
1486
+ // Clear current project's memory
1487
+ const currentProjectMemory = memoryFiles.find((m: MemoryFile) => m.level === 'project');
1488
+ if (currentProjectMemory) {
1489
+ await fs.unlink(currentProjectMemory.path);
1490
+ logger.success('Project memory cleared');
1491
+ logger.info('Use /init to initialize if needed');
1492
+ } else {
1493
+ logger.warn('No project memory found for current directory');
1494
+ }
1495
+ return;
1496
+ }
1497
+
1498
+ if (target === 'all') {
1499
+ // Clear all memories including global
1500
+ let cleared = 0;
1501
+ for (const file of memoryFiles) {
1502
+ await fs.unlink(file.path);
1503
+ cleared++;
1504
+ }
1505
+ logger.success(`Cleared ${cleared} memory file(s)`);
1506
+
1507
+ // Recreate global memory
1508
+ await this.memoryManager.saveMemory(
1509
+ '# Global Context\n\nGlobal preferences and settings will be added here.',
1510
+ 'global'
1511
+ );
1512
+ logger.info('Recreated global memory');
1513
+ logger.info('Use /init to initialize project memory if needed');
1514
+ return;
1515
+ }
1516
+
1517
+ if (target === 'global') {
1518
+ // Clear global memory
1519
+ const globalMemory = memoryFiles.find((m: MemoryFile) => m.level === 'global');
1520
+ if (globalMemory) {
1521
+ await fs.unlink(globalMemory.path);
1522
+ logger.success('Global memory cleared');
1523
+
1524
+ // Recreate global memory
1525
+ await this.memoryManager.saveMemory(
1526
+ '# Global Context\n\nGlobal preferences and settings will be added here.',
1527
+ 'global'
1528
+ );
1529
+ logger.info('Recreated with default content');
1530
+ } else {
1531
+ logger.warn('No global memory found');
1532
+ }
1533
+ return;
1534
+ }
1535
+
1536
+ // Clear specific file by filename or path
1537
+ const targetMemory = memoryFiles.find(
1538
+ (m: MemoryFile) => path.basename(m.path) === target || m.path === target
1539
+ );
1540
+
1541
+ if (targetMemory) {
1542
+ try {
1543
+ await fs.unlink(targetMemory.path);
1544
+ const levelLabel = targetMemory.level === 'global' ? 'Global' : 'Project';
1545
+ logger.success(`${levelLabel} memory cleared: ${path.basename(targetMemory.path)}`);
1546
+
1547
+ // Recreate global memory, not project memory
1548
+ if (targetMemory.level === 'global') {
1549
+ await this.memoryManager.saveMemory(
1550
+ '# Global Context\n\nGlobal preferences and settings will be added here.',
1551
+ 'global'
1552
+ );
1553
+ logger.info('Recreated with default content');
1554
+ } else {
1555
+ logger.info('Use /init to initialize if needed');
1556
+ }
1557
+ } catch (error) {
1558
+ logger.error(
1559
+ 'Failed to clear memory',
1560
+ error instanceof Error ? error.message : String(error)
1561
+ );
1562
+ }
1563
+ } else {
1564
+ logger.warn(`Memory file not found: ${target}`, 'Use /memory show to see available files');
1565
+ }
1566
+ }
1567
+
1568
+ private async showMemory(): Promise<void> {
1569
+ const memoryFiles = this.memoryManager.getMemoryFiles();
1570
+
1571
+ if (memoryFiles.length === 0) {
1572
+ logger.warn('No memory files loaded', 'Use /init to initialize project context');
1573
+ return;
1574
+ }
1575
+
1576
+ const memoriesDir = this.memoryManager.getMemoriesDir();
1577
+ logger.section('Memory Files');
1578
+ logger.info(`Directory: ${memoriesDir}`);
1579
+ console.log('');
1580
+
1581
+ memoryFiles.forEach((file: MemoryFile) => {
1582
+ const level =
1583
+ file.level === 'global'
1584
+ ? chalk.blue('[global]')
1585
+ : file.level === 'project'
1586
+ ? chalk.green('[project]')
1587
+ : chalk.yellow('[subdirectory]');
1588
+ logger.info(` ${level} ${path.basename(file.path)}`);
1589
+ });
1590
+
1591
+ console.log('');
1592
+ // logger.info('Usage: /memory clear [global|current|all|<filename>]');
1593
+ }
1594
+
1595
+ private async addMemory(): Promise<void> {
1596
+ const entry = (await text({
1597
+ message: 'Enter memory entry (opens editor):',
1598
+ })) as string;
1599
+
1600
+ if (entry && entry.trim()) {
1601
+ await this.memoryManager.addMemoryEntry(entry.trim());
1602
+ console.log(chalk.green('✅ Memory entry added'));
1603
+ }
1604
+ }
1605
+
1606
+ private async refreshMemory(): Promise<void> {
1607
+ const spinner = ora({ text: 'Refreshing memory...', interval: 200 }).start();
1608
+
1609
+ try {
1610
+ await this.memoryManager.loadMemory();
1611
+ spinner.succeed('Memory refreshed successfully');
1612
+ } catch (error: any) {
1613
+ spinner.fail(`Failed to refresh memory: ${error.message}`);
1614
+ }
1615
+ }
1616
+
1617
+ private async handleRestore(args: string[]): Promise<void> {
1618
+ if (!this.checkpointManager.isEnabled()) {
1619
+ logger.warn('Checkpointing is not enabled', 'Enable it with /mode or in settings');
1620
+ return;
1621
+ }
1622
+
1623
+ const checkpoints = this.checkpointManager.listCheckpoints();
1624
+
1625
+ if (checkpoints.length === 0) {
1626
+ logger.warn('No checkpoints available', 'Create checkpoints during your session');
1627
+ return;
1628
+ }
1629
+
1630
+ if (args.length > 0) {
1631
+ const checkpointId = args[0];
1632
+ try {
1633
+ await this.checkpointManager.restoreCheckpoint(checkpointId);
1634
+ logger.success(`Checkpoint ${checkpointId} restored successfully!`);
1635
+ } catch (error: any) {
1636
+ logger.error(error.message, 'Check if checkpoint ID is valid');
1637
+ }
1638
+ } else {
1639
+ const checkpointOptions = checkpoints.map((cp: Checkpoint) => ({
1640
+ value: cp.id,
1641
+ label: `${new Date(cp.timestamp).toLocaleString()} - ${cp.description}`,
1642
+ }));
1643
+
1644
+ const checkpointId = await select({
1645
+ message: 'Select checkpoint to restore:',
1646
+ options: checkpointOptions,
1647
+ }) as string | symbol;
1648
+
1649
+ if (typeof checkpointId === 'symbol') {
1650
+ return;
1651
+ }
1652
+
1653
+ try {
1654
+ await this.checkpointManager.restoreCheckpoint(checkpointId);
1655
+ logger.success(`Checkpoint ${checkpointId} restored successfully!`);
1656
+ } catch (error: any) {
1657
+ logger.error(error.message, 'Check if checkpoint ID is valid');
1658
+ }
1659
+ }
1660
+ }
1661
+
1662
+ private async handleTools(): Promise<void> {
1663
+ const toolRegistry = getToolRegistry();
1664
+ const tools = toolRegistry.getAll();
1665
+
1666
+ logger.section('Available Tools');
1667
+
1668
+ tools.forEach((tool) => {
1669
+ logger.info(` ${tool.name}`);
1670
+ logger.info(` ${tool.description}`);
1671
+ });
1672
+
1673
+ console.log('');
1674
+ const currentSetting = this.configManager.get('showToolDetails') ? 'verbose' : 'simple';
1675
+ logger.info(`Current tool display mode: ${currentSetting}`);
1676
+ logger.info('Use /tools verbose to switch to verbose mode');
1677
+ logger.info('Use /tools simple to switch to simple mode');
1678
+ }
1679
+
1680
+ private async handleToolsVerbose(args: string[]): Promise<void> {
1681
+ if (args.length === 0) {
1682
+ const currentSetting = this.configManager.get('showToolDetails') ? 'verbose' : 'simple';
1683
+ logger.info(`Current tool display mode: ${currentSetting}`);
1684
+ return;
1685
+ }
1686
+
1687
+ const mode = args[0].toLowerCase();
1688
+
1689
+ if (mode === 'verbose' || mode === 'detail' || mode === 'true' || mode === 'on') {
1690
+ this.configManager.set('showToolDetails', true);
1691
+ this.configManager.save('global');
1692
+ logger.success(
1693
+ 'Tool display mode switched to verbose mode',
1694
+ 'Will show complete tool call information'
1695
+ );
1696
+ } else if (mode === 'simple' || mode === 'concise' || mode === 'false' || mode === 'off') {
1697
+ this.configManager.set('showToolDetails', false);
1698
+ this.configManager.save('global');
1699
+ logger.success(
1700
+ 'Tool display mode switched to simple mode',
1701
+ 'Only show tool execution status'
1702
+ );
1703
+ } else {
1704
+ logger.warn('Invalid mode', 'Use verbose or simple');
1705
+ }
1706
+ }
1707
+
1708
+ private async handleStats(): Promise<void> {
1709
+ logger.section('Session Statistics');
1710
+ const authConfig = this.configManager.getAuthConfig();
1711
+ logger.info(` Base URL: ${authConfig.baseUrl}`);
1712
+ logger.info(` Execution Mode: ${this.configManager.getExecutionMode()}`);
1713
+ logger.info(` Language: ${this.configManager.getLanguage()}`);
1714
+ logger.info(` Checkpointing: ${this.checkpointManager.isEnabled() ? 'Enabled' : 'Disabled'}`);
1715
+ logger.info(` MCP Servers: ${this.mcpManager.getAllServers().length}`);
1716
+ logger.info(` Agents: ${this.agentManager.getAllAgents().length}`);
1717
+ }
1718
+
1719
+ private async handleTheme(): Promise<void> {
1720
+ logger.warn('Theme switching not implemented yet', 'Check back later for updates');
1721
+ }
1722
+
1723
+ private async handleLanguage(): Promise<void> {
1724
+ const language = (await select({
1725
+ message: 'Select language:',
1726
+ options: [
1727
+ { value: 'zh', label: 'Chinese' },
1728
+ { value: 'en', label: 'English' },
1729
+ ],
1730
+ })) as 'zh' | 'en' | symbol;
1731
+
1732
+ if (typeof language === 'symbol') {
1733
+ return;
1734
+ }
1735
+
1736
+ this.configManager.setLanguage(language);
1737
+ logger.success(
1738
+ `Language changed to: ${language === 'zh' ? 'Chinese' : 'English'}`,
1739
+ 'Restart CLI to apply changes'
1740
+ );
1741
+ }
1742
+
1743
+ private async handleAbout(): Promise<void> {
1744
+ logger.section('xAgent CLI');
1745
+ logger.info('Version: 1.0.0');
1746
+ logger.info('A powerful AI-powered command-line assistant');
1747
+ logger.blank();
1748
+ logger.link('Documentation', 'https://platform.xagent.cn/');
1749
+ logger.link('GitHub', 'https://github.com/xagent-ai/xagent-cli');
1750
+ }
1751
+
1752
+ private async handleUpdate(): Promise<void> {
1753
+ const separator = icons.separator.repeat(Math.min(40, process.stdout.columns || 80));
1754
+
1755
+ console.log('');
1756
+ console.log(colors.primaryBright(`${icons.rocket} Update Check`));
1757
+ console.log(colors.border(separator));
1758
+ console.log('');
1759
+
1760
+ try {
1761
+ const { getUpdateManager } = await import('./update.js');
1762
+ const updateManager = getUpdateManager();
1763
+ const versionInfo = await updateManager.checkForUpdates();
1764
+
1765
+ console.log(` ${icons.info} ${colors.textMuted('Current version:')} ${colors.primaryBright(versionInfo.currentVersion)}`);
1766
+ console.log(` ${icons.code} ${colors.textMuted('Latest version:')} ${colors.primaryBright(versionInfo.latestVersion)}`);
1767
+ console.log('');
1768
+
1769
+ if (versionInfo.updateAvailable) {
1770
+ console.log(colors.success(` 📦 A new version is available!`));
1771
+ console.log('');
1772
+
1773
+ if (versionInfo.releaseNotes) {
1774
+ console.log(colors.textMuted(' Release Notes:'));
1775
+ console.log(colors.textDim(` ${versionInfo.releaseNotes}`));
1776
+ console.log('');
1777
+ }
1778
+
1779
+ const shouldUpdate = await confirm({
1780
+ message: 'Do you want to update now?',
1781
+ });
1782
+
1783
+ if (shouldUpdate === true) {
1784
+ console.log('');
1785
+ await updateManager.autoUpdate();
1786
+ }
1787
+ } else {
1788
+ console.log(colors.success(` ✅ You are using the latest version`));
1789
+ console.log('');
1790
+ }
1791
+ } catch (error: any) {
1792
+ console.log(colors.error(` ❌ Failed to check for updates: ${error.message}`));
1793
+ console.log('');
1794
+ }
1795
+ }
1796
+
1797
+ private async handleCompress(args: string[]): Promise<void> {
1798
+ const config = this.configManager.getContextCompressionConfig();
1799
+
1800
+ // If there are arguments, process config or execute
1801
+ if (args.length > 0) {
1802
+ const action = args[0].toLowerCase();
1803
+
1804
+ if (action === 'exec' || action === 'run' || action === 'now') {
1805
+ await this.executeCompression(config);
1806
+ return;
1807
+ }
1808
+
1809
+ await this.setCompressConfig(args);
1810
+ return;
1811
+ }
1812
+
1813
+ // Display current configuration
1814
+ console.log(chalk.cyan('\n📦 Context Compression:\n'));
1815
+
1816
+ console.log(` Status: ${config.enabled ? chalk.green('Enabled') : chalk.red('Disabled')}`);
1817
+
1818
+ console.log('');
1819
+ console.log(chalk.gray('Usage:'));
1820
+ console.log(chalk.gray(' /compress - Show current configuration'));
1821
+ console.log(chalk.gray(' /compress exec - Execute compression now'));
1822
+ console.log(chalk.gray(' /compress on|off - Enable/disable compression'));
1823
+ console.log('');
1824
+ }
1825
+
1826
+ private async executeCompression(config: CompressionConfig): Promise<void> {
1827
+ const messages = this.conversationHistory;
1828
+
1829
+ if (!messages || messages.length === 0) {
1830
+ console.log(chalk.yellow('⚠️ No conversation to compress'));
1831
+ return;
1832
+ }
1833
+
1834
+ const { needsCompression, reason } = this.contextCompressor.needsCompression(messages, config);
1835
+
1836
+ if (!needsCompression) {
1837
+ console.log(chalk.green('✅ No compression needed'));
1838
+ console.log(chalk.gray(` ${reason}`));
1839
+ return;
1840
+ }
1841
+
1842
+ console.log(chalk.cyan('\n🚀 Executing context compression...\n'));
1843
+
1844
+ const spinner = ora({
1845
+ text: 'Compressing context...',
1846
+ spinner: 'dots',
1847
+ color: 'cyan',
1848
+ }).start();
1849
+
1850
+ try {
1851
+ const result: CompressionResult = await this.contextCompressor.compressContext(
1852
+ messages,
1853
+ 'You are a helpful AI assistant.',
1854
+ config
1855
+ );
1856
+
1857
+ spinner.succeed(chalk.green('✅ Compression complete'));
1858
+
1859
+ console.log('');
1860
+ console.log(
1861
+ ` ${chalk.cyan('Original:')} ${chalk.yellow(result.originalMessageCount.toString())} messages (${result.originalSize} chars)`
1862
+ );
1863
+ console.log(
1864
+ ` ${chalk.cyan('Compressed:')} ${chalk.yellow(result.compressedMessageCount.toString())} messages (${result.compressedSize} chars)`
1865
+ );
1866
+ console.log(
1867
+ ` ${chalk.cyan('Reduction:')} ${chalk.green(Math.round((1 - result.compressedSize / result.originalSize) * 100) + '%')}`
1868
+ );
1869
+ console.log(` ${chalk.cyan('Method:')} ${chalk.yellow(result.compressionMethod)}`);
1870
+
1871
+ console.log('');
1872
+ console.log(
1873
+ chalk.gray(
1874
+ 'Use /clear to start a new conversation, or continue chatting to see the compressed summary.'
1875
+ )
1876
+ );
1877
+ console.log('');
1878
+ } catch (error: any) {
1879
+ spinner.fail(chalk.red('Compression failed'));
1880
+ console.log(chalk.red(` ${error.message}`));
1881
+ }
1882
+ }
1883
+
1884
+ private async setCompressConfig(args: string[]): Promise<void> {
1885
+ const config = this.configManager.getContextCompressionConfig();
1886
+ const action = args[0].toLowerCase();
1887
+
1888
+ switch (action) {
1889
+ case 'on':
1890
+ config.enabled = true;
1891
+ this.configManager.setContextCompressionConfig(config);
1892
+ this.configManager.save('global');
1893
+ console.log(chalk.green('✅ Context compression enabled'));
1894
+ break;
1895
+
1896
+ case 'off':
1897
+ config.enabled = false;
1898
+ this.configManager.setContextCompressionConfig(config);
1899
+ this.configManager.save('global');
1900
+ console.log(chalk.green('✅ Context compression disabled'));
1901
+ break;
1902
+
1903
+ default:
1904
+ console.log(chalk.red(`❌ Unknown action: ${action}`));
1905
+ console.log(chalk.gray('Available actions: on, off, exec'));
1906
+ }
1907
+ }
1908
+
1909
+ private async handleSkill(args: string[]): Promise<void> {
1910
+ const os = await import('os');
1911
+ const path = await import('path');
1912
+ const { fileURLToPath } = await import('url');
1913
+ const { promises: fs } = await import('fs');
1914
+
1915
+ const action = args[0] || 'list';
1916
+ const userSkillsPath = this.configManager.getUserSkillsPath() || path.join(os.homedir(), '.xagent', 'skills');
1917
+
1918
+ switch (action) {
1919
+ case 'list':
1920
+ await this.listUserSkills(userSkillsPath, fs, path);
1921
+ break;
1922
+ case 'add':
1923
+ await this.addSkill(args[1], userSkillsPath, fs, path);
1924
+ break;
1925
+ case 'remove':
1926
+ await this.removeSkill(args[1], userSkillsPath, fs, path);
1927
+ break;
1928
+ default:
1929
+ console.log(chalk.cyan('\n🔧 Skills:\n'));
1930
+ console.log(` Skills directory: ${chalk.yellow(userSkillsPath)}\n`);
1931
+ console.log(chalk.gray('Available commands:'));
1932
+ console.log(chalk.gray(' /skill list - List installed skills'));
1933
+ console.log(chalk.gray(' /skill add <source> - Add a skill (local path or remote URL)'));
1934
+ console.log(chalk.gray(' /skill remove <name> - Remove a user-installed skill'));
1935
+ console.log();
1936
+ console.log(chalk.gray('Examples:'));
1937
+ console.log(chalk.gray(' /skill add ./my-skill - Local path'));
1938
+ console.log(chalk.gray(' /skill add owner/repo - GitHub shorthand'));
1939
+ console.log(chalk.gray(' /skill add https://github.com/owner/repo - GitHub URL'));
1940
+ console.log();
1941
+ }
1942
+ }
1943
+
1944
+ private async listUserSkills(userSkillsPath: string, fs: any, path: any): Promise<void> {
1945
+ console.log(chalk.cyan('\n🔧 Skills:\n'));
1946
+
1947
+ try {
1948
+ const entries = await fs.readdir(userSkillsPath, { withFileTypes: true });
1949
+ const skills = entries.filter((e: any) => e.isDirectory());
1950
+
1951
+ if (skills.length === 0) {
1952
+ console.log(chalk.gray(' No skills installed'));
1953
+ console.log(chalk.cyan('\n To add a skill, use:'));
1954
+ console.log(chalk.cyan(' /skill add <path-to-skill>\n'));
1955
+ return;
1956
+ }
1957
+
1958
+ for (const skill of skills) {
1959
+ const skillPath = path.join(userSkillsPath, skill.name as string);
1960
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
1961
+
1962
+ try {
1963
+ const content = await fs.readFile(skillMdPath, 'utf-8');
1964
+ const nameMatch = content.match(/^name:\s*(.+)$/m);
1965
+ const descMatch = content.match(/^description:\s*(.+)$/m);
1966
+ const name = nameMatch ? nameMatch[1].trim() : skill.name;
1967
+ const description = descMatch ? descMatch[1].trim() : 'No description';
1968
+
1969
+ console.log(` ${chalk.cyan('•')} ${chalk.yellow(name)}`);
1970
+ console.log(` ${chalk.gray(description)}`);
1971
+ console.log();
1972
+ } catch {
1973
+ console.log(` ${chalk.cyan('•')} ${chalk.yellow(skill.name)}`);
1974
+ console.log(` ${chalk.gray('(Missing SKILL.md)')}`);
1975
+ console.log();
1976
+ }
1977
+ }
1978
+
1979
+ console.log(chalk.gray(` Skills directory: ${userSkillsPath}`));
1980
+ console.log();
1981
+ } catch (error: any) {
1982
+ if (error.code === 'ENOENT') {
1983
+ console.log(chalk.gray(' No skills installed'));
1984
+ console.log(chalk.cyan('\n To add a skill, use:'));
1985
+ console.log(chalk.cyan(' /skill add <path-to-skill>\n'));
1986
+ } else {
1987
+ console.log(chalk.red(` Error: ${error.message}`));
1988
+ }
1989
+ }
1990
+ }
1991
+
1992
+ private async addSkill(source: string, userSkillsPath: string, fs: any, path: any): Promise<void> {
1993
+ if (!source) {
1994
+ console.log(chalk.yellow('\n⚠️ Please specify a skill source'));
1995
+ console.log(chalk.cyan(' Usage: /skill add <source>\n'));
1996
+ console.log(chalk.gray(' Examples:'));
1997
+ console.log(chalk.gray(' /skill add ./my-skill - Local path'));
1998
+ console.log(chalk.gray(' /skill add owner/repo - GitHub shorthand'));
1999
+ console.log(chalk.gray(' /skill add https://github.com/owner/repo'));
2000
+ console.log();
2001
+ return;
2002
+ }
2003
+
2004
+ const { parseSource, installSkill } = await import('./skill-installer.js');
2005
+ const parsed = parseSource(source.trim());
2006
+ const isLocal = parsed.type === 'local';
2007
+
2008
+ if (isLocal) {
2009
+ // Local installation
2010
+ const resolvedPath = path.resolve(source);
2011
+ const skillName = path.basename(resolvedPath);
2012
+ const destPath = path.join(userSkillsPath, skillName);
2013
+
2014
+ try {
2015
+ // Check if source exists
2016
+ await fs.access(resolvedPath);
2017
+
2018
+ // Check if SKILL.md exists
2019
+ const skillMdPath = path.join(resolvedPath, 'SKILL.md');
2020
+ try {
2021
+ await fs.access(skillMdPath);
2022
+ } catch {
2023
+ console.log(chalk.red(`\n❌ SKILL.md not found in ${resolvedPath}`));
2024
+ console.log(chalk.gray(' Each skill must have a SKILL.md file\n'));
2025
+ return;
2026
+ }
2027
+
2028
+ // Check if skill already exists
2029
+ try {
2030
+ await fs.access(destPath);
2031
+ console.log(chalk.yellow(`\n⚠️ Skill "${skillName}" already installed`));
2032
+ console.log(chalk.cyan(` Use: /skill remove ${skillName} to remove it first\n`));
2033
+ return;
2034
+ } catch {
2035
+ // Doesn't exist, proceed
2036
+ }
2037
+
2038
+ // Ensure skills directory exists
2039
+ await fs.mkdir(userSkillsPath, { recursive: true });
2040
+
2041
+ // Copy the skill
2042
+ await this.copyDirectory(resolvedPath, destPath);
2043
+
2044
+ console.log(chalk.green('\n✅ Skill installed successfully'));
2045
+ console.log(chalk.gray(` Name: ${skillName}`));
2046
+ console.log(chalk.gray(` Location: ${destPath}`));
2047
+ console.log(chalk.gray(` Type: Local`));
2048
+ console.log();
2049
+
2050
+ // Trigger system prompt update
2051
+ this.onSystemPromptUpdate?.();
2052
+ } catch (error: any) {
2053
+ if (error.code === 'ENOENT') {
2054
+ console.log(chalk.red(`\n❌ Skill not found: ${source}\n`));
2055
+ } else {
2056
+ console.log(chalk.red(`\n❌ Error installing skill: ${error.message}\n`));
2057
+ }
2058
+ }
2059
+ } else {
2060
+ // Remote installation
2061
+ console.log(chalk.cyan('\n📦 Installing skill from remote source...\n'));
2062
+ console.log(chalk.gray(` Source: ${source}`));
2063
+ console.log(chalk.gray(` Type: Remote`));
2064
+ console.log();
2065
+
2066
+ try {
2067
+ const result = await installSkill(source);
2068
+
2069
+ if (result.success) {
2070
+ console.log(chalk.green('✅ Skill installed successfully'));
2071
+ console.log(chalk.gray(` Name: ${result.skillName}`));
2072
+ console.log(chalk.gray(` Location: ${result.skillPath}`));
2073
+ console.log(chalk.gray(' Type: Remote'));
2074
+ console.log();
2075
+
2076
+ // Trigger system prompt update - skill is immediately available
2077
+ this.onSystemPromptUpdate?.();
2078
+ } else {
2079
+ console.log(chalk.red(`\n❌ Failed to install skill: ${result.error}\n`));
2080
+ }
2081
+ } catch (error: any) {
2082
+ console.log(chalk.red(`\n❌ Error installing skill: ${error.message}\n`));
2083
+ }
2084
+ }
2085
+ }
2086
+
2087
+ private async removeSkill(skillName: string, userSkillsPath: string, fs: any, path: any): Promise<void> {
2088
+ if (!skillName) {
2089
+ console.log(chalk.yellow('\n⚠️ Please specify a skill name'));
2090
+ console.log(chalk.cyan(' Usage: /skill remove <skill-name>\n'));
2091
+ return;
2092
+ }
2093
+
2094
+ // Protect find-skills from deletion
2095
+ if (skillName === 'find-skills') {
2096
+ console.log(chalk.red('\n❌ Cannot remove protected skill: find-skills'));
2097
+ console.log(chalk.gray(' find-skills is a built-in skill that helps you discover and install other skills.\n'));
2098
+ return;
2099
+ }
2100
+
2101
+ const skillPath = path.join(userSkillsPath, skillName);
2102
+
2103
+ try {
2104
+ await fs.access(skillPath);
2105
+
2106
+ // Verify it's in skills path
2107
+ if (!skillPath.startsWith(userSkillsPath)) {
2108
+ console.log(chalk.red(`\n❌ Cannot remove skill outside user directory\n`));
2109
+ return;
2110
+ }
2111
+
2112
+ // Remove the skill directory
2113
+ await fs.rm(skillPath, { recursive: true, force: true });
2114
+
2115
+ console.log(chalk.green('\n✅ Skill removed successfully'));
2116
+ console.log();
2117
+
2118
+ // Trigger system prompt update
2119
+ this.onSystemPromptUpdate?.();
2120
+ } catch (error: any) {
2121
+ if (error.code === 'ENOENT' || error.code === 'ENOENT') {
2122
+ console.log(chalk.yellow(`\n⚠️ Skill not found: ${skillName}\n`));
2123
+ } else {
2124
+ console.log(chalk.red(`\n❌ Error removing skill: ${error.message}\n`));
2125
+ }
2126
+ }
2127
+ }
2128
+
2129
+ private async copyDirectory(src: string, dest: string): Promise<void> {
2130
+ const entries = await fs.readdir(src, { withFileTypes: true });
2131
+ await fs.mkdir(dest, { recursive: true });
2132
+
2133
+ for (const entry of entries) {
2134
+ // Skip node_modules to keep dependencies isolated
2135
+ // if (entry.name === 'node_modules') continue;
2136
+
2137
+ const srcPath = path.join(src, entry.name);
2138
+ const destPath = path.join(dest, entry.name);
2139
+
2140
+ if (entry.isDirectory()) {
2141
+ await this.copyDirectory(srcPath, destPath);
2142
+ } else if (entry.isFile()) {
2143
+ await fs.copyFile(srcPath, destPath);
2144
+ }
2145
+ }
2146
+ }
2147
+ }
2148
+
2149
+ export function parseInput(input: string): InputType[] {
2150
+ const inputs: InputType[] = [];
2151
+ let remaining = input;
2152
+
2153
+ const fileRefRegex = /@([^\s]+)/g;
2154
+ let match;
2155
+ while ((match = fileRefRegex.exec(remaining)) !== null) {
2156
+ const filePath = match[1];
2157
+ const beforeMatch = remaining.substring(0, match.index);
2158
+ const afterMatch = remaining.substring(match.index + match[0].length);
2159
+
2160
+ if (beforeMatch.trim()) {
2161
+ inputs.push({ type: 'text', content: beforeMatch.trim() });
2162
+ }
2163
+
2164
+ inputs.push({ type: 'file', content: filePath });
2165
+ remaining = afterMatch;
2166
+ }
2167
+
2168
+ if (remaining.trim()) {
2169
+ if (remaining.startsWith('!')) {
2170
+ inputs.push({ type: 'command', content: remaining.slice(1).trim() });
2171
+ } else {
2172
+ inputs.push({ type: 'text', content: remaining.trim() });
2173
+ }
2174
+ }
2175
+
2176
+ return inputs;
2177
+ }
2178
+
2179
+ export function detectImageInput(input: string): boolean {
2180
+ return input.includes('[Pasted image') || input.includes('<image');
2181
+ }