@xagent-ai/cli 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (568) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/workflows/ci.yml +72 -0
  4. package/.github/workflows/release.yml +109 -0
  5. package/.gitmodules +3 -3
  6. package/README.md +326 -280
  7. package/README_CN.md +325 -279
  8. package/dist/ai-client/factory.d.ts +52 -0
  9. package/dist/ai-client/factory.d.ts.map +1 -0
  10. package/dist/ai-client/factory.js +132 -0
  11. package/dist/ai-client/factory.js.map +1 -0
  12. package/dist/ai-client/index.d.ts +20 -0
  13. package/dist/ai-client/index.d.ts.map +1 -0
  14. package/dist/ai-client/index.js +49 -0
  15. package/dist/ai-client/index.js.map +1 -0
  16. package/dist/ai-client/providers/anthropic.d.ts +57 -0
  17. package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
  18. package/dist/ai-client/providers/anthropic.js +400 -0
  19. package/dist/ai-client/providers/anthropic.js.map +1 -0
  20. package/dist/ai-client/providers/openai.d.ts +57 -0
  21. package/dist/ai-client/providers/openai.d.ts.map +1 -0
  22. package/dist/ai-client/providers/openai.js +286 -0
  23. package/dist/ai-client/providers/openai.js.map +1 -0
  24. package/dist/ai-client/providers/remote.d.ts +111 -0
  25. package/dist/ai-client/providers/remote.d.ts.map +1 -0
  26. package/dist/ai-client/providers/remote.js +351 -0
  27. package/dist/ai-client/providers/remote.js.map +1 -0
  28. package/dist/ai-client/registry.d.ts +51 -0
  29. package/dist/ai-client/registry.d.ts.map +1 -0
  30. package/dist/ai-client/registry.js +81 -0
  31. package/dist/ai-client/registry.js.map +1 -0
  32. package/dist/ai-client/types.d.ts +260 -0
  33. package/dist/ai-client/types.d.ts.map +1 -0
  34. package/dist/ai-client/types.js +73 -0
  35. package/dist/ai-client/types.js.map +1 -0
  36. package/dist/ai-client-factory.d.ts +62 -0
  37. package/dist/ai-client-factory.d.ts.map +1 -0
  38. package/dist/ai-client-factory.js +157 -0
  39. package/dist/ai-client-factory.js.map +1 -0
  40. package/dist/auth.d.ts +23 -1
  41. package/dist/auth.d.ts.map +1 -1
  42. package/dist/auth.js +160 -168
  43. package/dist/auth.js.map +1 -1
  44. package/dist/cancellation.d.ts +5 -4
  45. package/dist/cancellation.d.ts.map +1 -1
  46. package/dist/cancellation.js +55 -32
  47. package/dist/cancellation.js.map +1 -1
  48. package/dist/checkpoint.d.ts +1 -1
  49. package/dist/checkpoint.d.ts.map +1 -1
  50. package/dist/checkpoint.js +2 -2
  51. package/dist/checkpoint.js.map +1 -1
  52. package/dist/cli.js +626 -13
  53. package/dist/cli.js.map +1 -1
  54. package/dist/config.d.ts +10 -4
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +62 -25
  57. package/dist/config.js.map +1 -1
  58. package/dist/context-compressor.d.ts +81 -16
  59. package/dist/context-compressor.d.ts.map +1 -1
  60. package/dist/context-compressor.js +712 -153
  61. package/dist/context-compressor.js.map +1 -1
  62. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  63. package/dist/gui-subagent/action-parser/actionParser.js +4 -2
  64. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  65. package/dist/gui-subagent/agent/gui-agent.d.ts +29 -2
  66. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  67. package/dist/gui-subagent/agent/gui-agent.js +87 -45
  68. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  69. package/dist/gui-subagent/index.d.ts +16 -1
  70. package/dist/gui-subagent/index.d.ts.map +1 -1
  71. package/dist/gui-subagent/index.js +4 -0
  72. package/dist/gui-subagent/index.js.map +1 -1
  73. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
  74. package/dist/gui-subagent/operator/base-operator.js +0 -1
  75. package/dist/gui-subagent/operator/base-operator.js.map +1 -1
  76. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  77. package/dist/gui-subagent/operator/computer-operator.js +29 -8
  78. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  79. package/dist/gui-subagent/types/actions.d.ts +1 -1
  80. package/dist/gui-subagent/types/actions.d.ts.map +1 -1
  81. package/dist/gui-subagent/types/actions.js +0 -1
  82. package/dist/gui-subagent/types/actions.js.map +1 -1
  83. package/dist/gui-subagent/types/operator.d.ts +1 -1
  84. package/dist/gui-subagent/types/operator.d.ts.map +1 -1
  85. package/dist/index.d.ts +1 -2
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/input-processor.d.ts.map +1 -1
  90. package/dist/input-processor.js +6 -3
  91. package/dist/input-processor.js.map +1 -1
  92. package/dist/mcp.d.ts +5 -0
  93. package/dist/mcp.d.ts.map +1 -1
  94. package/dist/mcp.js +81 -35
  95. package/dist/mcp.js.map +1 -1
  96. package/dist/ripgrep.d.ts +29 -0
  97. package/dist/ripgrep.d.ts.map +1 -0
  98. package/dist/ripgrep.js +292 -0
  99. package/dist/ripgrep.js.map +1 -0
  100. package/dist/session.d.ts +23 -7
  101. package/dist/session.d.ts.map +1 -1
  102. package/dist/session.js +658 -347
  103. package/dist/session.js.map +1 -1
  104. package/dist/shell.d.ts +33 -0
  105. package/dist/shell.d.ts.map +1 -0
  106. package/dist/shell.js +125 -0
  107. package/dist/shell.js.map +1 -0
  108. package/dist/skill-installer.d.ts +38 -0
  109. package/dist/skill-installer.d.ts.map +1 -0
  110. package/dist/skill-installer.js +447 -0
  111. package/dist/skill-installer.js.map +1 -0
  112. package/dist/skill-invoker.d.ts +7 -1
  113. package/dist/skill-invoker.d.ts.map +1 -1
  114. package/dist/skill-invoker.js +34 -13
  115. package/dist/skill-invoker.js.map +1 -1
  116. package/dist/skill-loader.d.ts +8 -3
  117. package/dist/skill-loader.d.ts.map +1 -1
  118. package/dist/skill-loader.js +46 -44
  119. package/dist/skill-loader.js.map +1 -1
  120. package/dist/skill-manager.d.ts +85 -0
  121. package/dist/skill-manager.d.ts.map +1 -0
  122. package/dist/skill-manager.js +340 -0
  123. package/dist/skill-manager.js.map +1 -0
  124. package/dist/slash-commands.d.ts +38 -1
  125. package/dist/slash-commands.d.ts.map +1 -1
  126. package/dist/slash-commands.js +912 -296
  127. package/dist/slash-commands.js.map +1 -1
  128. package/dist/smart-approval.d.ts.map +1 -1
  129. package/dist/smart-approval.js +83 -71
  130. package/dist/smart-approval.js.map +1 -1
  131. package/dist/system-prompt-generator.d.ts +6 -0
  132. package/dist/system-prompt-generator.d.ts.map +1 -1
  133. package/dist/system-prompt-generator.js +88 -38
  134. package/dist/system-prompt-generator.js.map +1 -1
  135. package/dist/terminal.d.ts +28 -0
  136. package/dist/terminal.d.ts.map +1 -0
  137. package/dist/terminal.js +82 -0
  138. package/dist/terminal.js.map +1 -0
  139. package/dist/tools.d.ts +23 -7
  140. package/dist/tools.d.ts.map +1 -1
  141. package/dist/tools.js +800 -440
  142. package/dist/tools.js.map +1 -1
  143. package/dist/truncate.d.ts +55 -0
  144. package/dist/truncate.d.ts.map +1 -0
  145. package/dist/truncate.js +130 -0
  146. package/dist/truncate.js.map +1 -0
  147. package/dist/types.d.ts +27 -9
  148. package/dist/types.d.ts.map +1 -1
  149. package/dist/update.d.ts.map +1 -1
  150. package/dist/update.js +17 -28
  151. package/dist/update.js.map +1 -1
  152. package/dist/workflow.d.ts +5 -1
  153. package/dist/workflow.d.ts.map +1 -1
  154. package/dist/workflow.js +60 -47
  155. package/dist/workflow.js.map +1 -1
  156. package/docs/architecture/mcp-integration-guide.md +304 -194
  157. package/docs/architecture/overview.md +169 -169
  158. package/docs/architecture/tool-system-design.md +134 -134
  159. package/docs/cli/commands.md +349 -238
  160. package/docs/smart-mode.md +281 -281
  161. package/docs/third-party-models.md +439 -439
  162. package/find-skills/SKILL.md +133 -0
  163. package/package.json +89 -90
  164. package/scripts/install-ripgrep.js +241 -0
  165. package/src/ai-client/factory.ts +151 -0
  166. package/src/ai-client/index.ts +61 -0
  167. package/src/ai-client/providers/anthropic.ts +466 -0
  168. package/src/ai-client/providers/openai.ts +342 -0
  169. package/src/ai-client/providers/remote.ts +436 -0
  170. package/src/ai-client/registry.ts +97 -0
  171. package/src/ai-client/types.ts +345 -0
  172. package/src/ai-client-factory.ts +204 -0
  173. package/src/auth.ts +663 -614
  174. package/src/cancellation.ts +205 -176
  175. package/src/checkpoint.ts +219 -219
  176. package/src/cli.ts +1406 -743
  177. package/src/config.ts +341 -297
  178. package/src/context-compressor.ts +982 -290
  179. package/src/conversation.ts +288 -288
  180. package/src/gui-subagent/action-parser/actionParser.ts +318 -315
  181. package/src/gui-subagent/action-parser/constants.ts +14 -14
  182. package/src/gui-subagent/action-parser/index.ts +8 -8
  183. package/src/gui-subagent/action-parser/types.ts +31 -31
  184. package/src/gui-subagent/agent/gui-agent.ts +1151 -1089
  185. package/src/gui-subagent/agent/index.ts +5 -5
  186. package/src/gui-subagent/index.ts +177 -163
  187. package/src/gui-subagent/operator/base-operator.ts +244 -245
  188. package/src/gui-subagent/operator/computer-operator.ts +540 -520
  189. package/src/gui-subagent/operator/index.ts +6 -6
  190. package/src/gui-subagent/types/actions.ts +260 -262
  191. package/src/gui-subagent/types/index.ts +6 -6
  192. package/src/gui-subagent/types/operator.ts +106 -106
  193. package/src/gui-subagent/utils.ts +51 -51
  194. package/src/index.ts +17 -18
  195. package/src/input-processor.ts +6 -3
  196. package/src/logger.ts +438 -438
  197. package/src/mcp.ts +730 -682
  198. package/src/memory.ts +344 -344
  199. package/src/ripgrep.ts +368 -0
  200. package/src/session-manager.ts +308 -308
  201. package/src/session.ts +955 -390
  202. package/src/shell.ts +133 -0
  203. package/src/skill-installer.ts +518 -0
  204. package/src/skill-invoker.ts +960 -935
  205. package/src/skill-loader.ts +501 -496
  206. package/src/skill-manager.ts +384 -0
  207. package/src/slash-commands.ts +2181 -1389
  208. package/src/smart-approval.ts +117 -73
  209. package/src/system-prompt-generator.ts +89 -34
  210. package/src/terminal.ts +96 -0
  211. package/src/theme.ts +738 -738
  212. package/src/tools.ts +1336 -773
  213. package/src/truncate.ts +173 -0
  214. package/src/types.ts +219 -198
  215. package/src/update.ts +22 -32
  216. package/src/workflow.ts +523 -508
  217. package/tsconfig.json +22 -22
  218. package/vitest.config.ts +19 -19
  219. package/dist/ai-client.d.ts +0 -86
  220. package/dist/ai-client.d.ts.map +0 -1
  221. package/dist/ai-client.js +0 -1372
  222. package/dist/ai-client.js.map +0 -1
  223. package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
  224. package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
  225. package/dist/gui-subagent/operator/browser-operator.js +0 -306
  226. package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
  227. package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
  228. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
  229. package/dist/gui-subagent/operator/desktop-operator.js +0 -527
  230. package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
  231. package/dist/hook.d.ts +0 -73
  232. package/dist/hook.d.ts.map +0 -1
  233. package/dist/hook.js +0 -156
  234. package/dist/hook.js.map +0 -1
  235. package/dist/input-history.d.ts +0 -24
  236. package/dist/input-history.d.ts.map +0 -1
  237. package/dist/input-history.js +0 -94
  238. package/dist/input-history.js.map +0 -1
  239. package/dist/keyboard-manager.d.ts +0 -151
  240. package/dist/keyboard-manager.d.ts.map +0 -1
  241. package/dist/keyboard-manager.js +0 -396
  242. package/dist/keyboard-manager.js.map +0 -1
  243. package/dist/print-system-prompt.d.ts +0 -2
  244. package/dist/print-system-prompt.d.ts.map +0 -1
  245. package/dist/print-system-prompt.js +0 -40
  246. package/dist/print-system-prompt.js.map +0 -1
  247. package/dist/remote-ai-client.d.ts +0 -104
  248. package/dist/remote-ai-client.d.ts.map +0 -1
  249. package/dist/remote-ai-client.js +0 -552
  250. package/dist/remote-ai-client.js.map +0 -1
  251. package/dist/sdk-output-adapter.d.ts +0 -232
  252. package/dist/sdk-output-adapter.d.ts.map +0 -1
  253. package/dist/sdk-output-adapter.js +0 -636
  254. package/dist/sdk-output-adapter.js.map +0 -1
  255. package/dist/sdk-session-v2.d.ts +0 -13
  256. package/dist/sdk-session-v2.d.ts.map +0 -1
  257. package/dist/sdk-session-v2.js +0 -46
  258. package/dist/sdk-session-v2.js.map +0 -1
  259. package/dist/sdk-session.d.ts +0 -13
  260. package/dist/sdk-session.d.ts.map +0 -1
  261. package/dist/sdk-session.js +0 -48
  262. package/dist/sdk-session.js.map +0 -1
  263. package/dist/test-boundary-conditions.d.ts.map +0 -1
  264. package/dist/test-boundary-conditions.js.map +0 -1
  265. package/dist/test-cancellation-fix.d.ts.map +0 -1
  266. package/dist/test-cancellation-fix.js.map +0 -1
  267. package/dist/test-input-history.d.ts.map +0 -1
  268. package/dist/test-input-history.js.map +0 -1
  269. package/dist/test-interaction-flow.d.ts.map +0 -1
  270. package/dist/test-interaction-flow.js.map +0 -1
  271. package/dist/test-quick.d.ts.map +0 -1
  272. package/dist/test-quick.js.map +0 -1
  273. package/dist/test-user-interaction.d.ts.map +0 -1
  274. package/dist/test-user-interaction.js.map +0 -1
  275. package/dist/tools/edit-diff.d.ts +0 -32
  276. package/dist/tools/edit-diff.d.ts.map +0 -1
  277. package/dist/tools/edit-diff.js +0 -185
  278. package/dist/tools/edit-diff.js.map +0 -1
  279. package/dist/tools/edit.d.ts +0 -11
  280. package/dist/tools/edit.d.ts.map +0 -1
  281. package/dist/tools/edit.js +0 -129
  282. package/dist/tools/edit.js.map +0 -1
  283. package/dist/unified-session.d.ts +0 -42
  284. package/dist/unified-session.d.ts.map +0 -1
  285. package/dist/unified-session.js +0 -271
  286. package/dist/unified-session.js.map +0 -1
  287. package/skills/.claude-plugin/marketplace.json +0 -45
  288. package/skills/README.md +0 -94
  289. package/skills/THIRD_PARTY_NOTICES.md +0 -405
  290. package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
  291. package/skills/skills/algorithmic-art/SKILL.md +0 -405
  292. package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
  293. package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
  294. package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
  295. package/skills/skills/brand-guidelines/SKILL.md +0 -73
  296. package/skills/skills/canvas-design/LICENSE.txt +0 -202
  297. package/skills/skills/canvas-design/SKILL.md +0 -130
  298. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  299. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  300. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  301. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  302. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  303. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  304. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  305. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  306. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  307. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  308. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  309. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  310. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  311. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  312. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  313. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  314. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  315. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  316. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  317. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  318. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  319. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  320. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  321. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  322. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  323. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  324. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  325. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  326. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  327. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  328. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  329. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  330. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  331. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  332. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  333. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  334. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  335. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  336. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  337. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  338. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  339. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  340. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  341. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  342. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  343. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  344. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  345. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  346. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  347. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  348. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  349. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  350. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  351. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  352. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  353. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  354. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  355. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  356. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  357. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  358. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  359. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  360. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  361. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  362. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  363. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  364. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  365. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  366. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  367. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  368. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  369. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  370. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  371. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  372. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  373. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  374. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  375. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  376. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  377. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  378. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  379. package/skills/skills/doc-coauthoring/SKILL.md +0 -375
  380. package/skills/skills/docx/LICENSE.txt +0 -30
  381. package/skills/skills/docx/SKILL.md +0 -197
  382. package/skills/skills/docx/docx-js.md +0 -350
  383. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  384. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  385. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  386. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  387. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  388. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  389. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  390. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  391. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  392. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  393. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  394. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  395. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  396. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  397. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  398. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  399. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  400. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  401. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  402. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  403. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  404. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  405. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  406. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  407. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  408. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  409. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  410. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  411. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  412. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  413. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  414. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  415. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  416. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  417. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  418. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  419. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  420. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  421. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  422. package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
  423. package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
  424. package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
  425. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  426. package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
  427. package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  428. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  429. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  430. package/skills/skills/docx/ooxml.md +0 -610
  431. package/skills/skills/docx/scripts/__init__.py +0 -1
  432. package/skills/skills/docx/scripts/document.py +0 -1276
  433. package/skills/skills/docx/scripts/templates/comments.xml +0 -3
  434. package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  435. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  436. package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
  437. package/skills/skills/docx/scripts/templates/people.xml +0 -3
  438. package/skills/skills/docx/scripts/utilities.py +0 -374
  439. package/skills/skills/frontend-design/LICENSE.txt +0 -177
  440. package/skills/skills/frontend-design/SKILL.md +0 -42
  441. package/skills/skills/internal-comms/LICENSE.txt +0 -202
  442. package/skills/skills/internal-comms/SKILL.md +0 -32
  443. package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
  444. package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
  445. package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
  446. package/skills/skills/internal-comms/examples/general-comms.md +0 -16
  447. package/skills/skills/mcp-builder/LICENSE.txt +0 -202
  448. package/skills/skills/mcp-builder/SKILL.md +0 -236
  449. package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
  450. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  451. package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  452. package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  453. package/skills/skills/mcp-builder/scripts/connections.py +0 -151
  454. package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
  455. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  456. package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
  457. package/skills/skills/pdf/LICENSE.txt +0 -30
  458. package/skills/skills/pdf/SKILL.md +0 -294
  459. package/skills/skills/pdf/forms.md +0 -205
  460. package/skills/skills/pdf/reference.md +0 -612
  461. package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  462. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  463. package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
  464. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  465. package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
  466. package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
  467. package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  468. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  469. package/skills/skills/pptx/LICENSE.txt +0 -30
  470. package/skills/skills/pptx/SKILL.md +0 -484
  471. package/skills/skills/pptx/html2pptx.md +0 -625
  472. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  473. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  474. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  475. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  476. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  477. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  478. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  479. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  480. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  481. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  482. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  483. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  484. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  485. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  486. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  487. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  488. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  489. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  490. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  491. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  492. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  493. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  494. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  495. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  496. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  497. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  498. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  499. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  500. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  501. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  502. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  503. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  504. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  505. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  506. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  507. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  508. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  509. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  510. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  511. package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
  512. package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
  513. package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
  514. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  515. package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  516. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  517. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  518. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  519. package/skills/skills/pptx/ooxml.md +0 -427
  520. package/skills/skills/pptx/scripts/html2pptx.js +0 -979
  521. package/skills/skills/pptx/scripts/inventory.py +0 -1020
  522. package/skills/skills/pptx/scripts/rearrange.py +0 -231
  523. package/skills/skills/pptx/scripts/replace.py +0 -385
  524. package/skills/skills/pptx/scripts/thumbnail.py +0 -450
  525. package/skills/skills/skill-creator/LICENSE.txt +0 -202
  526. package/skills/skills/skill-creator/SKILL.md +0 -356
  527. package/skills/skills/skill-creator/references/output-patterns.md +0 -82
  528. package/skills/skills/skill-creator/references/workflows.md +0 -28
  529. package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
  530. package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
  531. package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
  532. package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
  533. package/skills/skills/slack-gif-creator/SKILL.md +0 -254
  534. package/skills/skills/slack-gif-creator/core/easing.py +0 -234
  535. package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
  536. package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
  537. package/skills/skills/slack-gif-creator/core/validators.py +0 -136
  538. package/skills/skills/slack-gif-creator/requirements.txt +0 -4
  539. package/skills/skills/theme-factory/LICENSE.txt +0 -202
  540. package/skills/skills/theme-factory/SKILL.md +0 -59
  541. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  542. package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
  543. package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
  544. package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
  545. package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
  546. package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
  547. package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
  548. package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
  549. package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
  550. package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
  551. package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
  552. package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
  553. package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
  554. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
  555. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
  556. package/skills/skills/webapp-testing/LICENSE.txt +0 -202
  557. package/skills/skills/webapp-testing/SKILL.md +0 -96
  558. package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
  559. package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
  560. package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
  561. package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
  562. package/skills/skills/xlsx/LICENSE.txt +0 -30
  563. package/skills/skills/xlsx/SKILL.md +0 -289
  564. package/skills/skills/xlsx/recalc.py +0 -178
  565. package/skills/spec/agent-skills-spec.md +0 -3
  566. package/skills/template/SKILL.md +0 -6
  567. package/src/ai-client.ts +0 -1560
  568. package/src/remote-ai-client.ts +0 -664
