@xagent-ai/cli 1.2.2 → 1.3.1

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