package/dist/session.js CHANGED
@@ -5,11 +5,18 @@ import axios from 'axios';
5
5
  import crypto from 'crypto';
6
6
  import ora from 'ora';
7
7
  import { createRequire } from 'module';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import fs from 'fs';
11
+ import fsPromises from 'fs/promises';
12
+ import os from 'os';
8
13
  const require = createRequire(import.meta.url);
9
14
  const packageJson = require('../package.json');
10
- import { ExecutionMode, AuthType } from './types.js';
11
- import { AIClient, detectThinkingKeywords, getThinkingTokens } from './ai-client.js';
12
- import { RemoteAIClient, TokenInvalidError } from './remote-ai-client.js';
15
+ import { ExecutionMode, AuthType, } from './types.js';
16
+ import { createAIClient } from './ai-client-factory.js';
17
+ import { detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
18
+ import { TokenInvalidError } from './ai-client/types.js';
19
+ import { fetchDefaultModels } from './ai-client/providers/remote.js';
13
20
  import { getConfigManager } from './config.js';
14
21
  import { AuthService, selectAuthType } from './auth.js';
15
22
  import { getToolRegistry } from './tools.js';
@@ -21,10 +28,11 @@ import { getConversationManager } from './conversation.js';
21
28
  import { getSessionManager } from './session-manager.js';
22
29
  import { SlashCommandHandler, parseInput } from './slash-commands.js';
23
30
  import { SystemPromptGenerator } from './system-prompt-generator.js';
24
- import { theme, icons, colors, styleHelpers, renderMarkdown, renderDiff, renderLines } from './theme.js';
31
+ import { theme, icons, colors, styleHelpers, renderMarkdown, renderDiff, renderLines, } from './theme.js';
25
32
  import { getCancellationManager } from './cancellation.js';
26
- import { getContextCompressor } from './context-compressor.js';
33
+ import { getContextCompressor, } from './context-compressor.js';
27
34
  import { getLogger } from './logger.js';
35
+ import { ensureTtySane, setupEscKeyHandler } from './terminal.js';
28
36
  const logger = getLogger();
29
37
  export class InteractiveSession {
30
38
  conversationManager;
@@ -33,7 +41,7 @@ export class InteractiveSession {
33
41
  aiClient = null;
34
42
  remoteAIClient = null;
35
43
  conversation = [];
36
- toolCalls = [];
44
+ tool_calls = [];
37
45
  executionMode;
38
46
  slashCommandHandler;
39
47
  configManager;
@@ -53,7 +61,7 @@ export class InteractiveSession {
53
61
  constructor(indentLevel = 0) {
54
62
  this.rl = readline.createInterface({
55
63
  input: process.stdin,
56
- output: process.stdout
64
+ output: process.stdout,
57
65
  });
58
66
  this.configManager = getConfigManager(process.cwd());
59
67
  this.agentManager = getAgentManager(process.cwd());
@@ -66,7 +74,7 @@ export class InteractiveSession {
66
74
  // Register /clear callback, clear local conversation when clearing dialogue
67
75
  this.slashCommandHandler.setClearCallback(() => {
68
76
  this.conversation = [];
69
- this.toolCalls = [];
77
+ this.tool_calls = [];
70
78
  this.currentTaskId = null;
71
79
  this.taskCompleted = false;
72
80
  this.isFirstApiCall = true;
@@ -76,6 +84,10 @@ export class InteractiveSession {
76
84
  this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
77
85
  await this.updateSystemPrompt();
78
86
  });
87
+ // Register config update callback, update aiClient config when /auth changes config
88
+ this.slashCommandHandler.setConfigUpdateCallback(() => {
89
+ this.updateAiClientConfig();
90
+ });
79
91
  this.executionMode = ExecutionMode.DEFAULT;
80
92
  this.cancellationManager = getCancellationManager();
81
93
  this.indentLevel = indentLevel;
@@ -88,6 +100,32 @@ export class InteractiveSession {
88
100
  setAIClient(aiClient) {
89
101
  this.aiClient = aiClient;
90
102
  }
103
+ /**
104
+ * Update aiClient config when /auth changes config (called from callback)
105
+ */
106
+ updateAiClientConfig() {
107
+ const authConfig = this.configManager.getAuthConfig();
108
+ const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
109
+ if (isRemote) {
110
+ // Already in remote mode, no change needed
111
+ if (this.remoteAIClient !== null) {
112
+ return;
113
+ }
114
+ // Switch to remote: clear local client, create remote client
115
+ this.aiClient = null;
116
+ this.remoteAIClient = createAIClient(authConfig);
117
+ }
118
+ else {
119
+ // Already in local mode, no change needed
120
+ if (this.aiClient !== null) {
121
+ return;
122
+ }
123
+ // Switch to local: clear remote client, create local client
124
+ this.remoteAIClient = null;
125
+ this.aiClient = createAIClient(authConfig);
126
+ }
127
+ this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
128
+ }
91
129
  setExecutionMode(mode) {
92
130
  this.executionMode = mode;
93
131
  }
@@ -95,25 +133,68 @@ export class InteractiveSession {
95
133
  * Update system prompt to reflect MCP changes (called after add/remove MCP)
96
134
  */
97
135
  async updateSystemPrompt() {
136
+ // Reload skills to pick up any newly added/removed skills
137
+ // First reset the SkillLoader to clear all cached skills
138
+ const { resetSkillLoader } = await import('./skill-loader.js');
139
+ resetSkillLoader();
140
+ // Then reload the skill invoker
141
+ const skillInvoker = (await import('./skill-invoker.js')).getSkillInvoker();
142
+ await skillInvoker.reload();
98
143
  const toolRegistry = getToolRegistry();
99
144
  const promptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
100
145
  // Use the current agent's original system prompt as base
101
146
  const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are xAgent, an AI-powered CLI tool.';
102
147
  const newSystemPrompt = await promptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
103
148
  // Replace old system prompt with new one
104
- this.conversation = this.conversation.filter(msg => msg.role !== 'system');
149
+ this.conversation = this.conversation.filter((msg) => msg.role !== 'system');
105
150
  this.conversation.unshift({
106
151
  role: 'system',
107
152
  content: newSystemPrompt,
108
- timestamp: Date.now()
153
+ timestamp: Date.now(),
109
154
  });
110
155
  // Sync to slashCommandHandler
111
156
  this.slashCommandHandler.setConversationHistory(this.conversation);
112
157
  }
158
+ /**
159
+ * Watch for skill updates from CLI and update system prompt accordingly
160
+ */
161
+ startSkillUpdateWatcher() {
162
+ const SKILL_STATE_FILE = '.skill-state.json';
163
+ const configManager = getConfigManager();
164
+ const userSkillsPath = configManager.getUserSkillsPath();
165
+ const stateFilePath = userSkillsPath
166
+ ? path.join(userSkillsPath, SKILL_STATE_FILE)
167
+ : null;
168
+ if (!stateFilePath) {
169
+ return; // No user skills path configured
170
+ }
171
+ let lastUpdateTime = 0;
172
+ // Check for updates every 2 seconds
173
+ const checkInterval = setInterval(async () => {
174
+ try {
175
+ const { existsSync, readFileSync } = await import('fs');
176
+ if (existsSync(stateFilePath)) {
177
+ const content = readFileSync(stateFilePath, 'utf-8');
178
+ const state = JSON.parse(content);
179
+ if (state.lastSkillUpdate && state.lastSkillUpdate > lastUpdateTime) {
180
+ lastUpdateTime = state.lastSkillUpdate;
181
+ // Update system prompt with new skills
182
+ await this.updateSystemPrompt();
183
+ console.log(colors.textMuted(' 🔄 Skills updated from CLI'));
184
+ }
185
+ }
186
+ }
187
+ catch (error) {
188
+ // Silent fail - watcher is optional
189
+ }
190
+ }, 2000);
191
+ // Clean up on session end
192
+ this._skillWatcherInterval = checkInterval;
193
+ }
113
194
  setAgent(agent) {
114
195
  this.currentAgent = agent;
115
196
  }
116
- async start() {
197
+ async start(initializedCount = 0) {
117
198
  // Set this session as the singleton for access from other modules
118
199
  setSingletonSession(this);
119
200
  // Initialize taskId for GUI operations
@@ -122,25 +203,43 @@ export class InteractiveSession {
122
203
  console.log('');
123
204
  console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
124
205
  console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
125
- console.log(colors.gradient('║') + ' '.repeat(13) + '🤖 ' + colors.gradient('XAGENT CLI') + ' '.repeat(32) + colors.gradient(' ║'));
126
- console.log(colors.gradient('║') + ' '.repeat(16) + colors.textMuted(`v${packageJson.version}`) + ' '.repeat(36) + colors.gradient(' ║'));
206
+ console.log(colors.gradient('║') +
207
+ ' '.repeat(13) +
208
+ '🤖 ' +
209
+ colors.gradient('XAGENT CLI') +
210
+ ' '.repeat(32) +
211
+ colors.gradient(' ║'));
212
+ console.log(colors.gradient('║') +
213
+ ' '.repeat(16) +
214
+ colors.textMuted(`v${packageJson.version}`) +
215
+ ' '.repeat(36) +
216
+ colors.gradient(' ║'));
127
217
  console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
128
218
  console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
129
219
  console.log(colors.textMuted(' AI-powered command-line assistant'));
220
+ // Show initialization message if skills were initialized
221
+ if (initializedCount > 0) {
222
+ console.log(colors.textMuted(` ✨ Initialized ${initializedCount} built-in skills`));
223
+ }
130
224
  console.log('');
131
225
  await this.initialize();
132
226
  this.showWelcomeMessage();
227
+ // Start watching for skill updates from CLI
228
+ this.startSkillUpdateWatcher();
229
+ // Set up ESC key handler using the terminal module
230
+ // This avoids conflicts with readline and provides clean ESC detection
231
+ let escCleanup;
232
+ if (process.stdin.isTTY) {
233
+ escCleanup = setupEscKeyHandler(() => {
234
+ if (this._isOperationInProgress) {
235
+ // An operation is running, let it be cancelled
236
+ this.cancellationManager.cancel();
237
+ }
238
+ // No operation running, ignore ESC
239
+ });
240
+ }
133
241
  // Track if an operation is in progress
134
242
  this._isOperationInProgress = false;
135
- // Listen for ESC cancellation - only cancel operations, don't exit the program
136
- const cancelHandler = () => {
137
- if (this._isOperationInProgress) {
138
- // An operation is running, let it be cancelled
139
- return;
140
- }
141
- // No operation running, ignore ESC or show a message
142
- };
143
- this.cancellationManager.on('cancelled', cancelHandler);
144
243
  this.promptLoop();
145
244
  // Keep the promise pending until shutdown
146
245
  return new Promise((resolve) => {
@@ -159,7 +258,7 @@ export class InteractiveSession {
159
258
  frameIndex = (frameIndex + 1) % frames.length;
160
259
  }, 120);
161
260
  logger.debug('[SESSION] 调用 configManager.load()...');
162
- await this.configManager.load();
261
+ this.configManager.load();
163
262
  logger.debug('[SESSION] Config loaded');
164
263
  let authConfig = this.configManager.getAuthConfig();
165
264
  let selectedAuthType = this.configManager.get('selectedAuthType');
@@ -186,9 +285,8 @@ export class InteractiveSession {
186
285
  clearInterval(refreshInterval);
187
286
  process.stdout.write('\r' + ' '.repeat(50) + '\r');
188
287
  if (newToken) {
189
- // Save new token and persist
190
- await this.configManager.set('apiKey', newToken);
191
- await this.configManager.save('global');
288
+ this.configManager.set('apiKey', newToken);
289
+ this.configManager.save('global');
192
290
  authConfig.apiKey = newToken;
193
291
  isValid = true;
194
292
  }
@@ -198,20 +296,18 @@ export class InteractiveSession {
198
296
  console.log(colors.warning('Your xAgent session has expired or is not configured'));
199
297
  console.log(colors.info('Please select an authentication method to continue.'));
200
298
  console.log('');
201
- // Clear invalid credentials and persist
202
- // Note: Do NOT overwrite selectedAuthType - let user re-select their preferred auth method
203
- await this.configManager.set('apiKey', '');
204
- await this.configManager.set('refreshToken', '');
205
- await this.configManager.save('global');
206
- await this.configManager.load();
299
+ this.configManager.set('apiKey', '');
300
+ this.configManager.set('refreshToken', '');
301
+ this.configManager.save('global');
302
+ this.configManager.load();
207
303
  authConfig = this.configManager.getAuthConfig();
208
304
  await this.setupAuthentication();
209
305
  authConfig = this.configManager.getAuthConfig();
210
- // Recreate readline interface after inquirer
306
+ // Recreate readline interface after interactive prompt
211
307
  this.rl.close();
212
308
  this.rl = readline.createInterface({
213
309
  input: process.stdin,
214
- output: process.stdout
310
+ output: process.stdout,
215
311
  });
216
312
  this.rl.on('close', () => {
217
313
  // readline closed
@@ -226,11 +322,11 @@ export class InteractiveSession {
226
322
  authConfig = this.configManager.getAuthConfig();
227
323
  selectedAuthType = this.configManager.get('selectedAuthType');
228
324
  logger.debug('[SESSION] selectedAuthType (after setup):', String(selectedAuthType));
229
- // Recreate readline interface after inquirer
325
+ // Recreate readline interface after interactive prompt
230
326
  this.rl.close();
231
327
  this.rl = readline.createInterface({
232
328
  input: process.stdin,
233
- output: process.stdout
329
+ output: process.stdout,
234
330
  });
235
331
  this.rl.on('close', () => {
236
332
  // readline closed
@@ -241,21 +337,42 @@ export class InteractiveSession {
241
337
  process.stdout.write('\r' + ' '.repeat(50) + '\r');
242
338
  }
243
339
  // For OPENAI_COMPATIBLE with API key, skip validation and proceed directly
244
- this.aiClient = new AIClient(authConfig);
245
- this.contextCompressor.setAIClient(this.aiClient);
246
- // Initialize remote AI client for OAuth XAGENT mode
247
- logger.debug('[SESSION] Final selectedAuthType:', String(selectedAuthType));
248
- logger.debug('[SESSION] Creating RemoteAIClient?', String(selectedAuthType === AuthType.OAUTH_XAGENT));
340
+ // Initialize AI clients and set contextCompressor appropriately
249
341
  if (selectedAuthType === AuthType.OAUTH_XAGENT) {
250
- const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
251
- // In OAuth XAGENT mode, we still pass apiKey (can be empty or used for other purposes)
252
- this.remoteAIClient = new RemoteAIClient(authConfig.apiKey || '', webBaseUrl, authConfig.showAIDebugInfo);
342
+ // Remote mode: fetch default models if not set
343
+ const currentLlm = this.configManager.get('remote_llmModelName');
344
+ const currentVlm = this.configManager.get('remote_vlmModelName');
345
+ if (!currentLlm || !currentVlm) {
346
+ const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
347
+ try {
348
+ const defaults = await fetchDefaultModels(authConfig.apiKey || '', webBaseUrl);
349
+ if (!currentLlm && defaults.llm?.name) {
350
+ this.configManager.set('remote_llmModelName', defaults.llm.name);
351
+ }
352
+ if (!currentVlm && defaults.vlm?.name) {
353
+ this.configManager.set('remote_vlmModelName', defaults.vlm.name);
354
+ }
355
+ this.configManager.save('global');
356
+ }
357
+ catch (error) {
358
+ logger.debug('[SESSION] Failed to fetch default models:', error.message);
359
+ }
360
+ }
361
+ // Remote mode: create RemoteAIClient and use it for context compression
362
+ this.remoteAIClient = createAIClient(authConfig);
363
+ this.contextCompressor.setAIClient(this.remoteAIClient);
253
364
  logger.debug('[DEBUG Initialize] RemoteAIClient created successfully');
254
365
  }
255
366
  else {
367
+ // Local mode: create local AIClient
368
+ this.aiClient = createAIClient(authConfig);
369
+ this.contextCompressor.setAIClient(this.aiClient);
256
370
  logger.debug('[DEBUG Initialize] RemoteAIClient NOT created (not OAuth XAGENT mode)');
257
371
  }
258
- this.executionMode = this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
372
+ // Sync remoteAIClient reference to slashCommandHandler for /provider command
373
+ this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
374
+ this.executionMode =
375
+ this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
259
376
  await this.agentManager.loadAgents();
260
377
  await this.memoryManager.loadMemory();
261
378
  await this.conversationManager.initialize();
@@ -292,7 +409,7 @@ export class InteractiveSession {
292
409
  this.checkpointManager = getCheckpointManager(process.cwd(), checkpointingConfig.enabled, checkpointingConfig.maxCheckpoints);
293
410
  await this.checkpointManager.initialize();
294
411
  }
295
- this.currentAgent = this.agentManager.getAgent('general-purpose');
412
+ this.currentAgent = this.agentManager.getAgent('general-purpose') ?? null;
296
413
  console.log(colors.success('✔ Initialization complete'));
297
414
  }
298
415
  catch (error) {
@@ -315,11 +432,11 @@ export class InteractiveSession {
315
432
  logger.debug('[SESSION] Sending validation request to:', url);
316
433
  const response = await axios.get(url, {
317
434
  headers: {
318
- 'Authorization': `Bearer ${apiKey}`,
319
- 'Content-Type': 'application/json'
435
+ Authorization: `Bearer ${apiKey}`,
436
+ 'Content-Type': 'application/json',
320
437
  },
321
438
  httpsAgent,
322
- timeout: 10000
439
+ timeout: 10000,
323
440
  });
324
441
  logger.debug('[SESSION] Validation response status:', String(response.status));
325
442
  return response.status === 200;
@@ -341,7 +458,7 @@ export class InteractiveSession {
341
458
  const httpsAgent = new https.Agent({ rejectUnauthorized: false });
342
459
  const response = await axios.post(url, { refreshToken }, {
343
460
  httpsAgent,
344
- timeout: 10000
461
+ timeout: 10000,
345
462
  });
346
463
  if (response.status === 200) {
347
464
  const data = response.data;
@@ -363,11 +480,14 @@ export class InteractiveSession {
363
480
  console.log('');
364
481
  const authType = await selectAuthType();
365
482
  this.configManager.set('selectedAuthType', authType);
483
+ // Get xagentApiBaseUrl from config (respects XAGENT_BASE_URL env var)
484
+ const config = this.configManager.getAuthConfig();
366
485
  const authService = new AuthService({
367
486
  type: authType,
368
487
  apiKey: '',
369
488
  baseUrl: '',
370
- modelName: ''
489
+ modelName: '',
490
+ xagentApiBaseUrl: config.xagentApiBaseUrl,
371
491
  });
372
492
  const success = await authService.authenticate();
373
493
  if (!success) {
@@ -377,16 +497,29 @@ export class InteractiveSession {
377
497
  process.exit(1);
378
498
  }
379
499
  const authConfig = authService.getAuthConfig();
500
+ // Clear modelName for remote mode
501
+ if (authType === AuthType.OAUTH_XAGENT) {
502
+ authConfig.modelName = '';
503
+ }
380
504
  // VLM configuration is optional - only show for non-OAuth (local) mode
381
505
  // Remote mode uses backend VLM configuration
382
506
  if (authType !== AuthType.OAUTH_XAGENT) {
383
507
  console.log('');
384
508
  console.log(colors.info(`${icons.info} VLM configuration is optional.`));
385
- console.log(colors.info(`You can configure it later using the /vlm command if needed.`));
509
+ console.log(colors.info(`You can configure it later using the /model command if needed.`));
386
510
  console.log('');
387
511
  }
388
- // Save LLM config only, skip VLM for now
389
- await this.configManager.setAuthConfig(authConfig);
512
+ this.configManager.setAuthConfig(authConfig);
513
+ // Set default remote model settings if not already set
514
+ if (authType === AuthType.OAUTH_XAGENT) {
515
+ if (!this.configManager.get('remote_llmModelName')) {
516
+ this.configManager.set('remote_llmModelName', '');
517
+ }
518
+ if (!this.configManager.get('remote_vlmModelName')) {
519
+ this.configManager.set('remote_vlmModelName', '');
520
+ }
521
+ this.configManager.save('global');
522
+ }
390
523
  }
391
524
  showWelcomeMessage() {
392
525
  const language = this.configManager.getLanguage();
@@ -410,28 +543,28 @@ export class InteractiveSession {
410
543
  [ExecutionMode.YOLO]: {
411
544
  color: colors.error,
412
545
  icon: icons.fire,
413
- description: 'Execute commands without confirmation'
546
+ description: 'Execute commands without confirmation',
414
547
  },
415
548
  [ExecutionMode.ACCEPT_EDITS]: {
416
549
  color: colors.warning,
417
550
  icon: icons.check,
418
- description: 'Accept all edits automatically'
551
+ description: 'Accept all edits automatically',
419
552
  },
420
553
  [ExecutionMode.PLAN]: {
421
554
  color: colors.info,
422
555
  icon: icons.brain,
423
- description: 'Plan before executing'
556
+ description: 'Plan before executing',
424
557
  },
425
558
  [ExecutionMode.DEFAULT]: {
426
559
  color: colors.success,
427
560
  icon: icons.bolt,
428
- description: 'Safe execution with confirmations'
561
+ description: 'Safe execution with confirmations',
429
562
  },
430
563
  [ExecutionMode.SMART]: {
431
564
  color: colors.primaryBright,
432
565
  icon: icons.sparkles,
433
- description: 'Smart approval with intelligent security checks'
434
- }
566
+ description: 'Smart approval with intelligent security checks',
567
+ },
435
568
  };
436
569
  const config = modeConfig[this.executionMode];
437
570
  const modeName = this.executionMode;
@@ -439,6 +572,26 @@ export class InteractiveSession {
439
572
  console.log(` ${config.color(config.icon)} ${styleHelpers.text.bold(config.color(modeName))}`);
440
573
  console.log(` ${colors.textDim(` ${config.description}`)}`);
441
574
  console.log('');
575
+ this.showRemoteModelInfo();
576
+ }
577
+ showRemoteModelInfo() {
578
+ const authConfig = this.configManager.getAuthConfig();
579
+ const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
580
+ if (isRemote) {
581
+ const llmModel = authConfig.remote_llmModelName || colors.textMuted('Not set');
582
+ const vlmModel = authConfig.remote_vlmModelName || colors.textMuted('Not set');
583
+ console.log(colors.textMuted(`${icons.brain} Remote Models:`));
584
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${llmModel}`);
585
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${vlmModel}`);
586
+ }
587
+ else {
588
+ const modelName = authConfig.modelName || colors.textMuted('Not set');
589
+ const guiSubagentModel = this.configManager.get('guiSubagentModel') || colors.textMuted('Not set');
590
+ console.log(colors.textMuted(`${icons.brain} Local Models:`));
591
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${modelName}`);
592
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${guiSubagentModel}`);
593
+ }
594
+ console.log('');
442
595
  }
443
596
  async promptLoop() {
444
597
  // Check if we're shutting down
@@ -449,15 +602,12 @@ export class InteractiveSession {
449
602
  if (this.rl) {
450
603
  this.rl.close();
451
604
  }
452
- // Enable raw mode BEFORE emitKeypressEvents for better ESC detection
453
- if (process.stdin.isTTY) {
454
- process.stdin.setRawMode(true);
455
- }
456
- process.stdin.resume();
457
- readline.emitKeypressEvents(process.stdin);
605
+ // Ensure TTY is in proper state for input handling
606
+ // This handles any state left by @clack/prompts or other interactions
607
+ ensureTtySane();
458
608
  this.rl = readline.createInterface({
459
609
  input: process.stdin,
460
- output: process.stdout
610
+ output: process.stdout,
461
611
  });
462
612
  const prompt = `${colors.primaryBright('❯')} `;
463
613
  this.rl.question(prompt, async (input) => {
@@ -481,7 +631,8 @@ export class InteractiveSession {
481
631
  if (trimmedInput.startsWith('/')) {
482
632
  const handled = await this.slashCommandHandler.handleCommand(trimmedInput);
483
633
  if (handled) {
484
- this.executionMode = this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
634
+ this.executionMode =
635
+ this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
485
636
  // Sync conversation history to slashCommandHandler
486
637
  this.slashCommandHandler.setConversationHistory(this.conversation);
487
638
  }
@@ -513,9 +664,9 @@ export class InteractiveSession {
513
664
  }
514
665
  async processUserMessage(message, agent) {
515
666
  const inputs = parseInput(message);
516
- const textInput = inputs.find(i => i.type === 'text');
517
- const fileInputs = inputs.filter(i => i.type === 'file');
518
- const commandInput = inputs.find(i => i.type === 'command');
667
+ const textInput = inputs.find((i) => i.type === 'text');
668
+ const fileInputs = inputs.filter((i) => i.type === 'file');
669
+ const commandInput = inputs.find((i) => i.type === 'command');
519
670
  if (commandInput) {
520
671
  await this.executeShellCommand(commandInput.content);
521
672
  return;
@@ -538,7 +689,7 @@ export class InteractiveSession {
538
689
  type: 'text',
539
690
  content: userContent,
540
691
  rawInput: message,
541
- timestamp: Date.now()
692
+ timestamp: Date.now(),
542
693
  };
543
694
  await this.sessionManager.addInput(sessionInput);
544
695
  // Calculate thinking tokens based on config and user input
@@ -552,25 +703,19 @@ export class InteractiveSession {
552
703
  const userMessage = {
553
704
  role: 'user',
554
705
  content: userContent,
555
- timestamp: Date.now()
706
+ timestamp: Date.now(),
556
707
  };
557
- // Save last user message for recovery after compression
558
- const lastUserMessage = userMessage;
559
708
  this.conversation.push(userMessage);
560
709
  await this.conversationManager.addMessage(userMessage);
561
- // Check if context compression is needed
562
- await this.checkAndCompressContext(lastUserMessage);
563
710
  // Use remote AI client if available (OAuth XAGENT mode)
564
711
  const currentSelectedAuthType = this.configManager.get('selectedAuthType');
565
- logger.debug('[DEBUG processUserMessage] remoteAIClient exists:', !!this.remoteAIClient ? 'true' : 'false');
566
- logger.debug('[DEBUG processUserMessage] selectedAuthType:', String(currentSelectedAuthType));
567
- logger.debug('[DEBUG processUserMessage] AuthType.OAUTH_XAGENT:', String(AuthType.OAUTH_XAGENT));
712
+ logger.debug(`[DEBUG] processUserMessage: remoteAIClient exists=${!!this.remoteAIClient}, selectedAuthType=${currentSelectedAuthType}`);
568
713
  if (this.remoteAIClient) {
569
- logger.debug('[DEBUG processUserMessage] Using generateRemoteResponse');
714
+ logger.debug('[DEBUG] Using generateRemoteResponse (remote mode)');
570
715
  await this.generateRemoteResponse(thinkingTokens);
571
716
  }
572
717
  else {
573
- logger.debug('[DEBUG processUserMessage] Using generateResponse (local mode)');
718
+ logger.debug('[DEBUG] Using generateResponse (local mode)');
574
719
  await this.generateResponse(thinkingTokens);
575
720
  }
576
721
  }
@@ -615,52 +760,68 @@ export class InteractiveSession {
615
760
  /**
616
761
  * Check and compress conversation context
617
762
  */
618
- async checkAndCompressContext(lastUserMessage) {
763
+ async checkAndCompressContext() {
619
764
  const compressionConfig = this.configManager.getContextCompressionConfig();
620
765
  if (!compressionConfig.enabled) {
621
766
  return;
622
767
  }
623
- const { needsCompression, reason } = this.contextCompressor.needsCompression(this.conversation, compressionConfig);
624
- if (needsCompression) {
625
- const indent = this.getIndent();
626
- console.log('');
627
- console.log(`${indent}${colors.warning(`${icons.brain} Context compression triggered: ${reason}`)}`);
628
- const toolRegistry = getToolRegistry();
629
- const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
630
- const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
631
- const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
632
- const result = await this.contextCompressor.compressContext(this.conversation, enhancedSystemPrompt, compressionConfig);
633
- if (result.wasCompressed) {
634
- this.conversation = result.compressedMessages;
635
- // console.log(`${indent}${colors.success(`✓ Compressed ${result.originalMessageCount} messages to ${result.compressedMessageCount} messages`)}`);
636
- console.log(`${indent}${colors.textMuted(`✓ Size: ${result.originalSize} ${result.compressedSize} chars (${Math.round((1 - result.compressedSize / result.originalSize) * 100)}% reduction)`)}`);
637
- // Display compressed summary content
638
- const summaryMessage = result.compressedMessages.find(m => m.role === 'assistant');
639
- if (summaryMessage && summaryMessage.content) {
640
- const maxPreviewLength = 800;
641
- let summaryContent = summaryMessage.content;
642
- const isTruncated = summaryContent.length > maxPreviewLength;
643
- if (isTruncated) {
644
- summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
645
- }
646
- console.log('');
647
- console.log(`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`);
648
- const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length * 2);
649
- console.log(`${indent}${colors.border(separator)}`);
650
- const renderedSummary = renderMarkdown(summaryContent, (process.stdout.columns || 80) - indent.length * 4);
651
- console.log(`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`);
652
- if (isTruncated) {
653
- console.log(`${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`);
654
- }
655
- console.log(`${indent}${colors.border(separator)}`);
768
+ const indent = this.getIndent();
769
+ const currentTokens = this.contextCompressor.estimateContextTokens(this.conversation);
770
+ const currentMessages = this.conversation.length;
771
+ const { needsCompression, reason, tokenCount } = this.contextCompressor.needsCompression(this.conversation, compressionConfig);
772
+ if (!needsCompression) {
773
+ return;
774
+ }
775
+ // Extract threshold and contextWindow from reason
776
+ const thresholdMatch = reason.match(/budget\s*\((\d+)/);
777
+ const contextWindowMatch = reason.match(/contextWindow:\s*(\d+)/);
778
+ const threshold = thresholdMatch ? parseInt(thresholdMatch[1], 10) : 0;
779
+ const contextWindow = contextWindowMatch ? parseInt(contextWindowMatch[1], 10) : 0;
780
+ console.log('');
781
+ console.log(`${indent}${colors.success(`${icons.sparkles} Compressing context (${currentMessages} msgs, ${tokenCount.toLocaleString()} > ${threshold.toLocaleString()}/${contextWindow.toLocaleString()} tokens, ${Math.round((tokenCount / contextWindow) * 100)}% of context window)...`)}`);
782
+ const toolRegistry = getToolRegistry();
783
+ const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
784
+ const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
785
+ const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
786
+ const result = await this.contextCompressor.compressContext(this.conversation, enhancedSystemPrompt, compressionConfig);
787
+ if (result.wasCompressed) {
788
+ this.conversation = result.compressedMessages;
789
+ const reductionPercent = Math.round((1 - result.compressedSize / result.originalSize) * 100);
790
+ console.log(`${indent}${colors.success(`${icons.success} Compressed ${result.originalMessageCount} → ${result.compressedMessageCount} messages (${reductionPercent}% smaller)`)}`);
791
+ // Summary is embedded in first user message, look for it
792
+ // The format is: "[Conversation Summary - X messages compressed]\n\n${summary}"
793
+ let summaryMessage = result.compressedMessages.find((m) => m.role === 'user' && m.content.includes('[Conversation Summary'));
794
+ if (summaryMessage) {
795
+ // Extract summary content after the header
796
+ const match = summaryMessage.content.match(/\[Conversation Summary.*?\]:\n\n(.+)/s);
797
+ if (match) {
798
+ summaryMessage = {
799
+ role: 'assistant',
800
+ content: match[1],
801
+ timestamp: summaryMessage.timestamp,
802
+ };
656
803
  }
657
- // Restore user messages after compression, ensuring user message exists for API calls
658
- if (lastUserMessage) {
659
- this.conversation.push(lastUserMessage);
804
+ }
805
+ if (summaryMessage && summaryMessage.content) {
806
+ const maxPreviewLength = 800;
807
+ let summaryContent = summaryMessage.content;
808
+ const isTruncated = summaryContent.length > maxPreviewLength;
809
+ if (isTruncated) {
810
+ summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
660
811
  }
661
- // Sync compressed conversation history to slashCommandHandler
662
- this.slashCommandHandler.setConversationHistory(this.conversation);
812
+ console.log('');
813
+ console.log(`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`);
814
+ const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length * 2);
815
+ console.log(`${indent}${colors.border(separator)}`);
816
+ const renderedSummary = renderMarkdown(summaryContent, (process.stdout.columns || 80) - indent.length * 4);
817
+ console.log(`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`);
818
+ if (isTruncated) {
819
+ console.log(`${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`);
820
+ }
821
+ console.log(`${indent}${colors.border(separator)}`);
663
822
  }
823
+ // Sync compressed conversation history to slashCommandHandler
824
+ this.slashCommandHandler.setConversationHistory(this.conversation);
664
825
  }
665
826
  }
666
827
  async executeShellCommand(command) {
@@ -683,15 +844,15 @@ export class InteractiveSession {
683
844
  tool: 'Bash',
684
845
  params: { command },
685
846
  result,
686
- timestamp: Date.now()
847
+ timestamp: Date.now(),
687
848
  };
688
- this.toolCalls.push(toolCall);
849
+ this.tool_calls.push(toolCall);
689
850
  // Record command execution to session manager
690
851
  await this.sessionManager.addInput({
691
852
  type: 'command',
692
853
  content: command,
693
854
  rawInput: command,
694
- timestamp: Date.now()
855
+ timestamp: Date.now(),
695
856
  });
696
857
  await this.sessionManager.addOutput({
697
858
  role: 'tool',
@@ -699,7 +860,7 @@ export class InteractiveSession {
699
860
  toolName: 'Bash',
700
861
  toolParams: { command },
701
862
  toolResult: result,
702
- timestamp: Date.now()
863
+ timestamp: Date.now(),
703
864
  });
704
865
  }
705
866
  catch (error) {
@@ -727,8 +888,19 @@ export class InteractiveSession {
727
888
  createRemoteCaller(taskId, status) {
728
889
  const client = this.remoteAIClient;
729
890
  return {
730
- chatCompletion: (messages, options) => client.chatCompletion(messages, { ...options, taskId, status }),
731
- isRemote: true
891
+ chatCompletion: (messages, options) => {
892
+ // Must fetch authConfig inside the closure, otherwise it captures stale config
893
+ const authConfig = this.configManager.getAuthConfig();
894
+ logger.debug(`[DEBUG] createRemoteCaller: llmModelName=${authConfig.remote_llmModelName}, vlmModelName=${authConfig.remote_vlmModelName}`);
895
+ return client.chatCompletion(messages, {
896
+ ...options,
897
+ taskId,
898
+ status: options.isFirstApiCall ? 'begin' : 'continue',
899
+ llmModelName: authConfig.remote_llmModelName,
900
+ vlmModelName: authConfig.remote_vlmModelName
901
+ });
902
+ },
903
+ isRemote: true,
732
904
  };
733
905
  }
734
906
  /**
@@ -738,18 +910,21 @@ export class InteractiveSession {
738
910
  const client = this.aiClient;
739
911
  return {
740
912
  chatCompletion: (messages, options) => client.chatCompletion(messages, options),
741
- isRemote: false
913
+ isRemote: false,
742
914
  };
743
915
  }
744
- async generateResponse(thinkingTokens = 0) {
745
- // Create taskId for this user interaction (for remote mode tracking)
746
- const taskId = crypto.randomUUID();
916
+ async generateResponse(thinkingTokens = 0, _customAIClient, existingTaskId) {
917
+ // Use existing taskId or create new one for this user interaction
918
+ // If taskId already exists (e.g., from tool calls), reuse it
919
+ const taskId = existingTaskId || this.currentTaskId || crypto.randomUUID();
747
920
  this.currentTaskId = taskId;
748
- this.isFirstApiCall = true;
749
- // Determine status based on whether this is the first API call
750
- const status = this.isFirstApiCall ? 'begin' : 'continue';
921
+ // isFirstApiCall is reset in generateRemoteResponse for new tasks
922
+ // For continuation calls (existingTaskId provided), keep previous value
751
923
  // Use unified LLM Caller with taskId (automatically selects local or remote mode)
752
- const { chatCompletion, isRemote } = this.createLLMCaller(taskId, status);
924
+ const status = this.isFirstApiCall ? 'begin' : 'continue';
925
+ const caller = this.createLLMCaller(taskId, status);
926
+ const chatCompletion = caller.chatCompletion;
927
+ const isRemote = caller.isRemote;
753
928
  if (!isRemote && !this.aiClient) {
754
929
  console.log(colors.error('AI client not initialized'));
755
930
  return;
@@ -777,29 +952,29 @@ export class InteractiveSession {
777
952
  const toolDefinitions = toolRegistry.getToolDefinitions();
778
953
  // Available tools for this session
779
954
  const availableTools = this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
780
- ? toolDefinitions.filter((tool) => allowedToolNames.includes(tool.function.name))
955
+ ? toolDefinitions.filter((tool) => typeof tool.function?.name === 'string' &&
956
+ allowedToolNames.includes(tool.function.name))
781
957
  : toolDefinitions;
782
- const baseSystemPrompt = this.currentAgent?.systemPrompt;
958
+ const baseSystemPrompt = this.currentAgent?.systemPrompt ?? '';
783
959
  const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
784
960
  const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
785
961
  const messages = [
786
962
  { role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
787
- ...this.conversation
963
+ ...this.conversation,
788
964
  ];
789
965
  const operationId = `ai-response-${Date.now()}`;
790
966
  const response = await this.cancellationManager.withCancellation(chatCompletion(messages, {
791
967
  tools: availableTools,
792
968
  toolChoice: availableTools.length > 0 ? 'auto' : 'none',
793
- thinkingTokens
969
+ thinkingTokens,
970
+ isFirstApiCall: this.isFirstApiCall,
794
971
  }), operationId);
795
972
  // Mark that first API call is complete
796
973
  this.isFirstApiCall = false;
797
974
  clearInterval(spinnerInterval);
798
975
  process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); // Clear spinner line
799
976
  const assistantMessage = response.choices[0].message;
800
- const content = typeof assistantMessage.content === 'string'
801
- ? assistantMessage.content
802
- : '';
977
+ const content = typeof assistantMessage.content === 'string' ? assistantMessage.content : '';
803
978
  const reasoningContent = assistantMessage.reasoning_content || '';
804
979
  // Display reasoning content if available and thinking mode is enabled
805
980
  if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
@@ -816,22 +991,25 @@ export class InteractiveSession {
816
991
  role: 'assistant',
817
992
  content,
818
993
  timestamp: Date.now(),
819
- reasoningContent,
820
- toolCalls: assistantMessage.tool_calls
994
+ reasoning_content: reasoningContent,
995
+ tool_calls: assistantMessage.tool_calls,
821
996
  });
822
997
  // Record output to session manager
823
998
  await this.sessionManager.addOutput({
824
999
  role: 'assistant',
825
1000
  content,
826
1001
  timestamp: Date.now(),
827
- reasoningContent,
828
- toolCalls: assistantMessage.tool_calls
1002
+ reasoning_content: reasoningContent,
1003
+ tool_calls: assistantMessage.tool_calls,
829
1004
  });
830
1005
  if (assistantMessage.tool_calls) {
831
1006
  await this.handleToolCalls(assistantMessage.tool_calls);
832
1007
  }
1008
+ else {
1009
+ await this.checkAndCompressContext();
1010
+ }
833
1011
  if (this.checkpointManager.isEnabled()) {
834
- await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.toolCalls]);
1012
+ await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.tool_calls]);
835
1013
  }
836
1014
  // Operation completed successfully, clear the flag
837
1015
  this._isOperationInProgress = false;
@@ -842,16 +1020,18 @@ export class InteractiveSession {
842
1020
  // Clear the operation flag
843
1021
  this._isOperationInProgress = false;
844
1022
  if (error.message === 'Operation cancelled by user') {
845
- // Mark task as cancelled
1023
+ // Notify backend to cancel the task
846
1024
  if (this.remoteAIClient && this.currentTaskId) {
847
- await this.remoteAIClient.cancelTask(this.currentTaskId);
1025
+ await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
848
1026
  }
849
1027
  return;
850
1028
  }
851
- // Mark task as cancelled when error occurs (发送 status: 'cancel')
852
- logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}`);
1029
+ // Distinguish error types: timeout vs other failures
1030
+ const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
1031
+ const failureReason = isTimeout ? 'timeout' : 'failure';
1032
+ logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`);
853
1033
  if (this.remoteAIClient && this.currentTaskId) {
854
- await this.remoteAIClient.cancelTask(this.currentTaskId);
1034
+ await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => { });
855
1035
  }
856
1036
  console.log(colors.error(`Error: ${error.message}`));
857
1037
  }
@@ -868,134 +1048,34 @@ export class InteractiveSession {
868
1048
  const taskId = existingTaskId || crypto.randomUUID();
869
1049
  this.currentTaskId = taskId;
870
1050
  logger.debug(`[Session] generateRemoteResponse: taskId=${taskId}, existingTaskId=${!!existingTaskId}`);
871
- // Reset isFirstApiCall for new task, keep true for continuation
872
- if (!existingTaskId) {
873
- this.isFirstApiCall = true;
874
- }
875
- // Determine status based on whether this is the first API call
876
- const status = this.isFirstApiCall ? 'begin' : 'continue';
1051
+ // Each new user message is a fresh task - always set isFirstApiCall = true
1052
+ // This ensures status is 'begin' for every user message
1053
+ this.isFirstApiCall = true;
1054
+ const status = 'begin';
877
1055
  logger.debug(`[Session] Status for this call: ${status}, isFirstApiCall=${this.isFirstApiCall}`);
878
- // 使用统一的 LLM Caller
879
- const { chatCompletion, isRemote } = this.createLLMCaller(taskId, status);
880
- if (!isRemote) {
881
- // 如果不是远程模式,回退到本地模式
882
- return this.generateResponse(thinkingTokens);
1056
+ // Check if remote client is available
1057
+ if (!this.remoteAIClient) {
1058
+ console.log(colors.error('Remote AI client not initialized'));
1059
+ return;
883
1060
  }
884
- const indent = this.getIndent();
885
- const thinkingText = colors.textMuted(`Thinking... (Press ESC to cancel)`);
886
- const icon = colors.primary(icons.brain);
887
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
888
- let frameIndex = 0;
889
- // Mark that an operation is in progress
890
- this._isOperationInProgress = true;
891
- // Custom spinner: only icon rotates, text stays static
892
- const spinnerInterval = setInterval(() => {
893
- process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${icon} ${thinkingText}`);
894
- frameIndex = (frameIndex + 1) % frames.length;
895
- }, 120);
896
1061
  try {
897
- // Load memory (与本地模式一致)
898
- const memory = await this.memoryManager.loadMemory();
899
- // Get tool definitions
900
- const toolRegistry = getToolRegistry();
901
- const allowedToolNames = this.currentAgent
902
- ? this.agentManager.getAvailableToolsForAgent(this.currentAgent, this.executionMode)
903
- : [];
904
- const allToolDefinitions = toolRegistry.getToolDefinitions();
905
- const availableTools = this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
906
- ? allToolDefinitions.filter((tool) => allowedToolNames.includes(tool.function.name))
907
- : allToolDefinitions;
908
- // Convert to the format expected by backend (与本地模式一致使用 availableTools)
909
- const tools = availableTools.map((tool) => ({
910
- type: 'function',
911
- function: {
912
- name: tool.function.name,
913
- description: tool.function.description || '',
914
- parameters: tool.function.parameters || {
915
- type: 'object',
916
- properties: {}
917
- }
918
- }
919
- }));
920
- // Generate system prompt (与本地模式一致)
921
- const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
922
- const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
923
- const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
924
- // Build messages with system prompt (与本地模式一致)
925
- const messages = [
926
- { role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
927
- ...this.conversation
928
- ];
929
- // Call unified LLM API with cancellation support
930
- const operationId = `remote-ai-response-${Date.now()}`;
931
- const response = await this.cancellationManager.withCancellation(chatCompletion(messages, {
932
- tools,
933
- toolChoice: tools.length > 0 ? 'auto' : 'none',
934
- thinkingTokens
935
- }), operationId);
936
- // Mark that first API call is complete
937
- this.isFirstApiCall = false;
938
- clearInterval(spinnerInterval);
939
- process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
940
- console.log('');
941
- // 使用统一的响应格式(与本地模式一致)
942
- const assistantMessage = response.choices[0].message;
943
- const content = typeof assistantMessage.content === 'string'
944
- ? assistantMessage.content
945
- : '';
946
- const reasoningContent = assistantMessage.reasoning_content || '';
947
- const toolCalls = assistantMessage.tool_calls || [];
948
- // Display reasoning content if available and thinking mode is enabled (与本地模式一致)
949
- if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
950
- this.displayThinkingContent(reasoningContent);
951
- }
952
- console.log(`${indent}${colors.primaryBright(`${icons.robot} Assistant:`)}`);
953
- console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
954
- console.log('');
955
- const renderedContent = renderMarkdown(content, (process.stdout.columns || 80) - indent.length * 2);
956
- console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
957
- console.log('');
958
- // Add assistant message to conversation (consistent with local mode, including reasoningContent)
959
- this.conversation.push({
960
- role: 'assistant',
961
- content,
962
- timestamp: Date.now(),
963
- reasoningContent,
964
- toolCalls: toolCalls
965
- });
966
- // Record output to session manager (consistent with local mode, including reasoningContent and toolCalls)
967
- await this.sessionManager.addOutput({
968
- role: 'assistant',
969
- content,
970
- timestamp: Date.now(),
971
- reasoningContent,
972
- toolCalls
973
- });
974
- // Handle tool calls
975
- if (toolCalls.length > 0) {
976
- await this.handleToolCalls(toolCalls, async () => {
977
- // Remote mode continuation: reuse existing taskId
978
- await this.generateRemoteResponse(0, this.currentTaskId || undefined);
979
- });
980
- }
981
- // Checkpoint support (consistent with local mode)
982
- if (this.checkpointManager.isEnabled()) {
983
- await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.toolCalls]);
984
- }
985
- // Operation completed successfully
986
- this._isOperationInProgress = false;
1062
+ // Use unified generateResponse without passing customAIClient,
1063
+ // let createLLMCaller handle remote/local selection
1064
+ await this.generateResponse(thinkingTokens, undefined, taskId);
987
1065
  // Mark task as completed (发送 status: 'end')
988
1066
  logger.debug(`[Session] Task completed: taskId=${this.currentTaskId}`);
989
1067
  if (this.remoteAIClient && this.currentTaskId) {
990
- await this.remoteAIClient.completeTask(this.currentTaskId);
1068
+ await this.remoteAIClient.completeTask?.(this.currentTaskId);
991
1069
  }
992
1070
  }
993
1071
  catch (error) {
994
- clearInterval(spinnerInterval);
995
- process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
996
1072
  // Clear the operation flag
997
1073
  this._isOperationInProgress = false;
998
1074
  if (error.message === 'Operation cancelled by user') {
1075
+ // Notify backend to cancel the task
1076
+ if (this.remoteAIClient && this.currentTaskId) {
1077
+ await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
1078
+ }
999
1079
  return;
1000
1080
  }
1001
1081
  // Handle token invalid error - trigger re-authentication
@@ -1005,49 +1085,48 @@ export class InteractiveSession {
1005
1085
  console.log(colors.info('Your browser session has been logged out. Please log in again.'));
1006
1086
  console.log('');
1007
1087
  // Clear invalid credentials and persist
1008
- // Note: Do NOT overwrite selectedAuthType - preserve user's chosen auth method
1009
- await this.configManager.set('apiKey', '');
1010
- await this.configManager.set('refreshToken', '');
1011
- await this.configManager.save('global');
1088
+ this.configManager.set('apiKey', '');
1089
+ this.configManager.set('refreshToken', '');
1090
+ this.configManager.save('global');
1012
1091
  logger.debug('[DEBUG generateRemoteResponse] Cleared invalid credentials, starting re-authentication...');
1013
1092
  // Re-authenticate
1014
1093
  await this.setupAuthentication();
1015
1094
  // Reload config to ensure we have the latest authConfig
1016
- logger.debug('[DEBUG generateRemoteResponse] Re-authentication completed, reloading config...');
1017
- await this.configManager.load();
1095
+ this.configManager.load();
1018
1096
  const authConfig = this.configManager.getAuthConfig();
1019
1097
  logger.debug('[DEBUG generateRemoteResponse] After re-auth:');
1020
1098
  logger.debug(' - authConfig.apiKey exists:', !!authConfig.apiKey ? 'true' : 'false');
1021
- logger.debug(' - authConfig.apiKey prefix:', authConfig.apiKey ? authConfig.apiKey.substring(0, 20) + '...' : 'empty');
1022
- // Recreate readline interface after inquirer
1099
+ // Recreate readline interface after interactive prompt
1023
1100
  this.rl.close();
1024
1101
  this.rl = readline.createInterface({
1025
1102
  input: process.stdin,
1026
- output: process.stdout
1103
+ output: process.stdout,
1027
1104
  });
1028
1105
  this.rl.on('close', () => {
1029
1106
  logger.debug('DEBUG: readline interface closed');
1030
1107
  });
1031
1108
  // Reinitialize RemoteAIClient with new token
1032
1109
  if (authConfig.apiKey) {
1033
- const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
1034
1110
  logger.debug('[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token');
1035
- const newWebBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
1036
- this.remoteAIClient = new RemoteAIClient(authConfig.apiKey, newWebBaseUrl, authConfig.showAIDebugInfo);
1111
+ this.remoteAIClient = createAIClient(authConfig);
1037
1112
  }
1038
1113
  else {
1039
1114
  logger.debug('[DEBUG generateRemoteResponse] WARNING: No apiKey after re-authentication!');
1040
1115
  }
1116
+ // Sync remoteAIClient reference to slashCommandHandler for /provider command
1117
+ this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
1041
1118
  // Retry the current operation
1042
1119
  console.log('');
1043
1120
  console.log(colors.info('Retrying with new authentication...'));
1044
1121
  console.log('');
1045
1122
  return this.generateRemoteResponse(thinkingTokens);
1046
1123
  }
1047
- // Mark task as cancelled when error occurs (发送 status: 'cancel')
1048
- logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}`);
1124
+ // Distinguish error types: timeout vs other failures
1125
+ const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
1126
+ const failureReason = isTimeout ? 'timeout' : 'failure';
1127
+ logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`);
1049
1128
  if (this.remoteAIClient && this.currentTaskId) {
1050
- await this.remoteAIClient.cancelTask(this.currentTaskId);
1129
+ await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => { });
1051
1130
  }
1052
1131
  console.log(colors.error(`Error: ${error.message}`));
1053
1132
  return;
@@ -1080,33 +1159,54 @@ export class InteractiveSession {
1080
1159
  }
1081
1160
  else {
1082
1161
  const toolDescription = this.getToolDescription(name, params);
1083
- console.log('');
1084
1162
  console.log(`${indent}${colors.textMuted(`${icons.loading} ${toolDescription}`)}`);
1085
1163
  }
1086
1164
  }
1087
1165
  // Execute all tools in parallel
1088
- const results = await toolRegistry.executeAll(preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })), this.executionMode);
1089
- // Process results and maintain order
1166
+ const results = await toolRegistry.executeAll(preparedToolCalls.map((tc) => ({ name: tc.name, params: tc.params })), this.executionMode);
1167
+ // Create a map to store results by tool call index to maintain original order
1168
+ const resultsByIndex = new Map();
1169
+ const usedIndices = new Set();
1170
+ for (const result of results) {
1171
+ // Find the first unused original index in preparedToolCalls that matches the tool name
1172
+ const originalIndex = preparedToolCalls.findIndex((tc, idx) => tc.name === result.tool && !usedIndices.has(idx));
1173
+ if (originalIndex !== -1) {
1174
+ usedIndices.add(originalIndex);
1175
+ resultsByIndex.set(originalIndex, result);
1176
+ }
1177
+ }
1178
+ // Process results in the original tool_calls order (critical for Anthropic format APIs)
1090
1179
  let hasError = false;
1091
- for (const { tool, result, error } of results) {
1092
- const toolCall = preparedToolCalls.find(tc => tc.name === tool);
1093
- if (!toolCall)
1094
- continue;
1095
- const { params } = toolCall;
1180
+ for (let i = 0; i < preparedToolCalls.length; i++) {
1181
+ const toolCall = preparedToolCalls[i];
1182
+ const { name: tool, params } = toolCall;
1183
+ const resultData = resultsByIndex.get(i);
1184
+ const result = resultData?.result;
1185
+ const error = resultData?.error;
1096
1186
  if (error) {
1097
- // Clear the operation flag
1098
- this._isOperationInProgress = false;
1099
1187
  if (error === 'Operation cancelled by user') {
1188
+ // Notify backend to cancel the task
1189
+ if (this.remoteAIClient && this.currentTaskId) {
1190
+ await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
1191
+ }
1192
+ // 清理 conversation 中未完成的 tool_call
1193
+ this.cleanupIncompleteToolCalls();
1194
+ this._isOperationInProgress = false;
1100
1195
  return;
1101
1196
  }
1102
1197
  hasError = true;
1103
1198
  console.log('');
1104
- console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${error}`)}`);
1199
+ console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${tool} - ${error}`)}`);
1200
+ // Add detailed error info including tool name and params for AI understanding and correction
1105
1201
  this.conversation.push({
1106
1202
  role: 'tool',
1107
- content: JSON.stringify({ error }),
1203
+ content: JSON.stringify({
1204
+ name: tool,
1205
+ parameters: params,
1206
+ error: error,
1207
+ }),
1108
1208
  tool_call_id: toolCall.id,
1109
- timestamp: Date.now()
1209
+ timestamp: Date.now(),
1110
1210
  });
1111
1211
  }
1112
1212
  else {
@@ -1116,7 +1216,7 @@ export class InteractiveSession {
1116
1216
  // Always show details for todo tools so users can see their task lists
1117
1217
  const isTodoTool = tool === 'todo_write' || tool === 'todo_read';
1118
1218
  // Special handling for edit tool with diff
1119
- const isEditTool = tool === 'edit';
1219
+ const isEditTool = tool === 'Edit';
1120
1220
  const hasDiff = isEditTool && result?.diff;
1121
1221
  // Special handling for Write tool with file preview
1122
1222
  const isWriteTool = tool === 'Write';
@@ -1144,7 +1244,10 @@ export class InteractiveSession {
1144
1244
  // Show edit result with diff
1145
1245
  console.log('');
1146
1246
  const diffOutput = renderDiff(result.diff);
1147
- const indentedDiff = diffOutput.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
1247
+ const indentedDiff = diffOutput
1248
+ .split('\n')
1249
+ .map((line) => `${displayIndent} ${line}`)
1250
+ .join('\n');
1148
1251
  console.log(`${indentedDiff}`);
1149
1252
  }
1150
1253
  else if (hasFilePreview) {
@@ -1164,7 +1267,8 @@ export class InteractiveSession {
1164
1267
  // Special handling for task tool (subagent) - show friendly summary
1165
1268
  console.log('');
1166
1269
  const subagentType = params.subagent_type;
1167
- const subagentName = params.description || (params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
1270
+ const subagentName = params.description ||
1271
+ (params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
1168
1272
  if (result?.success) {
1169
1273
  console.log(`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`);
1170
1274
  console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
@@ -1247,6 +1351,27 @@ export class InteractiveSession {
1247
1351
  }
1248
1352
  }
1249
1353
  }
1354
+ else if (tool === 'InvokeSkill') {
1355
+ // Special handling for InvokeSkill - show friendly summary
1356
+ console.log('');
1357
+ const skillName = params?.skillId || 'Unknown skill';
1358
+ const taskDesc = params?.taskDescription || '';
1359
+ if (result?.success) {
1360
+ console.log(`${displayIndent}${colors.success(`${icons.check} Skill: Completed`)}`);
1361
+ console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
1362
+ if (taskDesc) {
1363
+ const truncatedTask = taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
1364
+ console.log(`${displayIndent}${colors.textDim(` Task: ${truncatedTask}`)}`);
1365
+ }
1366
+ }
1367
+ else {
1368
+ console.log(`${displayIndent}${colors.error(`${icons.cross} Skill: Failed`)}`);
1369
+ console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
1370
+ if (result?.message) {
1371
+ console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
1372
+ }
1373
+ }
1374
+ }
1250
1375
  else if (showToolDetails) {
1251
1376
  console.log('');
1252
1377
  console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
@@ -1261,7 +1386,10 @@ export class InteractiveSession {
1261
1386
  const resultPreview = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1262
1387
  const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
1263
1388
  // Indent the preview
1264
- const indentedPreview = truncatedPreview.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
1389
+ const indentedPreview = truncatedPreview
1390
+ .split('\n')
1391
+ .map((line) => `${displayIndent} ${line}`)
1392
+ .join('\n');
1265
1393
  console.log(`${indentedPreview}`);
1266
1394
  }
1267
1395
  else {
@@ -1271,9 +1399,9 @@ export class InteractiveSession {
1271
1399
  tool,
1272
1400
  params,
1273
1401
  result,
1274
- timestamp: Date.now()
1402
+ timestamp: Date.now(),
1275
1403
  };
1276
- this.toolCalls.push(toolCallRecord);
1404
+ this.tool_calls.push(toolCallRecord);
1277
1405
  // Record tool output to session manager
1278
1406
  await this.sessionManager.addOutput({
1279
1407
  role: 'tool',
@@ -1281,78 +1409,104 @@ export class InteractiveSession {
1281
1409
  toolName: tool,
1282
1410
  toolParams: params,
1283
1411
  toolResult: result,
1284
- timestamp: Date.now()
1412
+ timestamp: Date.now(),
1285
1413
  });
1414
+ // Unified message format with tool name and params
1415
+ // Format: OpenAI-compatible tool result with plain text content
1286
1416
  this.conversation.push({
1287
1417
  role: 'tool',
1288
- content: JSON.stringify(result),
1418
+ content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
1289
1419
  tool_call_id: toolCall.id,
1290
- timestamp: Date.now()
1420
+ timestamp: Date.now(),
1291
1421
  });
1292
1422
  }
1293
1423
  }
1294
1424
  // Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
1295
1425
  // For all other cases (success, failure, errors), always return results for further processing
1296
- const guiSubagentCancelled = preparedToolCalls.some(tc => tc.name === 'task' && tc.params?.subagent_type === 'gui-subagent' && results.some(r => r.tool === 'task' && r.result?.cancelled === true));
1426
+ const guiSubagentCancelled = preparedToolCalls.some((tc) => tc.name === 'task' &&
1427
+ tc.params?.subagent_type === 'gui-subagent' &&
1428
+ results.some((r) => r.tool === 'task' && r.result?.cancelled === true));
1297
1429
  // If GUI agent was cancelled by user, don't continue generating response
1298
1430
  // This avoids wasting API calls and tokens on cancelled tasks
1299
1431
  if (guiSubagentCancelled) {
1300
- console.log('');
1301
- console.log(`${indent}${colors.textMuted('GUI task cancelled by user')}`);
1302
1432
  this._isOperationInProgress = false;
1303
1433
  return;
1304
1434
  }
1305
- // Handle errors and completion based on whether onComplete callback is provided
1306
- if (hasError) {
1307
- this._isOperationInProgress = false;
1308
- if (onComplete) {
1309
- // Remote mode: callback handles error state (throws to mark task cancelled)
1310
- throw new Error('Tool execution failed');
1311
- }
1312
- else {
1313
- // Local mode: throw error to mark task as cancelled
1314
- throw new Error('Tool execution failed');
1315
- }
1316
- }
1317
- // Continue based on mode
1435
+ // Continue based on mode - unified handling for both success and error cases
1318
1436
  if (onComplete) {
1437
+ await this.checkAndCompressContext();
1319
1438
  // Remote mode: use provided callback
1320
1439
  await onComplete();
1321
1440
  }
1322
1441
  else {
1323
- // Local mode: default behavior
1442
+ await this.checkAndCompressContext();
1443
+ // Local mode: default behavior - continue with generateResponse
1324
1444
  await this.generateResponse();
1325
1445
  }
1326
1446
  }
1447
+ /**
1448
+ * Clean up incomplete tool calls from conversation after cancellation
1449
+ * This removes assistant messages with tool_calls that don't have corresponding tool_results
1450
+ */
1451
+ async cleanupIncompleteToolCalls() {
1452
+ // 从后往前找到包含 tool_calls 的 assistant 消息
1453
+ for (let i = this.conversation.length - 1; i >= 0; i--) {
1454
+ const msg = this.conversation[i];
1455
+ if (msg.role === 'assistant' && msg.tool_calls?.length) {
1456
+ // 收集所有 tool_call IDs
1457
+ const allToolCallIds = new Set(msg.tool_calls.map((tc) => tc.id));
1458
+ // 找出哪些 tool_call IDs 已经有对应的 tool_result
1459
+ const completedToolCallIds = new Set();
1460
+ for (let k = i + 1; k < this.conversation.length; k++) {
1461
+ const resultMsg = this.conversation[k];
1462
+ if (resultMsg.role === 'tool' && resultMsg.tool_call_id) {
1463
+ completedToolCallIds.add(resultMsg.tool_call_id);
1464
+ }
1465
+ else if (resultMsg.role === 'user' || resultMsg.role === 'assistant') {
1466
+ break; // 遇到下一个角色消息就停止
1467
+ }
1468
+ }
1469
+ // 找出未完成的 tool_call IDs
1470
+ const incompleteToolCallIds = [...allToolCallIds].filter(id => !completedToolCallIds.has(id));
1471
+ // 如果所有 tool_call 都已完成,不需要清理
1472
+ if (incompleteToolCallIds.length === 0) {
1473
+ break;
1474
+ }
1475
+ // 只移除未完成的 tool_call,不移除已完成的 tool_result
1476
+ msg.tool_calls = msg.tool_calls.filter((tc) => !incompleteToolCallIds.includes(tc.id));
1477
+ break;
1478
+ }
1479
+ }
1480
+ }
1327
1481
  /**
1328
1482
  * Get user-friendly description for tool
1329
1483
  */
1330
1484
  getToolDescription(toolName, params) {
1331
1485
  const descriptions = {
1332
- 'Read': (p) => `Read file: ${this.truncatePath(p.filePath)}`,
1333
- 'Write': (p) => `Write file: ${this.truncatePath(p.filePath)}`,
1334
- 'Grep': (p) => `Search text: "${p.pattern}"`,
1335
- 'Bash': (p) => `Execute command: ${this.truncateCommand(p.command)}`,
1336
- 'ListDirectory': (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
1337
- 'SearchFiles': (p) => `Search files: ${p.pattern}`,
1338
- 'DeleteFile': (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
1339
- 'CreateDirectory': (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
1340
- 'edit': (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
1341
- 'web_search': (p) => `Web search: "${p.query}"`,
1342
- 'todo_write': () => `Update todo list`,
1343
- 'todo_read': () => `Read todo list`,
1344
- 'task': (p) => `Launch subtask: ${p.description}`,
1345
- 'ReadBashOutput': (p) => `Read task output: ${p.task_id}`,
1346
- 'web_fetch': () => `Fetch web content`,
1347
- 'ask_user_question': () => `Ask user`,
1348
- 'save_memory': () => `Save memory`,
1349
- 'exit_plan_mode': () => `Complete plan`,
1350
- 'xml_escape': (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
1351
- 'image_read': (p) => `Read image: ${this.truncatePath(p.image_input)}`,
1486
+ Read: (p) => `Read file: ${this.truncatePath(p.filePath)}`,
1487
+ Write: (p) => `Write file: ${this.truncatePath(p.filePath)}`,
1488
+ Grep: (p) => `Search text: "${p.pattern}"`,
1489
+ Bash: (p) => `Execute command: ${this.truncateCommand(p.command)}`,
1490
+ ListDirectory: (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
1491
+ SearchFiles: (p) => `Search files: ${p.pattern}`,
1492
+ DeleteFile: (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
1493
+ CreateDirectory: (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
1494
+ Edit: (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
1495
+ web_search: (p) => `Web search: "${p.query}"`,
1496
+ todo_write: () => `Update todo list`,
1497
+ todo_read: () => `Read todo list`,
1498
+ task: (p) => `Launch subtask: ${p.description}`,
1499
+ ReadBashOutput: (p) => `Read task output: ${p.task_id}`,
1500
+ web_fetch: () => `Fetch web content`,
1501
+ ask_user_question: () => `Ask user`,
1502
+ save_memory: () => `Save memory`,
1503
+ exit_plan_mode: () => `Complete plan`,
1504
+ xml_escape: (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
1505
+ image_read: (p) => `Read image: ${this.truncatePath(p.image_input)}`,
1352
1506
  // 'Skill': (p) => `Execute skill: ${p.skill}`,
1353
1507
  // 'ListSkills': () => `List available skills`,
1354
1508
  // 'GetSkillDetails': (p) => `Get skill details: ${p.skill}`,
1355
- 'InvokeSkill': (p) => `Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`
1509
+ InvokeSkill: (p) => `Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`,
1356
1510
  };
1357
1511
  const getDescription = descriptions[toolName];
1358
1512
  return getDescription ? getDescription(params) : `Execute tool: ${toolName}`;
@@ -1385,10 +1539,10 @@ export class InteractiveSession {
1385
1539
  return `${indent}${colors.textMuted('No tasks')}`;
1386
1540
  }
1387
1541
  const statusConfig = {
1388
- 'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1389
- 'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1390
- 'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
1391
- 'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
1542
+ pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1543
+ in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1544
+ completed: { icon: icons.success, color: colors.success, label: 'Completed' },
1545
+ failed: { icon: icons.error, color: colors.error, label: 'Failed' },
1392
1546
  };
1393
1547
  const lines = [];
1394
1548
  for (const todo of todos) {
@@ -1501,7 +1655,155 @@ export class InteractiveSession {
1501
1655
  return this.currentTaskId;
1502
1656
  }
1503
1657
  }
1658
+ /**
1659
+ * Clean up stale temporary workspaces from previous sessions.
1660
+ * Called at startup to ensure no leftover temp files accumulate.
1661
+ */
1662
+ async function cleanupStaleWorkspaces() {
1663
+ try {
1664
+ const configManager = getConfigManager();
1665
+ const workspacePath = configManager.getWorkspacePath();
1666
+ // Use default workspace path if workspacePath is empty or undefined
1667
+ const effectiveWorkspacePath = (workspacePath && workspacePath.trim())
1668
+ ? workspacePath
1669
+ : os.homedir();
1670
+ const baseWorkspaceDir = path.join(effectiveWorkspacePath, '.xagent', 'workspace');
1671
+ // First, verify the workspace directory exists before attempting cleanup
1672
+ try {
1673
+ const stats = await fsPromises.stat(baseWorkspaceDir);
1674
+ if (!stats.isDirectory()) {
1675
+ return; // Not a directory, skip cleanup
1676
+ }
1677
+ }
1678
+ catch {
1679
+ // Directory doesn't exist - nothing to clean
1680
+ return;
1681
+ }
1682
+ try {
1683
+ const entries = await fsPromises.readdir(baseWorkspaceDir, { withFileTypes: true });
1684
+ let cleanedCount = 0;
1685
+ for (const entry of entries) {
1686
+ if (entry.isDirectory()) {
1687
+ const fullPath = path.join(baseWorkspaceDir, entry.name);
1688
+ try {
1689
+ await fsPromises.rm(fullPath, { recursive: true, force: true });
1690
+ cleanedCount++;
1691
+ }
1692
+ catch {
1693
+ // Skip directories that can't be removed (in use or permission issues)
1694
+ }
1695
+ }
1696
+ }
1697
+ if (cleanedCount > 0) {
1698
+ console.log(colors.textDim(`🧹 Cleaned up ${cleanedCount} stale workspace(s)`));
1699
+ }
1700
+ }
1701
+ catch {
1702
+ // Can't read directory - skip cleanup
1703
+ }
1704
+ }
1705
+ catch {
1706
+ // Config not available - skip cleanup
1707
+ }
1708
+ }
1709
+ /**
1710
+ * Initialize built-in skills on first run.
1711
+ * Checks if user skills directory is empty or doesn't exist,
1712
+ * then copies all built-in skills including the protected find-skills.
1713
+ * @returns Number of skills initialized, or 0 if no initialization was needed.
1714
+ */
1715
+ async function initializeSkillsOnDemand() {
1716
+ const __filename = fileURLToPath(import.meta.url);
1717
+ const __dirname = path.dirname(__filename);
1718
+ // Get user skills directory (respects OS-specific paths)
1719
+ const configManager = getConfigManager();
1720
+ const userSkillsPath = configManager.getUserSkillsPath() || path.join(os.homedir(), '.xagent', 'skills');
1721
+ // Check if user skills directory exists and has skills
1722
+ let hasSkills = false;
1723
+ try {
1724
+ const entries = await fsPromises.readdir(userSkillsPath, { withFileTypes: true });
1725
+ hasSkills = entries.some(e => e.isDirectory());
1726
+ }
1727
+ catch {
1728
+ hasSkills = false;
1729
+ }
1730
+ // If skills already exist, skip initialization
1731
+ if (hasSkills) {
1732
+ return 0;
1733
+ }
1734
+ // Ensure user skills directory exists
1735
+ await fsPromises.mkdir(userSkillsPath, { recursive: true });
1736
+ // Define skill source directories
1737
+ const builtinSkillsDir = path.join(__dirname, '..', 'skills', 'skills');
1738
+ const findSkillsDir = path.join(__dirname, '..', 'find-skills');
1739
+ const skillsToInstall = [];
1740
+ // Add find-skills from root directory
1741
+ if (fs.existsSync(findSkillsDir) && fs.existsSync(path.join(findSkillsDir, 'SKILL.md'))) {
1742
+ skillsToInstall.push({ source: findSkillsDir, name: 'find-skills' });
1743
+ }
1744
+ // Add skills from skills/skills directory
1745
+ if (fs.existsSync(builtinSkillsDir)) {
1746
+ const entries = fs.readdirSync(builtinSkillsDir, { withFileTypes: true });
1747
+ for (const entry of entries) {
1748
+ if (entry.isDirectory()) {
1749
+ const skillPath = path.join(builtinSkillsDir, entry.name);
1750
+ if (fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
1751
+ skillsToInstall.push({ source: skillPath, name: entry.name });
1752
+ }
1753
+ }
1754
+ }
1755
+ }
1756
+ if (skillsToInstall.length === 0) {
1757
+ return 0;
1758
+ }
1759
+ // Create user skills directory (already done above, but ensure it exists)
1760
+ await fsPromises.mkdir(userSkillsPath, { recursive: true });
1761
+ // Copy all skills
1762
+ for (const { source, name } of skillsToInstall) {
1763
+ const destPath = path.join(userSkillsPath, name);
1764
+ if (!fs.existsSync(destPath)) {
1765
+ await copyDirectoryRecursiveAsync(source, destPath);
1766
+ }
1767
+ }
1768
+ return skillsToInstall.length;
1769
+ }
1770
+ // Synchronous version (kept for backwards compatibility)
1771
+ function copyDirectoryRecursive(src, dest) {
1772
+ if (!fs.existsSync(dest)) {
1773
+ fs.mkdirSync(dest, { recursive: true });
1774
+ }
1775
+ const entries = fs.readdirSync(src, { withFileTypes: true });
1776
+ for (const entry of entries) {
1777
+ const srcPath = path.join(src, entry.name);
1778
+ const destPath = path.join(dest, entry.name);
1779
+ if (entry.isDirectory()) {
1780
+ copyDirectoryRecursive(srcPath, destPath);
1781
+ }
1782
+ else if (entry.isFile()) {
1783
+ fs.copyFileSync(srcPath, destPath);
1784
+ }
1785
+ }
1786
+ }
1787
+ // Asynchronous version for concurrent-safe initialization
1788
+ async function copyDirectoryRecursiveAsync(src, dest) {
1789
+ await fsPromises.mkdir(dest, { recursive: true });
1790
+ const entries = await fsPromises.readdir(src, { withFileTypes: true });
1791
+ for (const entry of entries) {
1792
+ const srcPath = path.join(src, entry.name);
1793
+ const destPath = path.join(dest, entry.name);
1794
+ if (entry.isDirectory()) {
1795
+ await copyDirectoryRecursiveAsync(srcPath, destPath);
1796
+ }
1797
+ else if (entry.isFile()) {
1798
+ await fsPromises.copyFile(srcPath, destPath);
1799
+ }
1800
+ }
1801
+ }
1504
1802
  export async function startInteractiveSession() {
1803
+ // Clean up any leftover temp workspaces from previous sessions
1804
+ await cleanupStaleWorkspaces();
1805
+ // Initialize built-in skills on first run (silent, returns count)
1806
+ const initializedCount = await initializeSkillsOnDemand();
1505
1807
  const session = new InteractiveSession();
1506
1808
  // Flag to control shutdown
1507
1809
  session._isShuttingDown = false;
@@ -1537,6 +1839,15 @@ export async function startInteractiveSession() {
1537
1839
  // Force exit
1538
1840
  process.exit(0);
1539
1841
  });
1842
+ await session.start(initializedCount);
1843
+ // Check for updates on startup
1844
+ try {
1845
+ const { checkUpdatesOnStartup } = await import('./update.js');
1846
+ await checkUpdatesOnStartup();
1847
+ }
1848
+ catch (error) {
1849
+ // Silently ignore update check failures
1850
+ }
1540
1851
  await session.start();
1541
1852
  }
1542
1853
  // Singleton session instance for access from other modules