@xagent-ai/cli 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (568) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/workflows/ci.yml +72 -0
  4. package/.github/workflows/release.yml +109 -0
  5. package/.gitmodules +3 -3
  6. package/README.md +326 -280
  7. package/README_CN.md +325 -279
  8. package/dist/ai-client/factory.d.ts +52 -0
  9. package/dist/ai-client/factory.d.ts.map +1 -0
  10. package/dist/ai-client/factory.js +132 -0
  11. package/dist/ai-client/factory.js.map +1 -0
  12. package/dist/ai-client/index.d.ts +20 -0
  13. package/dist/ai-client/index.d.ts.map +1 -0
  14. package/dist/ai-client/index.js +49 -0
  15. package/dist/ai-client/index.js.map +1 -0
  16. package/dist/ai-client/providers/anthropic.d.ts +57 -0
  17. package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
  18. package/dist/ai-client/providers/anthropic.js +400 -0
  19. package/dist/ai-client/providers/anthropic.js.map +1 -0
  20. package/dist/ai-client/providers/openai.d.ts +57 -0
  21. package/dist/ai-client/providers/openai.d.ts.map +1 -0
  22. package/dist/ai-client/providers/openai.js +286 -0
  23. package/dist/ai-client/providers/openai.js.map +1 -0
  24. package/dist/ai-client/providers/remote.d.ts +111 -0
  25. package/dist/ai-client/providers/remote.d.ts.map +1 -0
  26. package/dist/ai-client/providers/remote.js +351 -0
  27. package/dist/ai-client/providers/remote.js.map +1 -0
  28. package/dist/ai-client/registry.d.ts +51 -0
  29. package/dist/ai-client/registry.d.ts.map +1 -0
  30. package/dist/ai-client/registry.js +81 -0
  31. package/dist/ai-client/registry.js.map +1 -0
  32. package/dist/ai-client/types.d.ts +260 -0
  33. package/dist/ai-client/types.d.ts.map +1 -0
  34. package/dist/ai-client/types.js +73 -0
  35. package/dist/ai-client/types.js.map +1 -0
  36. package/dist/ai-client-factory.d.ts +62 -0
  37. package/dist/ai-client-factory.d.ts.map +1 -0
  38. package/dist/ai-client-factory.js +157 -0
  39. package/dist/ai-client-factory.js.map +1 -0
  40. package/dist/auth.d.ts +23 -1
  41. package/dist/auth.d.ts.map +1 -1
  42. package/dist/auth.js +160 -168
  43. package/dist/auth.js.map +1 -1
  44. package/dist/cancellation.d.ts +5 -4
  45. package/dist/cancellation.d.ts.map +1 -1
  46. package/dist/cancellation.js +55 -32
  47. package/dist/cancellation.js.map +1 -1
  48. package/dist/checkpoint.d.ts +1 -1
  49. package/dist/checkpoint.d.ts.map +1 -1
  50. package/dist/checkpoint.js +2 -2
  51. package/dist/checkpoint.js.map +1 -1
  52. package/dist/cli.js +626 -13
  53. package/dist/cli.js.map +1 -1
  54. package/dist/config.d.ts +10 -4
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +62 -25
  57. package/dist/config.js.map +1 -1
  58. package/dist/context-compressor.d.ts +81 -16
  59. package/dist/context-compressor.d.ts.map +1 -1
  60. package/dist/context-compressor.js +712 -153
  61. package/dist/context-compressor.js.map +1 -1
  62. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  63. package/dist/gui-subagent/action-parser/actionParser.js +4 -2
  64. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  65. package/dist/gui-subagent/agent/gui-agent.d.ts +29 -2
  66. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  67. package/dist/gui-subagent/agent/gui-agent.js +87 -45
  68. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  69. package/dist/gui-subagent/index.d.ts +16 -1
  70. package/dist/gui-subagent/index.d.ts.map +1 -1
  71. package/dist/gui-subagent/index.js +4 -0
  72. package/dist/gui-subagent/index.js.map +1 -1
  73. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
  74. package/dist/gui-subagent/operator/base-operator.js +0 -1
  75. package/dist/gui-subagent/operator/base-operator.js.map +1 -1
  76. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  77. package/dist/gui-subagent/operator/computer-operator.js +29 -8
  78. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  79. package/dist/gui-subagent/types/actions.d.ts +1 -1
  80. package/dist/gui-subagent/types/actions.d.ts.map +1 -1
  81. package/dist/gui-subagent/types/actions.js +0 -1
  82. package/dist/gui-subagent/types/actions.js.map +1 -1
  83. package/dist/gui-subagent/types/operator.d.ts +1 -1
  84. package/dist/gui-subagent/types/operator.d.ts.map +1 -1
  85. package/dist/index.d.ts +1 -2
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/input-processor.d.ts.map +1 -1
  90. package/dist/input-processor.js +6 -3
  91. package/dist/input-processor.js.map +1 -1
  92. package/dist/mcp.d.ts +5 -0
  93. package/dist/mcp.d.ts.map +1 -1
  94. package/dist/mcp.js +81 -35
  95. package/dist/mcp.js.map +1 -1
  96. package/dist/ripgrep.d.ts +29 -0
  97. package/dist/ripgrep.d.ts.map +1 -0
  98. package/dist/ripgrep.js +292 -0
  99. package/dist/ripgrep.js.map +1 -0
  100. package/dist/session.d.ts +23 -7
  101. package/dist/session.d.ts.map +1 -1
  102. package/dist/session.js +624 -243
  103. package/dist/session.js.map +1 -1
  104. package/dist/shell.d.ts +33 -0
  105. package/dist/shell.d.ts.map +1 -0
  106. package/dist/shell.js +125 -0
  107. package/dist/shell.js.map +1 -0
  108. package/dist/skill-installer.d.ts +38 -0
  109. package/dist/skill-installer.d.ts.map +1 -0
  110. package/dist/skill-installer.js +447 -0
  111. package/dist/skill-installer.js.map +1 -0
  112. package/dist/skill-invoker.d.ts +7 -1
  113. package/dist/skill-invoker.d.ts.map +1 -1
  114. package/dist/skill-invoker.js +34 -13
  115. package/dist/skill-invoker.js.map +1 -1
  116. package/dist/skill-loader.d.ts +8 -3
  117. package/dist/skill-loader.d.ts.map +1 -1
  118. package/dist/skill-loader.js +46 -44
  119. package/dist/skill-loader.js.map +1 -1
  120. package/dist/skill-manager.d.ts +85 -0
  121. package/dist/skill-manager.d.ts.map +1 -0
  122. package/dist/skill-manager.js +340 -0
  123. package/dist/skill-manager.js.map +1 -0
  124. package/dist/slash-commands.d.ts +38 -1
  125. package/dist/slash-commands.d.ts.map +1 -1
  126. package/dist/slash-commands.js +912 -296
  127. package/dist/slash-commands.js.map +1 -1
  128. package/dist/smart-approval.d.ts.map +1 -1
  129. package/dist/smart-approval.js +67 -55
  130. package/dist/smart-approval.js.map +1 -1
  131. package/dist/system-prompt-generator.d.ts +6 -0
  132. package/dist/system-prompt-generator.d.ts.map +1 -1
  133. package/dist/system-prompt-generator.js +84 -34
  134. package/dist/system-prompt-generator.js.map +1 -1
  135. package/dist/terminal.d.ts +28 -0
  136. package/dist/terminal.d.ts.map +1 -0
  137. package/dist/terminal.js +82 -0
  138. package/dist/terminal.js.map +1 -0
  139. package/dist/tools.d.ts +23 -7
  140. package/dist/tools.d.ts.map +1 -1
  141. package/dist/tools.js +797 -437
  142. package/dist/tools.js.map +1 -1
  143. package/dist/truncate.d.ts +55 -0
  144. package/dist/truncate.d.ts.map +1 -0
  145. package/dist/truncate.js +130 -0
  146. package/dist/truncate.js.map +1 -0
  147. package/dist/types.d.ts +27 -9
  148. package/dist/types.d.ts.map +1 -1
  149. package/dist/update.d.ts.map +1 -1
  150. package/dist/update.js +17 -28
  151. package/dist/update.js.map +1 -1
  152. package/dist/workflow.d.ts +5 -1
  153. package/dist/workflow.d.ts.map +1 -1
  154. package/dist/workflow.js +60 -47
  155. package/dist/workflow.js.map +1 -1
  156. package/docs/architecture/mcp-integration-guide.md +304 -194
  157. package/docs/architecture/overview.md +169 -169
  158. package/docs/architecture/tool-system-design.md +134 -134
  159. package/docs/cli/commands.md +349 -238
  160. package/docs/smart-mode.md +281 -281
  161. package/docs/third-party-models.md +439 -439
  162. package/find-skills/SKILL.md +133 -0
  163. package/package.json +89 -90
  164. package/scripts/install-ripgrep.js +241 -0
  165. package/src/ai-client/factory.ts +151 -0
  166. package/src/ai-client/index.ts +61 -0
  167. package/src/ai-client/providers/anthropic.ts +466 -0
  168. package/src/ai-client/providers/openai.ts +342 -0
  169. package/src/ai-client/providers/remote.ts +436 -0
  170. package/src/ai-client/registry.ts +97 -0
  171. package/src/ai-client/types.ts +345 -0
  172. package/src/ai-client-factory.ts +204 -0
  173. package/src/auth.ts +663 -614
  174. package/src/cancellation.ts +205 -176
  175. package/src/checkpoint.ts +219 -219
  176. package/src/cli.ts +1406 -743
  177. package/src/config.ts +341 -297
  178. package/src/context-compressor.ts +982 -290
  179. package/src/conversation.ts +288 -288
  180. package/src/gui-subagent/action-parser/actionParser.ts +318 -315
  181. package/src/gui-subagent/action-parser/constants.ts +14 -14
  182. package/src/gui-subagent/action-parser/index.ts +8 -8
  183. package/src/gui-subagent/action-parser/types.ts +31 -31
  184. package/src/gui-subagent/agent/gui-agent.ts +1151 -1089
  185. package/src/gui-subagent/agent/index.ts +5 -5
  186. package/src/gui-subagent/index.ts +177 -163
  187. package/src/gui-subagent/operator/base-operator.ts +244 -245
  188. package/src/gui-subagent/operator/computer-operator.ts +540 -520
  189. package/src/gui-subagent/operator/index.ts +6 -6
  190. package/src/gui-subagent/types/actions.ts +260 -262
  191. package/src/gui-subagent/types/index.ts +6 -6
  192. package/src/gui-subagent/types/operator.ts +106 -106
  193. package/src/gui-subagent/utils.ts +51 -51
  194. package/src/index.ts +17 -18
  195. package/src/input-processor.ts +6 -3
  196. package/src/logger.ts +438 -438
  197. package/src/mcp.ts +730 -682
  198. package/src/memory.ts +344 -344
  199. package/src/ripgrep.ts +368 -0
  200. package/src/session-manager.ts +308 -308
  201. package/src/session.ts +948 -386
  202. package/src/shell.ts +133 -0
  203. package/src/skill-installer.ts +518 -0
  204. package/src/skill-invoker.ts +960 -935
  205. package/src/skill-loader.ts +501 -496
  206. package/src/skill-manager.ts +384 -0
  207. package/src/slash-commands.ts +2181 -1389
  208. package/src/smart-approval.ts +117 -73
  209. package/src/system-prompt-generator.ts +89 -34
  210. package/src/terminal.ts +96 -0
  211. package/src/theme.ts +738 -738
  212. package/src/tools.ts +1336 -773
  213. package/src/truncate.ts +173 -0
  214. package/src/types.ts +219 -198
  215. package/src/update.ts +22 -32
  216. package/src/workflow.ts +523 -508
  217. package/tsconfig.json +22 -22
  218. package/vitest.config.ts +19 -19
  219. package/dist/ai-client.d.ts +0 -86
  220. package/dist/ai-client.d.ts.map +0 -1
  221. package/dist/ai-client.js +0 -1372
  222. package/dist/ai-client.js.map +0 -1
  223. package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
  224. package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
  225. package/dist/gui-subagent/operator/browser-operator.js +0 -306
  226. package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
  227. package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
  228. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
  229. package/dist/gui-subagent/operator/desktop-operator.js +0 -527
  230. package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
  231. package/dist/hook.d.ts +0 -73
  232. package/dist/hook.d.ts.map +0 -1
  233. package/dist/hook.js +0 -156
  234. package/dist/hook.js.map +0 -1
  235. package/dist/input-history.d.ts +0 -24
  236. package/dist/input-history.d.ts.map +0 -1
  237. package/dist/input-history.js +0 -94
  238. package/dist/input-history.js.map +0 -1
  239. package/dist/keyboard-manager.d.ts +0 -151
  240. package/dist/keyboard-manager.d.ts.map +0 -1
  241. package/dist/keyboard-manager.js +0 -396
  242. package/dist/keyboard-manager.js.map +0 -1
  243. package/dist/print-system-prompt.d.ts +0 -2
  244. package/dist/print-system-prompt.d.ts.map +0 -1
  245. package/dist/print-system-prompt.js +0 -40
  246. package/dist/print-system-prompt.js.map +0 -1
  247. package/dist/remote-ai-client.d.ts +0 -104
  248. package/dist/remote-ai-client.d.ts.map +0 -1
  249. package/dist/remote-ai-client.js +0 -552
  250. package/dist/remote-ai-client.js.map +0 -1
  251. package/dist/sdk-output-adapter.d.ts +0 -232
  252. package/dist/sdk-output-adapter.d.ts.map +0 -1
  253. package/dist/sdk-output-adapter.js +0 -636
  254. package/dist/sdk-output-adapter.js.map +0 -1
  255. package/dist/sdk-session-v2.d.ts +0 -13
  256. package/dist/sdk-session-v2.d.ts.map +0 -1
  257. package/dist/sdk-session-v2.js +0 -46
  258. package/dist/sdk-session-v2.js.map +0 -1
  259. package/dist/sdk-session.d.ts +0 -13
  260. package/dist/sdk-session.d.ts.map +0 -1
  261. package/dist/sdk-session.js +0 -48
  262. package/dist/sdk-session.js.map +0 -1
  263. package/dist/test-boundary-conditions.d.ts.map +0 -1
  264. package/dist/test-boundary-conditions.js.map +0 -1
  265. package/dist/test-cancellation-fix.d.ts.map +0 -1
  266. package/dist/test-cancellation-fix.js.map +0 -1
  267. package/dist/test-input-history.d.ts.map +0 -1
  268. package/dist/test-input-history.js.map +0 -1
  269. package/dist/test-interaction-flow.d.ts.map +0 -1
  270. package/dist/test-interaction-flow.js.map +0 -1
  271. package/dist/test-quick.d.ts.map +0 -1
  272. package/dist/test-quick.js.map +0 -1
  273. package/dist/test-user-interaction.d.ts.map +0 -1
  274. package/dist/test-user-interaction.js.map +0 -1
  275. package/dist/tools/edit-diff.d.ts +0 -32
  276. package/dist/tools/edit-diff.d.ts.map +0 -1
  277. package/dist/tools/edit-diff.js +0 -185
  278. package/dist/tools/edit-diff.js.map +0 -1
  279. package/dist/tools/edit.d.ts +0 -11
  280. package/dist/tools/edit.d.ts.map +0 -1
  281. package/dist/tools/edit.js +0 -129
  282. package/dist/tools/edit.js.map +0 -1
  283. package/dist/unified-session.d.ts +0 -42
  284. package/dist/unified-session.d.ts.map +0 -1
  285. package/dist/unified-session.js +0 -271
  286. package/dist/unified-session.js.map +0 -1
  287. package/skills/.claude-plugin/marketplace.json +0 -45
  288. package/skills/README.md +0 -94
  289. package/skills/THIRD_PARTY_NOTICES.md +0 -405
  290. package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
  291. package/skills/skills/algorithmic-art/SKILL.md +0 -405
  292. package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
  293. package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
  294. package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
  295. package/skills/skills/brand-guidelines/SKILL.md +0 -73
  296. package/skills/skills/canvas-design/LICENSE.txt +0 -202
  297. package/skills/skills/canvas-design/SKILL.md +0 -130
  298. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  299. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  300. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  301. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  302. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  303. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  304. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  305. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  306. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  307. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  308. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  309. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  310. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  311. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  312. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  313. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  314. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  315. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  316. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  317. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  318. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  319. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  320. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  321. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  322. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  323. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  324. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  325. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  326. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  327. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  328. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  329. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  330. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  331. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  332. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  333. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  334. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  335. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  336. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  337. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  338. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  339. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  340. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  341. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  342. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  343. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  344. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  345. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  346. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  347. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  348. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  349. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  350. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  351. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  352. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  353. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  354. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  355. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  356. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  357. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  358. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  359. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  360. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  361. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  362. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  363. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  364. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  365. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  366. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  367. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  368. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  369. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  370. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  371. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  372. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  373. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  374. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  375. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  376. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  377. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  378. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  379. package/skills/skills/doc-coauthoring/SKILL.md +0 -375
  380. package/skills/skills/docx/LICENSE.txt +0 -30
  381. package/skills/skills/docx/SKILL.md +0 -197
  382. package/skills/skills/docx/docx-js.md +0 -350
  383. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  384. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  385. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  386. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  387. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  388. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  389. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  390. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  391. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  392. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  393. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  394. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  395. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  396. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  397. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  398. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  399. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  400. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  401. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  402. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  403. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  404. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  405. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  406. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  407. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  408. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  409. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  410. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  411. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  412. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  413. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  414. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  415. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  416. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  417. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  418. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  419. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  420. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  421. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  422. package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
  423. package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
  424. package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
  425. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  426. package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
  427. package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  428. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  429. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  430. package/skills/skills/docx/ooxml.md +0 -610
  431. package/skills/skills/docx/scripts/__init__.py +0 -1
  432. package/skills/skills/docx/scripts/document.py +0 -1276
  433. package/skills/skills/docx/scripts/templates/comments.xml +0 -3
  434. package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  435. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  436. package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
  437. package/skills/skills/docx/scripts/templates/people.xml +0 -3
  438. package/skills/skills/docx/scripts/utilities.py +0 -374
  439. package/skills/skills/frontend-design/LICENSE.txt +0 -177
  440. package/skills/skills/frontend-design/SKILL.md +0 -42
  441. package/skills/skills/internal-comms/LICENSE.txt +0 -202
  442. package/skills/skills/internal-comms/SKILL.md +0 -32
  443. package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
  444. package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
  445. package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
  446. package/skills/skills/internal-comms/examples/general-comms.md +0 -16
  447. package/skills/skills/mcp-builder/LICENSE.txt +0 -202
  448. package/skills/skills/mcp-builder/SKILL.md +0 -236
  449. package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
  450. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  451. package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  452. package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  453. package/skills/skills/mcp-builder/scripts/connections.py +0 -151
  454. package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
  455. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  456. package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
  457. package/skills/skills/pdf/LICENSE.txt +0 -30
  458. package/skills/skills/pdf/SKILL.md +0 -294
  459. package/skills/skills/pdf/forms.md +0 -205
  460. package/skills/skills/pdf/reference.md +0 -612
  461. package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  462. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  463. package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
  464. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  465. package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
  466. package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
  467. package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  468. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  469. package/skills/skills/pptx/LICENSE.txt +0 -30
  470. package/skills/skills/pptx/SKILL.md +0 -484
  471. package/skills/skills/pptx/html2pptx.md +0 -625
  472. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  473. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  474. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  475. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  476. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  477. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  478. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  479. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  480. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  481. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  482. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  483. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  484. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  485. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  486. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  487. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  488. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  489. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  490. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  491. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  492. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  493. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  494. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  495. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  496. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  497. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  498. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  499. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  500. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  501. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  502. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  503. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  504. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  505. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  506. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  507. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  508. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  509. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  510. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  511. package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
  512. package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
  513. package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
  514. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  515. package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  516. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  517. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  518. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  519. package/skills/skills/pptx/ooxml.md +0 -427
  520. package/skills/skills/pptx/scripts/html2pptx.js +0 -979
  521. package/skills/skills/pptx/scripts/inventory.py +0 -1020
  522. package/skills/skills/pptx/scripts/rearrange.py +0 -231
  523. package/skills/skills/pptx/scripts/replace.py +0 -385
  524. package/skills/skills/pptx/scripts/thumbnail.py +0 -450
  525. package/skills/skills/skill-creator/LICENSE.txt +0 -202
  526. package/skills/skills/skill-creator/SKILL.md +0 -356
  527. package/skills/skills/skill-creator/references/output-patterns.md +0 -82
  528. package/skills/skills/skill-creator/references/workflows.md +0 -28
  529. package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
  530. package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
  531. package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
  532. package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
  533. package/skills/skills/slack-gif-creator/SKILL.md +0 -254
  534. package/skills/skills/slack-gif-creator/core/easing.py +0 -234
  535. package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
  536. package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
  537. package/skills/skills/slack-gif-creator/core/validators.py +0 -136
  538. package/skills/skills/slack-gif-creator/requirements.txt +0 -4
  539. package/skills/skills/theme-factory/LICENSE.txt +0 -202
  540. package/skills/skills/theme-factory/SKILL.md +0 -59
  541. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  542. package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
  543. package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
  544. package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
  545. package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
  546. package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
  547. package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
  548. package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
  549. package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
  550. package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
  551. package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
  552. package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
  553. package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
  554. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
  555. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
  556. package/skills/skills/webapp-testing/LICENSE.txt +0 -202
  557. package/skills/skills/webapp-testing/SKILL.md +0 -96
  558. package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
  559. package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
  560. package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
  561. package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
  562. package/skills/skills/xlsx/LICENSE.txt +0 -30
  563. package/skills/skills/xlsx/SKILL.md +0 -289
  564. package/skills/skills/xlsx/recalc.py +0 -178
  565. package/skills/spec/agent-skills-spec.md +0 -3
  566. package/skills/template/SKILL.md +0 -6
  567. package/src/ai-client.ts +0 -1560
  568. package/src/remote-ai-client.ts +0 -664
package/dist/tools.js CHANGED
@@ -1,18 +1,21 @@
1
1
  import fs from 'fs/promises';
2
+ import { select, text } from '@clack/prompts';
2
3
  import path from 'path';
3
4
  import { fileURLToPath } from 'url';
4
5
  import readline from 'readline';
5
- import { exec, spawn } from 'child_process';
6
- import { promisify } from 'util';
6
+ import { spawn } from 'child_process';
7
7
  import { glob } from 'glob';
8
8
  import axios from 'axios';
9
- import inquirer from 'inquirer';
10
9
  import { ExecutionMode, AuthType } from './types.js';
11
10
  import { colors, icons } from './theme.js';
12
11
  import { getLogger } from './logger.js';
13
12
  import { getCancellationManager } from './cancellation.js';
14
13
  import { SystemPromptGenerator } from './system-prompt-generator.js';
15
- const execAsync = promisify(exec);
14
+ import { getSingletonSession } from './session.js';
15
+ import { ripgrep, fdFind } from './ripgrep.js';
16
+ import { getShellConfig, killProcessTree, quoteShellCommand } from './shell.js';
17
+ import { truncateTail, buildTruncationNotice } from './truncate.js';
18
+ import { createAIClient } from './ai-client-factory.js';
16
19
  //
17
20
  // Tool Description Pattern
18
21
  //
@@ -64,8 +67,16 @@ export class ReadTool {
64
67
  - Use offset and limit for large files to avoid loading entire content
65
68
  - Combine with ListDirectory to explore project structure first
66
69
  - Don't re-read files unnecessarily`;
67
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
70
+ allowedModes = [
71
+ ExecutionMode.YOLO,
72
+ ExecutionMode.ACCEPT_EDITS,
73
+ ExecutionMode.PLAN,
74
+ ExecutionMode.SMART,
75
+ ];
68
76
  async execute(params) {
77
+ if (!params || typeof params.filePath !== 'string') {
78
+ throw new Error('filePath is required and must be a string');
79
+ }
69
80
  const { filePath, offset = 0, limit } = params;
70
81
  try {
71
82
  // Handle ~ (user home directory) in file paths
@@ -82,10 +93,18 @@ export class ReadTool {
82
93
  const absolutePath = path.resolve(resolvedPath);
83
94
  const content = await fs.readFile(absolutePath, 'utf-8');
84
95
  const lines = content.split('\n');
96
+ const totalLines = lines.length;
85
97
  const startLine = Math.max(0, offset);
86
- const endLine = limit !== undefined ? Math.min(lines.length, startLine + limit) : lines.length;
98
+ const endLine = limit !== undefined ? Math.min(totalLines, startLine + limit) : totalLines;
87
99
  const selectedLines = lines.slice(startLine, endLine);
88
- return selectedLines.join('\n');
100
+ const result = selectedLines.join('\n');
101
+ // Add truncation notice if content is limited
102
+ if (limit !== undefined && endLine < totalLines) {
103
+ const remaining = totalLines - endLine;
104
+ const nextOffset = endLine;
105
+ return (result + `\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`);
106
+ }
107
+ return result;
89
108
  }
90
109
  catch (error) {
91
110
  // Show user-friendly path in error message
@@ -146,7 +165,7 @@ export class WriteTool {
146
165
  message: `Successfully wrote to ${filePath}`,
147
166
  filePath,
148
167
  lineCount,
149
- preview: isTruncated ? preview + '\n...' : preview
168
+ preview: isTruncated ? preview + '\n...' : preview,
150
169
  };
151
170
  }
152
171
  catch (error) {
@@ -156,7 +175,7 @@ export class WriteTool {
156
175
  }
157
176
  export class GrepTool {
158
177
  name = 'Grep';
159
- description = `Search for text patterns within files using regex or literal string matching. This is your PRIMARY tool for finding specific code, functions, or content.
178
+ description = `Search for text patterns within files using ripgrep. This is your PRIMARY tool for finding specific code, functions, or content.
160
179
 
161
180
  # When to Use
162
181
  - Finding specific function definitions or calls
@@ -173,89 +192,41 @@ export class GrepTool {
173
192
  # Parameters
174
193
  - \`pattern\`: Regex or literal string to search for
175
194
  - \`path\`: (Optional) Directory to search in, default: "."
176
- - \`include\`: (Optional) File glob pattern to include
177
- - \`exclude\`: (Optional) File glob pattern to exclude
178
- - \`case_sensitive\`: (Optional) Case-sensitive search, default: false
179
- - \`fixed_strings\`: (Optional) Treat pattern as literal string, default: false
195
+ - \`glob\`: (Optional) File glob pattern to include (e.g., "*.ts", "**/*.js")
196
+ - \`ignoreCase\`: (Optional) Case-insensitive search, default: false
197
+ - \`literal\`: (Optional) Treat pattern as literal string, default: false
180
198
  - \`context\`: (Optional) Lines of context before/after matches
181
- - \`no_ignore\`: (Optional) Don't ignore node_modules/.git, default: false
182
199
 
183
200
  # Examples
184
201
  - Find function: Grep(pattern="function myFunction")
185
202
  - Find with context: Grep(pattern="TODO", context=3)
186
- - TypeScript only: Grep(pattern="interface", include="*.ts")
203
+ - TypeScript only: Grep(pattern="interface", glob="*.ts")
204
+ - Case-insensitive: Grep(pattern="error", ignoreCase=true)
187
205
 
188
206
  # Best Practices
189
- - Use case_sensitive=true for short patterns to reduce false positives
190
- - Use fixed_strings=true if your pattern has special regex characters
207
+ - Use ignoreCase=true for short patterns to reduce false positives
208
+ - Use literal=true if your pattern has special regex characters
191
209
  - Use context to see the surrounding code for each match
192
- - Combine with include/exclude to narrow down file types`;
193
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
210
+ - Combine with glob to narrow down file types`;
211
+ allowedModes = [
212
+ ExecutionMode.YOLO,
213
+ ExecutionMode.ACCEPT_EDITS,
214
+ ExecutionMode.PLAN,
215
+ ExecutionMode.SMART,
216
+ ];
194
217
  async execute(params) {
195
- const { pattern, path: searchPath = '.', include, exclude, case_sensitive = false, fixed_strings = false, context, after, before, no_ignore = false } = params;
218
+ const { pattern, path: searchPath = '.', glob: includeGlob, ignoreCase = false, literal = false, context, limit, } = params;
196
219
  try {
197
- const ignorePatterns = no_ignore ? [] : ['node_modules/**', '.git/**', 'dist/**', 'build/**'];
198
- if (exclude) {
199
- ignorePatterns.push(exclude);
200
- }
201
- const absolutePath = path.resolve(searchPath);
202
- const files = await glob('**/*', {
203
- cwd: absolutePath,
204
- nodir: true,
205
- ignore: ignorePatterns
220
+ const result = await ripgrep({
221
+ pattern,
222
+ path: searchPath,
223
+ glob: includeGlob,
224
+ ignoreCase,
225
+ literal,
226
+ context,
227
+ limit,
206
228
  });
207
- const results = [];
208
- for (const file of files) {
209
- const fullPath = path.join(absolutePath, file);
210
- if (include && !file.match(include)) {
211
- continue;
212
- }
213
- try {
214
- const content = await fs.readFile(fullPath, 'utf-8');
215
- const lines = content.split('\n');
216
- lines.forEach((line, index) => {
217
- let matches = false;
218
- if (fixed_strings) {
219
- matches = case_sensitive
220
- ? line.includes(pattern)
221
- : line.toLowerCase().includes(pattern.toLowerCase());
222
- }
223
- else {
224
- try {
225
- const flags = case_sensitive ? 'g' : 'gi';
226
- const regex = new RegExp(pattern, flags);
227
- matches = regex.test(line);
228
- }
229
- catch (e) {
230
- matches = case_sensitive
231
- ? line.includes(pattern)
232
- : line.toLowerCase().includes(pattern.toLowerCase());
233
- }
234
- }
235
- if (matches) {
236
- const contextLines = [];
237
- if (before || context) {
238
- const beforeCount = before || context || 0;
239
- for (let i = Math.max(0, index - beforeCount); i < index; i++) {
240
- contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
241
- }
242
- }
243
- contextLines.push(`${fullPath}:${index + 1}:${line.trim()}`);
244
- if (after || context) {
245
- const afterCount = after || context || 0;
246
- for (let i = index + 1; i < Math.min(lines.length, index + 1 + afterCount); i++) {
247
- contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
248
- }
249
- }
250
- results.push(...contextLines);
251
- }
252
- });
253
- }
254
- catch (error) {
255
- continue;
256
- }
257
- }
258
- return results;
229
+ return result.split('\n').filter((line) => line.trim());
259
230
  }
260
231
  catch (error) {
261
232
  throw new Error(`Grep failed: ${error.message}`);
@@ -287,13 +258,23 @@ export class BashTool {
287
258
  - \`description\`: (Optional) Description of what the command does
288
259
  - \`timeout\`: (Optional) Timeout in seconds, default: 120
289
260
  - \`run_in_bg\`: (Optional) Run in background, default: false
261
+ - \`skillPath\`: (Optional) Skill directory path - when provided, NODE_PATH will include the skill's node_modules for dependency resolution
290
262
 
291
263
  # Examples
292
264
  - Install dependencies: Bash(command="npm install", description="Install npm dependencies")
265
+ - Run in skill directory with local deps: Bash(command="npm install docx", skillPath="~/.xagent/skills/docx")
266
+
267
+ # NODE_PATH Resolution
268
+ When \`skillPath\` is provided, the command will have access to:
269
+ - \`<skillPath>/node_modules\` (skill's local dependencies)
270
+ - xAgent's global node_modules
271
+
272
+ This is useful when working with skills that have local dependencies.
293
273
  - Run tests: Bash(command="npm test", description="Run unit tests")
294
274
  - Build project: Bash(command="npm run build", description="Build the project")
295
275
 
296
276
  # Best Practices
277
+ - To install npm packages that persist across sessions, use: \`XAGENT_USER_NPM=1 npm install <package>\`
297
278
  - Always provide a description for context
298
279
  - Set appropriate timeout for long-running commands
299
280
  - Use run_in_bg=true for commands that take a long time
@@ -301,7 +282,7 @@ export class BashTool {
301
282
  - Use absolute paths or paths relative to project root`;
302
283
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
303
284
  async execute(params) {
304
- const { command, cwd, description, timeout = 120, run_in_bg = false } = params;
285
+ const { command, cwd, description, timeout = 120, run_in_bg = false, skillPath } = params;
305
286
  // Determine effective working directory
306
287
  // Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
307
288
  let effectiveCwd;
@@ -318,26 +299,104 @@ export class BashTool {
318
299
  // No cwd provided, use default
319
300
  effectiveCwd = undefined;
320
301
  }
321
- // Set up environment with NODE_PATH only for node commands
322
- const nodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
302
+ // Resolve actual working directory
303
+ const actualCwd = effectiveCwd || process.cwd();
304
+ // Set up environment with NODE_PATH for node commands
305
+ const builtinNodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
306
+ // Get user skills path from config (unified path: ~/.xagent/skills)
307
+ const { getConfigManager } = await import('./config.js');
308
+ const configManager = getConfigManager();
309
+ const userSkillsPath = configManager.getUserSkillsPath();
310
+ // Skill deps path: ~/.xagent/skills/{skillName}/node_modules
311
+ const builtinDepsPath = userSkillsPath ? path.join(userSkillsPath, 'builtin-deps') : null;
312
+ // Determine which node_modules to use
313
+ let skillNodeModulesPath = null;
314
+ // Priority 1: skillPath parameter (workspace scenario - LLM works in workspace, not skill dir)
315
+ if (skillPath) {
316
+ if (skillPath.includes('/builtin-deps/')) {
317
+ // Skill with deps in builtin-deps directory
318
+ const match = skillPath.match(/\/builtin-deps\/([^/]+)/);
319
+ if (match) {
320
+ skillNodeModulesPath = path.join(builtinDepsPath, match[1], 'node_modules');
321
+ }
322
+ }
323
+ else {
324
+ // Regular skill
325
+ skillNodeModulesPath = path.join(skillPath, 'node_modules');
326
+ }
327
+ }
328
+ // Priority 2: Check if we're inside a skill directory
329
+ else if (userSkillsPath && userSkillsPath.trim() && actualCwd.startsWith(userSkillsPath)) {
330
+ const relativePath = actualCwd.substring(userSkillsPath.length);
331
+ const pathParts = relativePath.split(path.sep).filter(Boolean);
332
+ if (pathParts.length > 0) {
333
+ if (pathParts[0] === 'builtin-deps' && pathParts.length > 1) {
334
+ // Skill with local deps in builtin-deps
335
+ const skillName = pathParts[1];
336
+ skillNodeModulesPath = path.join(builtinDepsPath, skillName, 'node_modules');
337
+ }
338
+ else {
339
+ // Regular skill
340
+ const skillName = pathParts[0];
341
+ const skillRoot = path.join(userSkillsPath, skillName);
342
+ try {
343
+ const skillMdPath = path.join(skillRoot, 'SKILL.md');
344
+ await fs.access(skillMdPath);
345
+ skillNodeModulesPath = path.join(skillRoot, 'node_modules');
346
+ }
347
+ catch {
348
+ // Not a skill directory, skip
349
+ }
350
+ }
351
+ }
352
+ }
353
+ // Build NODE_PATH - skill's node_modules takes precedence (last-wins)
354
+ let nodePath;
355
+ if (skillNodeModulesPath) {
356
+ nodePath = `${skillNodeModulesPath}${path.delimiter}${builtinNodeModulesPath}`;
357
+ }
358
+ else {
359
+ nodePath = builtinNodeModulesPath;
360
+ }
323
361
  const env = {
324
362
  ...process.env,
325
- NODE_PATH: nodeModulesPath
363
+ NODE_PATH: nodePath
326
364
  };
327
- // Only add NODE_PATH prefix for node commands
328
- const isNodeCommand = /\bnode\b/.test(command);
329
- const finalCommand = isNodeCommand
330
- ? `set NODE_PATH=${nodeModulesPath} && ${command}`
331
- : command;
365
+ // Handle npm install commands
366
+ const isNpmInstall = /\bnpm\s+install\b/i.test(command);
367
+ let finalCommand = command;
368
+ if (isNpmInstall && skillNodeModulesPath) {
369
+ // Install to skill's own node_modules
370
+ await fs.mkdir(skillNodeModulesPath, { recursive: true }).catch(() => { });
371
+ finalCommand = command.replace(/\bnpm\s+install\b/i, `npm install --prefix "${skillNodeModulesPath}"`);
372
+ }
373
+ // Get shell configuration (Windows Git Bash detection, etc.)
374
+ const { shell, args } = getShellConfig();
375
+ // Set up cross-platform encoding environment for command execution
376
+ if (process.platform === 'win32') {
377
+ // Windows: set code page to UTF-8 and ensure console output encoding
378
+ // chcp 65001 sets the console code page to UTF-8
379
+ // Use *>$null to suppress output (PowerShell-style, not CMD-style)
380
+ finalCommand = `chcp 65001 *>$null; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ${finalCommand}`;
381
+ }
382
+ else {
383
+ // Unix/macOS: set locale to UTF-8 for proper encoding handling
384
+ finalCommand = `export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8; ${finalCommand}`;
385
+ }
386
+ const shellArgs = [...args, quoteShellCommand(finalCommand)];
332
387
  try {
333
388
  if (run_in_bg) {
334
389
  const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
335
- const childProcess = spawn(finalCommand, {
390
+ const spawnOptions = {
336
391
  cwd: effectiveCwd || process.cwd(),
337
- shell: true,
338
- detached: true,
339
- env
340
- });
392
+ env,
393
+ stdio: ['pipe', 'pipe', 'pipe'],
394
+ };
395
+ // On Windows, don't use detached mode for PowerShell as it breaks output piping
396
+ if (process.platform !== 'win32') {
397
+ spawnOptions.detached = true;
398
+ }
399
+ const childProcess = spawn(shell, shellArgs, spawnOptions);
341
400
  const output = [];
342
401
  childProcess.stdout?.on('data', (data) => {
343
402
  const text = data.toString();
@@ -348,43 +407,127 @@ export class BashTool {
348
407
  output.push(text);
349
408
  });
350
409
  childProcess.on('close', (code) => {
351
- console.log(`Background task ${taskId} exited with code ${code}`);
410
+ // Silent cleanup - don't log to avoid noise during normal operation
411
+ // Note: On Windows with PowerShell, the shell process exits after
412
+ // the command completes
352
413
  });
353
414
  const toolRegistry = getToolRegistry();
354
415
  toolRegistry.addBackgroundTask(taskId, {
355
416
  process: childProcess,
356
417
  startTime: Date.now(),
357
- output
418
+ output,
358
419
  });
359
420
  return {
360
421
  stdout: '',
361
422
  stderr: '',
362
423
  exitCode: 0,
363
- taskId
424
+ taskId,
364
425
  };
365
426
  }
366
427
  else {
367
- const { stdout, stderr } = await execAsync(finalCommand, {
428
+ // Execute command with spawn for better control
429
+ const result = await this.spawnWithTimeout(shell, shellArgs, {
368
430
  cwd: effectiveCwd || process.cwd(),
369
- maxBuffer: 1024 * 1024 * 10,
370
- timeout: timeout * 1000,
371
- env
431
+ env,
432
+ timeout,
372
433
  });
434
+ // Apply truncation to stdout and stderr separately
435
+ const stdoutResult = truncateTail(result.stdout);
436
+ const stderrResult = truncateTail(result.stderr);
437
+ let stdout = stdoutResult.content;
438
+ let stderr = stderrResult.content;
439
+ let truncationNotice = '';
440
+ if (stdoutResult.truncated) {
441
+ truncationNotice += buildTruncationNotice(stdoutResult) + '\n';
442
+ }
443
+ if (stderrResult.truncated) {
444
+ truncationNotice += buildTruncationNotice(stderrResult) + '\n';
445
+ }
373
446
  return {
374
447
  stdout,
375
448
  stderr,
376
- exitCode: 0
449
+ exitCode: result.exitCode,
450
+ truncated: stdoutResult.truncated || stderrResult.truncated,
451
+ truncationNotice: truncationNotice || undefined,
377
452
  };
378
453
  }
379
454
  }
380
455
  catch (error) {
456
+ // Check if this was a timeout
457
+ if (error.message === 'timeout') {
458
+ return {
459
+ stdout: '',
460
+ stderr: 'Command timed out',
461
+ exitCode: -1,
462
+ truncated: false,
463
+ };
464
+ }
381
465
  return {
382
466
  stdout: error.stdout || '',
383
467
  stderr: error.stderr || error.message,
384
- exitCode: error.code || 1
468
+ exitCode: error.code || 1,
385
469
  };
386
470
  }
387
471
  }
472
+ /**
473
+ * Execute a command with timeout support and proper process termination.
474
+ */
475
+ spawnWithTimeout(shell, args, options) {
476
+ return new Promise((resolve, reject) => {
477
+ const { cwd, env, timeout } = options;
478
+ let timedOut = false;
479
+ let timeoutHandle;
480
+ const spawnOptions = {
481
+ cwd,
482
+ env,
483
+ stdio: ['pipe', 'pipe', 'pipe'],
484
+ };
485
+ // On Windows, don't use detached mode for PowerShell as it breaks output piping
486
+ if (process.platform !== 'win32') {
487
+ spawnOptions.detached = true;
488
+ }
489
+ const child = spawn(shell, args, spawnOptions);
490
+ const stdoutChunks = [];
491
+ const stderrChunks = [];
492
+ // Set timeout if provided
493
+ if (timeout > 0) {
494
+ timeoutHandle = setTimeout(() => {
495
+ timedOut = true;
496
+ if (child.pid) {
497
+ killProcessTree(child.pid);
498
+ }
499
+ }, timeout * 1000);
500
+ }
501
+ // Stream stdout
502
+ child.stdout?.on('data', (data) => {
503
+ stdoutChunks.push(data);
504
+ });
505
+ // Stream stderr
506
+ child.stderr?.on('data', (data) => {
507
+ stderrChunks.push(data);
508
+ });
509
+ // Handle process exit
510
+ child.on('close', (code) => {
511
+ if (timeoutHandle)
512
+ clearTimeout(timeoutHandle);
513
+ if (timedOut) {
514
+ reject(new Error('timeout'));
515
+ return;
516
+ }
517
+ resolve({
518
+ stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
519
+ stderr: Buffer.concat(stderrChunks).toString('utf-8'),
520
+ exitCode: code ?? -1,
521
+ });
522
+ });
523
+ // Handle spawn errors
524
+ child.on('error', (err) => {
525
+ if (timeoutHandle)
526
+ clearTimeout(timeoutHandle);
527
+ reject(err);
528
+ });
529
+ });
530
+ }
388
531
  }
389
532
  export class ListDirectoryTool {
390
533
  name = 'ListDirectory';
@@ -416,7 +559,12 @@ export class ListDirectoryTool {
416
559
  - Results are absolute paths
417
560
  - Ignores node_modules and .git by default
418
561
  - Combine with Read to examine file contents`;
419
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
562
+ allowedModes = [
563
+ ExecutionMode.YOLO,
564
+ ExecutionMode.ACCEPT_EDITS,
565
+ ExecutionMode.PLAN,
566
+ ExecutionMode.SMART,
567
+ ];
420
568
  async execute(params) {
421
569
  const { path: dirPath = '.', recursive = false } = params;
422
570
  try {
@@ -429,9 +577,9 @@ export class ListDirectoryTool {
429
577
  const files = await glob(pattern, {
430
578
  cwd: absolutePath,
431
579
  nodir: false,
432
- ignore: ['node_modules/**', '.git/**']
580
+ ignore: ['node_modules/**', '.git/**'],
433
581
  });
434
- return files.map(file => path.join(absolutePath, file));
582
+ return files.map((file) => path.join(absolutePath, file));
435
583
  }
436
584
  catch (error) {
437
585
  throw new Error(`Failed to list directory: ${error.message}`);
@@ -440,7 +588,7 @@ export class ListDirectoryTool {
440
588
  }
441
589
  export class SearchFilesTool {
442
590
  name = 'SearchFiles';
443
- description = `Search for files matching a glob pattern. This is your PRIMARY tool for finding files by name or extension.
591
+ description = `Search for files matching a glob pattern using fd. This is your PRIMARY tool for finding files by name or extension.
444
592
 
445
593
  # When to Use
446
594
  - Finding all files of a certain type (*.ts, *.json, *.md)
@@ -475,21 +623,35 @@ export class SearchFilesTool {
475
623
  - Combine with path parameter to search specific directories
476
624
  - Use limit parameter to avoid huge result sets
477
625
  - Results are file paths, not content (use Grep on results if needed)`;
478
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
626
+ allowedModes = [
627
+ ExecutionMode.YOLO,
628
+ ExecutionMode.ACCEPT_EDITS,
629
+ ExecutionMode.PLAN,
630
+ ExecutionMode.SMART,
631
+ ];
479
632
  async execute(params) {
480
633
  const { pattern, path: searchPath = '.', limit = 1000 } = params;
481
634
  try {
482
- const files = await glob(pattern, {
483
- cwd: searchPath,
484
- ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
635
+ const output = await fdFind({
636
+ pattern,
637
+ path: searchPath,
638
+ limit,
485
639
  });
640
+ if (output === 'No files found') {
641
+ return {
642
+ files: [],
643
+ total: 0,
644
+ truncated: false,
645
+ };
646
+ }
647
+ const files = output.split('\n').filter((line) => line.trim());
486
648
  const total = files.length;
487
649
  const truncated = total > limit;
488
650
  const result = truncated ? files.slice(0, limit) : files;
489
651
  return {
490
652
  files: result,
491
653
  total,
492
- truncated
654
+ truncated,
493
655
  };
494
656
  }
495
657
  catch (error) {
@@ -532,7 +694,7 @@ export class DeleteFileTool {
532
694
  return {
533
695
  success: true,
534
696
  message: `Successfully deleted ${filePath}`,
535
- filePath
697
+ filePath,
536
698
  };
537
699
  }
538
700
  catch (error) {
@@ -574,7 +736,7 @@ export class CreateDirectoryTool {
574
736
  await fs.mkdir(absolutePath, { recursive });
575
737
  return {
576
738
  success: true,
577
- message: `Successfully created directory ${dirPath}`
739
+ message: `Successfully created directory ${dirPath}`,
578
740
  };
579
741
  }
580
742
  catch (error) {
@@ -584,29 +746,29 @@ export class CreateDirectoryTool {
584
746
  }
585
747
  // 编辑工具辅助函数
586
748
  function detectLineEnding(content) {
587
- const crlfIdx = content.indexOf("\r\n");
588
- const lfIdx = content.indexOf("\n");
749
+ const crlfIdx = content.indexOf('\r\n');
750
+ const lfIdx = content.indexOf('\n');
589
751
  if (lfIdx === -1)
590
- return "\n";
752
+ return '\n';
591
753
  if (crlfIdx === -1)
592
- return "\n";
593
- return crlfIdx < lfIdx ? "\r\n" : "\n";
754
+ return '\n';
755
+ return crlfIdx < lfIdx ? '\r\n' : '\n';
594
756
  }
595
757
  function normalizeToLF(text) {
596
- return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
758
+ return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
597
759
  }
598
760
  function restoreLineEndings(text, ending) {
599
- return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
761
+ return ending === '\r\n' ? text.replace(/\n/g, '\r\n') : text;
600
762
  }
601
763
  function normalizeForFuzzyMatch(text) {
602
- return (text
603
- .split("\n")
764
+ return text
765
+ .split('\n')
604
766
  .map((line) => line.trimEnd())
605
- .join("\n")
767
+ .join('\n')
606
768
  .replace(/['‘’""]/g, "'")
607
769
  .replace(/["""]/g, '"')
608
- .replace(/[—–‑−]/g, "-")
609
- .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " "));
770
+ .replace(/[—–‑−]/g, '-')
771
+ .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, ' ');
610
772
  }
611
773
  function fuzzyFindText(content, oldText) {
612
774
  const exactIndex = content.indexOf(oldText);
@@ -640,14 +802,16 @@ function fuzzyFindText(content, oldText) {
640
802
  };
641
803
  }
642
804
  function stripBom(content) {
643
- return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
805
+ return content.startsWith('\uFEFF')
806
+ ? { bom: '\uFEFF', text: content.slice(1) }
807
+ : { bom: '', text: content };
644
808
  }
645
809
  async function generateDiffString(oldContent, newContent, contextLines = 4) {
646
- const diffModule = await import("diff");
810
+ const diffModule = await import('diff');
647
811
  const parts = diffModule.diffLines(oldContent, newContent);
648
812
  const output = [];
649
- const oldLines = oldContent.split("\n");
650
- const newLines = newContent.split("\n");
813
+ const oldLines = oldContent.split('\n');
814
+ const newLines = newContent.split('\n');
651
815
  const maxLineNum = Math.max(oldLines.length, newLines.length);
652
816
  const lineNumWidth = String(maxLineNum).length;
653
817
  let oldLineNum = 1;
@@ -656,8 +820,8 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
656
820
  let firstChangedLine;
657
821
  for (let i = 0; i < parts.length; i++) {
658
822
  const part = parts[i];
659
- const raw = part.value.split("\n");
660
- if (raw[raw.length - 1] === "") {
823
+ const raw = part.value.split('\n');
824
+ if (raw[raw.length - 1] === '') {
661
825
  raw.pop();
662
826
  }
663
827
  if (part.added || part.removed) {
@@ -666,12 +830,12 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
666
830
  }
667
831
  for (const line of raw) {
668
832
  if (part.added) {
669
- const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
833
+ const lineNum = String(newLineNum).padStart(lineNumWidth, ' ');
670
834
  output.push(`+${lineNum} ${line}`);
671
835
  newLineNum++;
672
836
  }
673
837
  else {
674
- const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
838
+ const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
675
839
  output.push(`-${lineNum} ${line}`);
676
840
  oldLineNum++;
677
841
  }
@@ -693,18 +857,18 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
693
857
  linesToShow = linesToShow.slice(0, contextLines);
694
858
  }
695
859
  if (skipStart > 0) {
696
- output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
860
+ output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
697
861
  oldLineNum += skipStart;
698
862
  newLineNum += skipStart;
699
863
  }
700
864
  for (const line of linesToShow) {
701
- const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
865
+ const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
702
866
  output.push(` ${lineNum} ${line}`);
703
867
  oldLineNum++;
704
868
  newLineNum++;
705
869
  }
706
870
  if (skipEnd > 0) {
707
- output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
871
+ output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
708
872
  }
709
873
  }
710
874
  else {
@@ -714,7 +878,7 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
714
878
  lastWasChange = false;
715
879
  }
716
880
  }
717
- return { diff: output.join("\n"), firstChangedLine };
881
+ return { diff: output.join('\n'), firstChangedLine };
718
882
  }
719
883
  export class EditTool {
720
884
  name = 'Edit';
@@ -779,7 +943,7 @@ edit(
779
943
  }
780
944
  // Read the file
781
945
  const buffer = await fs.readFile(absolutePath);
782
- const rawContent = buffer.toString("utf-8");
946
+ const rawContent = buffer.toString('utf-8');
783
947
  // Strip BOM before matching
784
948
  const { bom, text: content } = stripBom(rawContent);
785
949
  const originalEnding = detectLineEnding(content);
@@ -817,7 +981,7 @@ edit(
817
981
  };
818
982
  }
819
983
  const finalContent = bom + restoreLineEndings(newContent, originalEnding);
820
- await fs.writeFile(absolutePath, finalContent, "utf-8");
984
+ await fs.writeFile(absolutePath, finalContent, 'utf-8');
821
985
  const diffResult = await generateDiffString(baseContent, newContent);
822
986
  return {
823
987
  success: true,
@@ -863,7 +1027,12 @@ export class WebSearchTool {
863
1027
  - Combine with web_fetch to get full content from relevant URLs
864
1028
  - Use quotes for exact phrase matching
865
1029
  - Consider adding context like year or version in query`;
866
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1030
+ allowedModes = [
1031
+ ExecutionMode.YOLO,
1032
+ ExecutionMode.ACCEPT_EDITS,
1033
+ ExecutionMode.PLAN,
1034
+ ExecutionMode.SMART,
1035
+ ];
867
1036
  async execute(params) {
868
1037
  const { query } = params;
869
1038
  try {
@@ -877,14 +1046,14 @@ export class WebSearchTool {
877
1046
  }
878
1047
  const response = await axios.post(`${baseUrl}/search`, { query }, {
879
1048
  headers: {
880
- 'Authorization': `Bearer ${searchApiKey}`,
881
- 'Content-Type': 'application/json'
1049
+ Authorization: `Bearer ${searchApiKey}`,
1050
+ 'Content-Type': 'application/json',
882
1051
  },
883
- timeout: 30000
1052
+ timeout: 30000,
884
1053
  });
885
1054
  return {
886
1055
  results: response.data.results || [],
887
- message: `Found ${response.data.results?.length || 0} results for "${query}"`
1056
+ message: `Found ${response.data.results?.length || 0} results for "${query}"`,
888
1057
  };
889
1058
  }
890
1059
  catch (error) {
@@ -940,22 +1109,27 @@ Each task needs:
940
1109
  - Don't batch multiple completions - update as you go
941
1110
  - Keep task descriptions clear and actionable
942
1111
  - Use appropriate priority levels to indicate urgency`;
943
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1112
+ allowedModes = [
1113
+ ExecutionMode.YOLO,
1114
+ ExecutionMode.ACCEPT_EDITS,
1115
+ ExecutionMode.PLAN,
1116
+ ExecutionMode.SMART,
1117
+ ];
944
1118
  todoList = [];
945
1119
  async execute(params) {
946
1120
  const { todos } = params;
947
1121
  try {
948
1122
  this.todoList = todos;
949
1123
  const summary = {
950
- pending: todos.filter(t => t.status === 'pending').length,
951
- in_progress: todos.filter(t => t.status === 'in_progress').length,
952
- completed: todos.filter(t => t.status === 'completed').length,
953
- failed: todos.filter(t => t.status === 'failed').length
1124
+ pending: todos.filter((t) => t.status === 'pending').length,
1125
+ in_progress: todos.filter((t) => t.status === 'in_progress').length,
1126
+ completed: todos.filter((t) => t.status === 'completed').length,
1127
+ failed: todos.filter((t) => t.status === 'failed').length,
954
1128
  };
955
1129
  return {
956
1130
  success: true,
957
1131
  message: `Updated todo list: ${summary.pending} pending, ${summary.in_progress} in progress, ${summary.completed} completed, ${summary.failed} failed`,
958
- todos: this.todoList
1132
+ todos: this.todoList,
959
1133
  };
960
1134
  }
961
1135
  catch (error) {
@@ -987,7 +1161,12 @@ export class TodoReadTool {
987
1161
  # Best Practices
988
1162
  - Use todo_write to modify the list, not todo_read
989
1163
  - Check todo_read after todo_write to verify updates`;
990
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1164
+ allowedModes = [
1165
+ ExecutionMode.YOLO,
1166
+ ExecutionMode.ACCEPT_EDITS,
1167
+ ExecutionMode.PLAN,
1168
+ ExecutionMode.SMART,
1169
+ ];
991
1170
  todoWriteTool;
992
1171
  constructor(todoWriteTool) {
993
1172
  this.todoWriteTool = todoWriteTool;
@@ -997,14 +1176,14 @@ export class TodoReadTool {
997
1176
  const todos = this.todoWriteTool.getTodos();
998
1177
  const summary = {
999
1178
  total: todos.length,
1000
- pending: todos.filter(t => t.status === 'pending').length,
1001
- in_progress: todos.filter(t => t.status === 'in_progress').length,
1002
- completed: todos.filter(t => t.status === 'completed').length,
1003
- failed: todos.filter(t => t.status === 'failed').length
1179
+ pending: todos.filter((t) => t.status === 'pending').length,
1180
+ in_progress: todos.filter((t) => t.status === 'in_progress').length,
1181
+ completed: todos.filter((t) => t.status === 'completed').length,
1182
+ failed: todos.filter((t) => t.status === 'failed').length,
1004
1183
  };
1005
1184
  return {
1006
1185
  todos,
1007
- summary
1186
+ summary,
1008
1187
  };
1009
1188
  }
1010
1189
  catch (error) {
@@ -1051,7 +1230,12 @@ export class TaskTool {
1051
1230
  - Include relevant context (file paths, requirements, constraints)
1052
1231
  - Set appropriate executionMode if needed
1053
1232
  - For parallel execution, ensure tasks are truly independent`;
1054
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1233
+ allowedModes = [
1234
+ ExecutionMode.YOLO,
1235
+ ExecutionMode.ACCEPT_EDITS,
1236
+ ExecutionMode.PLAN,
1237
+ ExecutionMode.SMART,
1238
+ ];
1055
1239
  async execute(params, _executionMode) {
1056
1240
  const mode = params.executionMode || _executionMode || ExecutionMode.YOLO;
1057
1241
  try {
@@ -1059,13 +1243,8 @@ export class TaskTool {
1059
1243
  const agentManager = getAgentManager(process.cwd());
1060
1244
  const { getConfigManager } = await import('./config.js');
1061
1245
  const config = getConfigManager();
1062
- const { AIClient } = await import('./ai-client.js');
1063
- const aiClient = new AIClient({
1064
- type: AuthType.OPENAI_COMPATIBLE,
1065
- apiKey: config.get('apiKey'),
1066
- baseUrl: config.get('baseUrl'),
1067
- modelName: config.get('modelName') || 'Qwen3-Coder'
1068
- });
1246
+ const authConfig = config.getAuthConfig();
1247
+ const aiClient = createAIClient(authConfig);
1069
1248
  const toolRegistry = getToolRegistry();
1070
1249
  if (params.agents && params.agents.length > 0) {
1071
1250
  return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry, aiClient);
@@ -1076,13 +1255,14 @@ export class TaskTool {
1076
1255
  // Support both 'prompt' and 'query' parameter names (tool definition uses 'query')
1077
1256
  const prompt = params.prompt || params.query;
1078
1257
  if (!prompt) {
1079
- throw new Error('Task query/prompt is required. Received params: ' + JSON.stringify({
1080
- subagent_type: params.subagent_type,
1081
- prompt: params.prompt,
1082
- query: params.query,
1083
- description: params.description,
1084
- agents: params.agents?.length
1085
- }));
1258
+ throw new Error('Task query/prompt is required. Received params: ' +
1259
+ JSON.stringify({
1260
+ subagent_type: params.subagent_type,
1261
+ prompt: params.prompt,
1262
+ query: params.query,
1263
+ description: params.description,
1264
+ agents: params.agents?.length,
1265
+ }));
1086
1266
  }
1087
1267
  const result = await this.executeSingleAgent(params.subagent_type, prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry, aiClient, config);
1088
1268
  return result;
@@ -1095,11 +1275,16 @@ export class TaskTool {
1095
1275
  * Create unified VLM caller
1096
1276
  * Uses remote VLM if remoteAIClient is provided, otherwise uses local VLM
1097
1277
  * Both modes receive full messages array for consistent behavior
1278
+ * @param remoteAIClient - Remote AI client for VLM calls
1279
+ * @param taskId - Task identifier for backend tracking
1280
+ * @param localConfig - Local VLM configuration
1281
+ * @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
1282
+ * @param signal - Abort signal for cancellation
1098
1283
  */
1099
- createRemoteVlmCaller(remoteAIClient, taskId, localConfig, signal) {
1284
+ createRemoteVlmCaller(remoteAIClient, taskId, localConfig, isFirstVlmCallRef, signal) {
1100
1285
  // Remote mode: use RemoteAIClient
1101
1286
  if (remoteAIClient) {
1102
- return this.createRemoteVLMCaller(remoteAIClient, taskId, signal);
1287
+ return this.createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal);
1103
1288
  }
1104
1289
  // Local mode: use local API
1105
1290
  return this.createLocalVLMCaller(localConfig, signal);
@@ -1107,11 +1292,24 @@ export class TaskTool {
1107
1292
  /**
1108
1293
  * Create remote VLM caller using RemoteAIClient
1109
1294
  * Now receives full messages array for consistent behavior with local mode
1295
+ * @param remoteAIClient - Remote AI client
1296
+ * @param taskId - Task identifier for backend tracking
1297
+ * @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
1298
+ * @param signal - Abort signal for cancellation
1110
1299
  */
1111
- createRemoteVLMCaller(remoteAIClient, taskId, signal) {
1112
- return async (messages, systemPrompt) => {
1300
+ createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal) {
1301
+ return async (messages, systemPrompt, _taskId, _isFirstVlmCallRef) => {
1113
1302
  try {
1114
- return await remoteAIClient.invokeVLM(messages, systemPrompt, { signal, taskId });
1303
+ // Use the ref to track first call status for the backend
1304
+ const status = isFirstVlmCallRef.current ? 'begin' : 'continue';
1305
+ const result = await remoteAIClient.invokeVLM(messages, systemPrompt, {
1306
+ signal,
1307
+ taskId,
1308
+ status,
1309
+ });
1310
+ // Update ref after call so subsequent calls use 'continue'
1311
+ isFirstVlmCallRef.current = false;
1312
+ return result;
1115
1313
  }
1116
1314
  catch (error) {
1117
1315
  throw new Error(`Remote VLM call failed: ${error.message}`);
@@ -1141,7 +1339,7 @@ export class TaskTool {
1141
1339
  method: 'POST',
1142
1340
  headers: {
1143
1341
  'Content-Type': 'application/json',
1144
- 'Authorization': `Bearer ${apiKey}`,
1342
+ Authorization: `Bearer ${apiKey}`,
1145
1343
  },
1146
1344
  body: JSON.stringify(requestBody),
1147
1345
  signal: abortSignal,
@@ -1150,7 +1348,7 @@ export class TaskTool {
1150
1348
  const errorText = await response.text();
1151
1349
  throw new Error(`VLM API error: ${errorText}`);
1152
1350
  }
1153
- const result = await response.json();
1351
+ const result = (await response.json());
1154
1352
  return result.choices?.[0]?.message?.content || '';
1155
1353
  };
1156
1354
  }
@@ -1163,10 +1361,11 @@ export class TaskTool {
1163
1361
  console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
1164
1362
  console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
1165
1363
  console.log('');
1166
- // Get VLM configuration (used for local mode fallback)
1167
- const baseUrl = config.get('guiSubagentBaseUrl') || config.get('baseUrl') || '';
1168
- const apiKey = config.get('guiSubagentApiKey') || config.get('apiKey') || '';
1169
- const modelName = config.get('guiSubagentModel') || config.get('modelName') || '';
1364
+ // Get VLM configuration for local mode
1365
+ // NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
1366
+ const baseUrl = config.get('guiSubagentBaseUrl') || '';
1367
+ const apiKey = config.get('guiSubagentApiKey') || '';
1368
+ const modelName = config.get('guiSubagentModel') || '';
1170
1369
  // Determine mode: remote if remoteAIClient exists, otherwise local
1171
1370
  const isRemoteMode = !!remoteAIClient;
1172
1371
  // Log mode information
@@ -1175,11 +1374,11 @@ export class TaskTool {
1175
1374
  }
1176
1375
  else {
1177
1376
  console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
1178
- // Local mode requires configuration check
1179
- if (!baseUrl) {
1377
+ // Local mode requires explicit VLM configuration
1378
+ if (!baseUrl || !apiKey || !modelName) {
1180
1379
  return {
1181
1380
  success: false,
1182
- message: `GUI task "${description}" failed: No valid API URL configured`
1381
+ message: `GUI task "${description}" failed: VLM not configured. Please run /model to configure Vision-Language Model first.`,
1183
1382
  };
1184
1383
  }
1185
1384
  console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
@@ -1198,23 +1397,27 @@ export class TaskTool {
1198
1397
  taskId = null;
1199
1398
  }
1200
1399
  }
1400
+ // Track first VLM call for proper status management
1401
+ const isFirstVlmCallRef = { current: true };
1201
1402
  // Create remoteVlmCaller using the unified method (handles both local and remote modes)
1202
- const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName });
1403
+ const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName }, isFirstVlmCallRef);
1203
1404
  // Set up stdin polling for ESC cancellation
1204
1405
  let rawModeEnabled = false;
1205
1406
  let stdinPollingInterval = null;
1206
1407
  const cancellationManager = getCancellationManager();
1207
1408
  const logger = getLogger();
1208
1409
  const setupStdinPolling = () => {
1410
+ logger.debug(`[GUIAgent ESC] setupStdinPolling called, process.stdin.isTTY: ${process.stdin.isTTY}`);
1209
1411
  if (process.stdin.isTTY) {
1210
1412
  try {
1211
1413
  process.stdin.setRawMode(true);
1212
1414
  rawModeEnabled = true;
1213
1415
  process.stdin.resume();
1214
1416
  readline.emitKeypressEvents(process.stdin);
1417
+ logger.debug(`[GUIAgent ESC] Raw mode enabled successfully`);
1215
1418
  }
1216
1419
  catch (e) {
1217
- logger.debug(`[GUIAgent] Could not set raw mode: ${e}`);
1420
+ logger.debug(`[GUIAgent ESC] Could not set raw mode: ${e.message}`);
1218
1421
  }
1219
1422
  stdinPollingInterval = setInterval(() => {
1220
1423
  try {
@@ -1222,14 +1425,23 @@ export class TaskTool {
1222
1425
  const chunk = process.stdin.read(1);
1223
1426
  if (chunk && chunk.length > 0) {
1224
1427
  const code = chunk[0];
1225
- if (code === 0x1B) { // ESC
1226
- logger.debug('[GUIAgent] ESC detected!');
1428
+ if (code === 0x1b) {
1429
+ // ESC
1430
+ logger.debug('[GUIAgent ESC Polling] ESC detected! Code: 0x1b');
1227
1431
  cancellationManager.cancel();
1228
1432
  }
1433
+ else {
1434
+ // Log other key codes for debugging
1435
+ logger.debug(`[GUIAgent ESC Polling] Key code: 0x${code.toString(16)}`);
1436
+ }
1229
1437
  }
1230
1438
  }
1439
+ else {
1440
+ logger.debug('[GUIAgent ESC Polling] rawModeEnabled is false');
1441
+ }
1231
1442
  }
1232
1443
  catch (e) {
1444
+ logger.debug(`[GUIAgent ESC Polling] Error: ${e.message}`);
1233
1445
  // Ignore polling errors
1234
1446
  }
1235
1447
  }, 10);
@@ -1248,7 +1460,9 @@ export class TaskTool {
1248
1460
  };
1249
1461
  cancellationManager.on('cancelled', cancelHandler);
1250
1462
  // Start polling for ESC
1463
+ logger.debug(`[GUIAgent ESC] About to call setupStdinPolling`);
1251
1464
  setupStdinPolling();
1465
+ logger.debug(`[GUIAgent ESC] setupStdinPolling called`);
1252
1466
  try {
1253
1467
  // Import and create GUIAgent
1254
1468
  const { createGUISubAgent } = await import('./gui-subagent/index.js');
@@ -1256,11 +1470,14 @@ export class TaskTool {
1256
1470
  model: !isRemoteMode ? modelName : undefined,
1257
1471
  modelBaseUrl: !isRemoteMode ? baseUrl : undefined,
1258
1472
  modelApiKey: !isRemoteMode ? apiKey : undefined,
1473
+ taskId: taskId || undefined,
1474
+ isFirstVlmCallRef,
1259
1475
  remoteVlmCaller,
1260
1476
  isLocalMode: !isRemoteMode,
1261
- maxLoopCount: 30,
1477
+ maxLoopCount: 100,
1262
1478
  loopIntervalInMs: 500,
1263
1479
  showAIDebugInfo: config.get('showAIDebugInfo') || false,
1480
+ indentLevel: indentLevel,
1264
1481
  });
1265
1482
  // Add constraints to prompt if any
1266
1483
  const fullPrompt = prompt;
@@ -1278,7 +1495,7 @@ export class TaskTool {
1278
1495
  success: true,
1279
1496
  cancelled: true, // Mark as cancelled so main agent won't continue
1280
1497
  message: `GUI task "${description}" cancelled by user`,
1281
- result: 'Task cancelled'
1498
+ result: 'Task cancelled',
1282
1499
  };
1283
1500
  }
1284
1501
  cleanupStdinPolling();
@@ -1286,20 +1503,58 @@ export class TaskTool {
1286
1503
  // Flush stdout to ensure all output is displayed before returning
1287
1504
  process.stdout.write('\n');
1288
1505
  // Return result based on GUIAgent status
1506
+ // Always return all info except screenshots (base64) to avoid huge payload
1507
+ const conversationsWithoutScreenshots = result.conversations.map((conv) => ({
1508
+ ...conv,
1509
+ screenshotBase64: undefined, // Remove screenshots to avoid huge payload
1510
+ }));
1289
1511
  if (result.status === 'end') {
1290
- const iterations = result.conversations.filter(c => c.from === 'human' && c.screenshotBase64).length;
1512
+ const iterations = conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length;
1291
1513
  console.log(`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`);
1292
1514
  return {
1293
1515
  success: true,
1294
1516
  message: `GUI task "${description}" completed`,
1295
- result: `Completed in ${iterations} iterations`
1517
+ result: {
1518
+ status: result.status,
1519
+ iterations,
1520
+ actions: conversationsWithoutScreenshots
1521
+ .filter((c) => c.from === 'assistant' && c.actionType)
1522
+ .map((c) => c.actionType),
1523
+ conversations: conversationsWithoutScreenshots,
1524
+ error: result.error,
1525
+ },
1526
+ };
1527
+ }
1528
+ else if (result.status === 'call_llm') {
1529
+ // Empty action or needs LLM decision - return to main agent with full context
1530
+ console.log(`${indent}${colors.warning(`${icons.warning} GUI agent returned to main agent for LLM decision`)}`);
1531
+ return {
1532
+ success: true,
1533
+ message: `GUI task "${description}" returned for LLM decision`,
1534
+ result: {
1535
+ status: result.status,
1536
+ iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
1537
+ actions: conversationsWithoutScreenshots
1538
+ .filter((c) => c.from === 'assistant' && c.actionType)
1539
+ .map((c) => c.actionType),
1540
+ conversations: conversationsWithoutScreenshots,
1541
+ error: result.error,
1542
+ },
1296
1543
  };
1297
1544
  }
1298
1545
  else if (result.status === 'user_stopped') {
1299
1546
  return {
1300
1547
  success: true,
1301
1548
  message: `GUI task "${description}" stopped by user`,
1302
- result: 'User stopped'
1549
+ result: {
1550
+ status: result.status,
1551
+ iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
1552
+ actions: conversationsWithoutScreenshots
1553
+ .filter((c) => c.from === 'assistant' && c.actionType)
1554
+ .map((c) => c.actionType),
1555
+ conversations: conversationsWithoutScreenshots,
1556
+ stopped: true,
1557
+ },
1303
1558
  };
1304
1559
  }
1305
1560
  else {
@@ -1307,7 +1562,16 @@ export class TaskTool {
1307
1562
  const errorMsg = result.error || 'Unknown error';
1308
1563
  return {
1309
1564
  success: false,
1310
- message: `GUI task "${description}" failed: ${errorMsg}`
1565
+ message: `GUI task "${description}" failed: ${errorMsg}`,
1566
+ result: {
1567
+ status: result.status,
1568
+ iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
1569
+ actions: conversationsWithoutScreenshots
1570
+ .filter((c) => c.from === 'assistant' && c.actionType)
1571
+ .map((c) => c.actionType),
1572
+ conversations: conversationsWithoutScreenshots,
1573
+ error: result.error,
1574
+ },
1311
1575
  };
1312
1576
  }
1313
1577
  }
@@ -1323,20 +1587,20 @@ export class TaskTool {
1323
1587
  success: true,
1324
1588
  cancelled: true, // Mark as cancelled so main agent won't continue
1325
1589
  message: `GUI task "${description}" cancelled by user`,
1326
- result: 'Task cancelled'
1590
+ result: 'Task cancelled',
1327
1591
  };
1328
1592
  }
1329
1593
  if (error.message === 'Operation cancelled by user') {
1330
1594
  return {
1331
1595
  success: true,
1332
1596
  message: `GUI task "${description}" cancelled by user`,
1333
- result: 'Task cancelled'
1597
+ result: 'Task cancelled',
1334
1598
  };
1335
1599
  }
1336
1600
  // Return failure without throwing - let the main agent handle it
1337
1601
  return {
1338
1602
  success: false,
1339
- message: `GUI task "${description}" failed: ${error.message}`
1603
+ message: `GUI task "${description}" failed: ${error.message}`,
1340
1604
  };
1341
1605
  }
1342
1606
  }
@@ -1385,15 +1649,37 @@ export class TaskTool {
1385
1649
  modelName = agent.model;
1386
1650
  }
1387
1651
  }
1388
- // Create a new AIClient for this subagent with its specific model
1389
- const { AIClient: SubAgentAIClient } = await import('./ai-client.js');
1390
- const subAgentClient = new SubAgentAIClient({
1391
- type: AuthType.OPENAI_COMPATIBLE,
1392
- apiKey: apiKey,
1393
- baseUrl: baseUrl,
1394
- modelName: modelName,
1395
- showAIDebugInfo: config.get('showAIDebugInfo') || false
1396
- });
1652
+ // Create AI client for this subagent
1653
+ let subAgentClient;
1654
+ let isRemoteMode = false;
1655
+ let mainTaskId = null;
1656
+ const authConfig = config.getAuthConfig();
1657
+ if (authConfig.type === AuthType.OAUTH_XAGENT) {
1658
+ // Remote mode: try to reuse session's RemoteAIClient first
1659
+ const session = getSingletonSession();
1660
+ const existingClient = session?.getRemoteAIClient();
1661
+ if (existingClient) {
1662
+ subAgentClient = existingClient;
1663
+ isRemoteMode = true;
1664
+ // Get the main taskId from session - subagent shares the same taskId as the parent task
1665
+ mainTaskId = session?.getTaskId() || null;
1666
+ }
1667
+ else {
1668
+ subAgentClient = createAIClient(authConfig);
1669
+ }
1670
+ }
1671
+ else {
1672
+ // Local mode: create client with subagent-specific model config
1673
+ const subAuthConfig = {
1674
+ ...authConfig,
1675
+ type: AuthType.OPENAI_COMPATIBLE,
1676
+ apiKey: apiKey,
1677
+ baseUrl: baseUrl,
1678
+ modelName: modelName,
1679
+ showAIDebugInfo: config.get('showAIDebugInfo') || false,
1680
+ };
1681
+ subAgentClient = createAIClient(subAuthConfig);
1682
+ }
1397
1683
  const indent = ' '.repeat(indentLevel);
1398
1684
  const indentNext = ' '.repeat(indentLevel + 1);
1399
1685
  const agentName = agent.name || subagent_type;
@@ -1401,12 +1687,15 @@ export class TaskTool {
1401
1687
  const executionHistory = [];
1402
1688
  // Helper function to indent multi-line content
1403
1689
  const indentMultiline = (content, baseIndent) => {
1404
- return content.split('\n').map(line => `${baseIndent} ${line}`).join('\n');
1690
+ return content
1691
+ .split('\n')
1692
+ .map((line) => `${baseIndent} ${line}`)
1693
+ .join('\n');
1405
1694
  };
1406
1695
  const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, mode, agent);
1407
1696
  const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(agent.systemPrompt);
1408
1697
  const fullPrompt = constraints.length > 0
1409
- ? `${prompt}\n\nConstraints:\n${constraints.map(c => `- ${c}`).join('\n')}`
1698
+ ? `${prompt}\n\nConstraints:\n${constraints.map((c) => `- ${c}`).join('\n')}`
1410
1699
  : prompt;
1411
1700
  // Set up raw mode and stdin polling for ESC detection
1412
1701
  const cancellationManager = getCancellationManager();
@@ -1432,7 +1721,8 @@ export class TaskTool {
1432
1721
  const chunk = process.stdin.read(1);
1433
1722
  if (chunk && chunk.length > 0) {
1434
1723
  const code = chunk[0];
1435
- if (code === 0x1B) { // ESC
1724
+ if (code === 0x1b) {
1725
+ // ESC
1436
1726
  logger.debug('[TaskTool] ESC detected via polling!');
1437
1727
  cancellationManager.cancel();
1438
1728
  }
@@ -1468,7 +1758,7 @@ export class TaskTool {
1468
1758
  };
1469
1759
  let messages = [
1470
1760
  { role: 'system', content: enhancedSystemPrompt },
1471
- { role: 'user', content: fullPrompt }
1761
+ { role: 'user', content: fullPrompt },
1472
1762
  ];
1473
1763
  const availableTools = agentManager.getAvailableToolsForAgent(agent, mode);
1474
1764
  const allToolDefinitions = toolRegistry.getToolDefinitions();
@@ -1482,8 +1772,8 @@ export class TaskTool {
1482
1772
  function: {
1483
1773
  name: toolName,
1484
1774
  description: `Tool: ${toolName}`,
1485
- parameters: { type: 'object', properties: {}, required: [] }
1486
- }
1775
+ parameters: { type: 'object', properties: {}, required: [] },
1776
+ },
1487
1777
  };
1488
1778
  });
1489
1779
  let iteration = 0;
@@ -1492,11 +1782,22 @@ export class TaskTool {
1492
1782
  iteration++;
1493
1783
  // Check for cancellation before each iteration
1494
1784
  checkCancellation();
1495
- // Use withCancellation to make API call cancellable
1496
- const result = await cancellationManager.withCancellation(subAgentClient.chatCompletion(messages, {
1785
+ // Prepare chat options with taskId and model names for remote mode
1786
+ const chatOptions = {
1497
1787
  tools: toolDefinitions,
1498
- temperature: 0.7
1499
- }), `api-${subagent_type}-${iteration}`);
1788
+ temperature: 0.7,
1789
+ };
1790
+ // Pass taskId, status, and model names for remote mode subagent calls
1791
+ // Subagent shares the same taskId as the main task
1792
+ if (isRemoteMode && mainTaskId) {
1793
+ chatOptions.taskId = mainTaskId;
1794
+ chatOptions.status = iteration === 1 ? 'begin' : 'continue';
1795
+ // Pass model names to ensure subagent uses the same models as main task
1796
+ chatOptions.llmModelName = config.get('remote_llmModelName');
1797
+ chatOptions.vlmModelName = config.get('remote_vlmModelName');
1798
+ }
1799
+ // Use withCancellation to make API call cancellable
1800
+ const result = (await cancellationManager.withCancellation(subAgentClient.chatCompletion(messages, chatOptions), `api-${subagent_type}-${iteration}`));
1500
1801
  // Check for cancellation after API call
1501
1802
  checkCancellation();
1502
1803
  if (!result || !result.choices || result.choices.length === 0) {
@@ -1514,8 +1815,8 @@ export class TaskTool {
1514
1815
  }
1515
1816
  else if (Array.isArray(messageContent)) {
1516
1817
  const textParts = messageContent
1517
- .filter(item => typeof item?.text === 'string' && item.text.trim() !== '')
1518
- .map(item => item.text);
1818
+ .filter((item) => typeof item?.text === 'string' && item.text.trim() !== '')
1819
+ .map((item) => item.text);
1519
1820
  contentStr = textParts.join('');
1520
1821
  hasValidContent = textParts.length > 0;
1521
1822
  }
@@ -1531,12 +1832,21 @@ export class TaskTool {
1531
1832
  if (choice.finish_reason === 'length') {
1532
1833
  throw new Error(`Sub-agent ${subagent_type} response truncated due to length limits`);
1533
1834
  }
1534
- // Add assistant message to conversation
1535
- messages.push({ role: 'assistant', content: contentStr });
1835
+ // Add assistant message to conversation (必须包含 tool_calls,否则 tool_result 无法匹配)
1836
+ const assistantMessage = { role: 'assistant', content: contentStr };
1837
+ if (toolCalls && toolCalls.length > 0) {
1838
+ assistantMessage.tool_calls = toolCalls;
1839
+ }
1840
+ if (reasoningContent) {
1841
+ assistantMessage.reasoning_content = reasoningContent;
1842
+ }
1843
+ messages.push(assistantMessage);
1536
1844
  // Display reasoning content if present
1537
1845
  if (reasoningContent) {
1538
1846
  console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
1539
- const truncatedReasoning = reasoningContent.length > 500 ? reasoningContent.substring(0, 500) + '...' : reasoningContent;
1847
+ const truncatedReasoning = reasoningContent.length > 500
1848
+ ? reasoningContent.substring(0, 500) + '...'
1849
+ : reasoningContent;
1540
1850
  const indentedReasoning = indentMultiline(truncatedReasoning, indent);
1541
1851
  console.log(`${indentedReasoning}\n`);
1542
1852
  }
@@ -1587,10 +1897,10 @@ export class TaskTool {
1587
1897
  }
1588
1898
  else {
1589
1899
  const statusConfig = {
1590
- 'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1591
- 'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1592
- 'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
1593
- 'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
1900
+ pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1901
+ in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1902
+ completed: { icon: icons.success, color: colors.success, label: 'Completed' },
1903
+ failed: { icon: icons.error, color: colors.error, label: 'Failed' },
1594
1904
  };
1595
1905
  for (const todo of todos) {
1596
1906
  const status = statusConfig[todo.status] || statusConfig['pending'];
@@ -1606,7 +1916,10 @@ export class TaskTool {
1606
1916
  // Display edit result with diff
1607
1917
  console.log('');
1608
1918
  const diffOutput = renderDiff(toolResult.diff);
1609
- const indentedDiff = diffOutput.split('\n').map(line => `${indent} ${line}`).join('\n');
1919
+ const indentedDiff = diffOutput
1920
+ .split('\n')
1921
+ .map((line) => `${indent} ${line}`)
1922
+ .join('\n');
1610
1923
  console.log(`${indentedDiff}\n`);
1611
1924
  }
1612
1925
  else if (hasFilePreview) {
@@ -1647,12 +1960,12 @@ export class TaskTool {
1647
1960
  status: 'success',
1648
1961
  params: parsedParams,
1649
1962
  result: truncatedPreview,
1650
- timestamp: new Date().toISOString()
1963
+ timestamp: new Date().toISOString(),
1651
1964
  });
1652
1965
  messages.push({
1653
1966
  role: 'tool',
1654
1967
  content: JSON.stringify(toolResult),
1655
- tool_call_id: toolCall.id
1968
+ tool_call_id: toolCall.id,
1656
1969
  });
1657
1970
  }
1658
1971
  catch (error) {
@@ -1669,12 +1982,12 @@ export class TaskTool {
1669
1982
  executionHistory: {
1670
1983
  totalIterations: iteration,
1671
1984
  toolsExecuted: executionHistory.length,
1672
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1673
- failedTools: executionHistory.filter(t => t.status === 'error').length,
1985
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
1986
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
1674
1987
  history: executionHistory,
1675
- cancelled: true
1676
- }
1677
- }
1988
+ cancelled: true,
1989
+ },
1990
+ },
1678
1991
  };
1679
1992
  }
1680
1993
  console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error.message}\n`);
@@ -1684,12 +1997,12 @@ export class TaskTool {
1684
1997
  status: 'error',
1685
1998
  params: parsedParams,
1686
1999
  error: error.message,
1687
- timestamp: new Date().toISOString()
2000
+ timestamp: new Date().toISOString(),
1688
2001
  });
1689
2002
  messages.push({
1690
2003
  role: 'tool',
1691
2004
  content: JSON.stringify({ error: error.message }),
1692
- tool_call_id: toolCall.id
2005
+ tool_call_id: toolCall.id,
1693
2006
  });
1694
2007
  }
1695
2008
  }
@@ -1708,16 +2021,16 @@ export class TaskTool {
1708
2021
  executionHistory: {
1709
2022
  totalIterations: iteration,
1710
2023
  toolsExecuted: executionHistory.length,
1711
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1712
- failedTools: executionHistory.filter(t => t.status === 'error').length,
1713
- history: executionHistory
1714
- }
1715
- }
2024
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2025
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
2026
+ history: executionHistory,
2027
+ },
2028
+ },
1716
2029
  };
1717
2030
  }
1718
2031
  // Max iterations reached - return accumulated results instead of throwing error
1719
2032
  // Get the last assistant message content
1720
- const lastAssistantMsg = messages.filter(m => m.role === 'assistant').pop();
2033
+ const lastAssistantMsg = messages.filter((m) => m.role === 'assistant').pop();
1721
2034
  const lastContentStr = typeof lastAssistantMsg?.content === 'string'
1722
2035
  ? lastAssistantMsg.content
1723
2036
  : JSON.stringify(lastAssistantMsg?.content || '');
@@ -1732,12 +2045,12 @@ export class TaskTool {
1732
2045
  executionHistory: {
1733
2046
  totalIterations: iteration,
1734
2047
  toolsExecuted: executionHistory.length,
1735
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1736
- failedTools: executionHistory.filter(t => t.status === 'error').length,
2048
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2049
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
1737
2050
  history: executionHistory,
1738
- maxIterationsReached: true
1739
- }
1740
- }
2051
+ maxIterationsReached: true,
2052
+ },
2053
+ },
1741
2054
  };
1742
2055
  }
1743
2056
  async executeParallelAgents(agents, description, mode, agentManager, toolRegistry, aiClient, indentLevel = 1) {
@@ -1765,7 +2078,8 @@ export class TaskTool {
1765
2078
  const chunk = process.stdin.read(1);
1766
2079
  if (chunk && chunk.length > 0) {
1767
2080
  const code = chunk[0];
1768
- if (code === 0x1B) { // ESC
2081
+ if (code === 0x1b) {
2082
+ // ESC
1769
2083
  logger.debug('[ParallelAgents] ESC detected via polling!');
1770
2084
  cancellationManager.cancel();
1771
2085
  }
@@ -1801,7 +2115,7 @@ export class TaskTool {
1801
2115
  success: false,
1802
2116
  agent: agentTask.subagent_type,
1803
2117
  description: agentTask.description,
1804
- error: 'Operation cancelled by user'
2118
+ error: 'Operation cancelled by user',
1805
2119
  };
1806
2120
  }
1807
2121
  try {
@@ -1810,7 +2124,7 @@ export class TaskTool {
1810
2124
  success: true,
1811
2125
  agent: agentTask.subagent_type,
1812
2126
  description: agentTask.description,
1813
- result: result.result
2127
+ result: result.result,
1814
2128
  };
1815
2129
  }
1816
2130
  catch (error) {
@@ -1818,14 +2132,14 @@ export class TaskTool {
1818
2132
  success: false,
1819
2133
  agent: agentTask.subagent_type,
1820
2134
  description: agentTask.description,
1821
- error: error.message
2135
+ error: error.message,
1822
2136
  };
1823
2137
  }
1824
2138
  });
1825
2139
  const results = await Promise.all(agentPromises);
1826
2140
  const duration = Date.now() - startTime;
1827
- const successfulAgents = results.filter(r => r.success);
1828
- const failedAgents = results.filter(r => !r.success);
2141
+ const successfulAgents = results.filter((r) => r.success);
2142
+ const failedAgents = results.filter((r) => !r.success);
1829
2143
  console.log(`${indent}${colors.success('✔')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`);
1830
2144
  console.log(`${indent}${colors.info('ℹ')} Success: ${successfulAgents.length}/${agents.length} agents\n`);
1831
2145
  if (failedAgents.length > 0) {
@@ -1841,16 +2155,16 @@ export class TaskTool {
1841
2155
  return {
1842
2156
  success: failedAgents.length === 0,
1843
2157
  message: `Parallel task "${description}" completed: ${successfulAgents.length}/${agents.length} successful`,
1844
- results: successfulAgents.map(r => ({
2158
+ results: successfulAgents.map((r) => ({
1845
2159
  agent: r.agent,
1846
2160
  description: r.description,
1847
- result: r.result
2161
+ result: r.result,
1848
2162
  })),
1849
- errors: failedAgents.map(r => ({
2163
+ errors: failedAgents.map((r) => ({
1850
2164
  agent: r.agent,
1851
2165
  description: r.description,
1852
- error: r.error
1853
- }))
2166
+ error: r.error,
2167
+ })),
1854
2168
  };
1855
2169
  }
1856
2170
  }
@@ -1882,7 +2196,12 @@ export class ReadBashOutputTool {
1882
2196
  - Use appropriate poll_interval based on expected task duration
1883
2197
  - Check status to see if task is still running or completed
1884
2198
  - Combine with todo_write to track background task progress`;
1885
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2199
+ allowedModes = [
2200
+ ExecutionMode.YOLO,
2201
+ ExecutionMode.ACCEPT_EDITS,
2202
+ ExecutionMode.PLAN,
2203
+ ExecutionMode.SMART,
2204
+ ];
1886
2205
  async execute(params) {
1887
2206
  const { task_id, poll_interval = 10 } = params;
1888
2207
  try {
@@ -1892,7 +2211,7 @@ export class ReadBashOutputTool {
1892
2211
  throw new Error(`Task ${task_id} not found`);
1893
2212
  }
1894
2213
  const interval = Math.min(Math.max(poll_interval, 1), 120);
1895
- await new Promise(resolve => setTimeout(resolve, interval * 1000));
2214
+ await new Promise((resolve) => setTimeout(resolve, interval * 1000));
1896
2215
  const duration = Date.now() - task.startTime;
1897
2216
  const output = task.output.join('');
1898
2217
  const status = task.process.exitCode === null ? 'running' : 'completed';
@@ -1900,7 +2219,7 @@ export class ReadBashOutputTool {
1900
2219
  taskId: task_id,
1901
2220
  output,
1902
2221
  status,
1903
- duration: Math.floor(duration / 1000)
2222
+ duration: Math.floor(duration / 1000),
1904
2223
  };
1905
2224
  }
1906
2225
  catch (error) {
@@ -1936,7 +2255,12 @@ export class WebFetchTool {
1936
2255
  - Use specific prompts to extract relevant information
1937
2256
  - Check if the page is accessible if you get errors
1938
2257
  - Large pages may be truncated due to size limits`;
1939
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2258
+ allowedModes = [
2259
+ ExecutionMode.YOLO,
2260
+ ExecutionMode.ACCEPT_EDITS,
2261
+ ExecutionMode.PLAN,
2262
+ ExecutionMode.SMART,
2263
+ ];
1940
2264
  async execute(params) {
1941
2265
  const { prompt } = params;
1942
2266
  try {
@@ -1948,7 +2272,7 @@ export class WebFetchTool {
1948
2272
  const response = await axios.get(url, {
1949
2273
  timeout: 30000,
1950
2274
  maxContentLength: 10 * 1024 * 1024,
1951
- validateStatus: () => true
2275
+ validateStatus: () => true,
1952
2276
  });
1953
2277
  let content = response.data;
1954
2278
  if (typeof content === 'object') {
@@ -1957,7 +2281,7 @@ export class WebFetchTool {
1957
2281
  return {
1958
2282
  content,
1959
2283
  url,
1960
- status: response.status
2284
+ status: response.status,
1961
2285
  };
1962
2286
  }
1963
2287
  catch (error) {
@@ -1998,7 +2322,12 @@ export class AskUserQuestionTool {
1998
2322
  - Provide options when possible for faster response
1999
2323
  - Use multiSelect=true when multiple answers are valid
2000
2324
  - Be clear and concise in question wording`;
2001
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2325
+ allowedModes = [
2326
+ ExecutionMode.YOLO,
2327
+ ExecutionMode.ACCEPT_EDITS,
2328
+ ExecutionMode.PLAN,
2329
+ ExecutionMode.SMART,
2330
+ ];
2002
2331
  async execute(params) {
2003
2332
  const { questions } = params;
2004
2333
  try {
@@ -2008,26 +2337,18 @@ export class AskUserQuestionTool {
2008
2337
  const answers = [];
2009
2338
  for (const q of questions) {
2010
2339
  if (q.options && q.options.length > 0) {
2011
- const result = await inquirer.prompt([
2012
- {
2013
- type: q.multiSelect ? 'checkbox' : 'list',
2014
- name: 'answer',
2015
- message: q.question,
2016
- choices: q.options,
2017
- default: q.multiSelect ? [] : q.options[0]
2018
- }
2019
- ]);
2020
- answers.push(Array.isArray(result.answer) ? result.answer.join(', ') : result.answer);
2340
+ const options = q.options.map((opt) => ({ value: opt, label: opt }));
2341
+ const result = await select({
2342
+ message: q.question,
2343
+ options,
2344
+ });
2345
+ answers.push(result);
2021
2346
  }
2022
2347
  else {
2023
- const result = await inquirer.prompt([
2024
- {
2025
- type: 'input',
2026
- name: 'answer',
2027
- message: q.question
2028
- }
2029
- ]);
2030
- answers.push(result.answer);
2348
+ const result = (await text({
2349
+ message: q.question,
2350
+ }));
2351
+ answers.push(result);
2031
2352
  }
2032
2353
  }
2033
2354
  return { answers };
@@ -2066,7 +2387,12 @@ export class SaveMemoryTool {
2066
2387
  - Keep facts concise and specific
2067
2388
  - Remember project-specific conventions for consistency
2068
2389
  - This persists across sessions (global memory)`;
2069
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2390
+ allowedModes = [
2391
+ ExecutionMode.YOLO,
2392
+ ExecutionMode.ACCEPT_EDITS,
2393
+ ExecutionMode.PLAN,
2394
+ ExecutionMode.SMART,
2395
+ ];
2070
2396
  async execute(params) {
2071
2397
  const { fact } = params;
2072
2398
  try {
@@ -2075,7 +2401,7 @@ export class SaveMemoryTool {
2075
2401
  await memoryManager.saveMemory(fact, 'global');
2076
2402
  return {
2077
2403
  success: true,
2078
- message: `Successfully saved fact to memory`
2404
+ message: `Successfully saved fact to memory`,
2079
2405
  };
2080
2406
  }
2081
2407
  catch (error) {
@@ -2111,14 +2437,19 @@ export class ExitPlanModeTool {
2111
2437
  - Include all necessary steps and considerations
2112
2438
  - The plan will be saved for reference during execution
2113
2439
  - Use this only when truly ready to start coding`;
2114
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2440
+ allowedModes = [
2441
+ ExecutionMode.YOLO,
2442
+ ExecutionMode.ACCEPT_EDITS,
2443
+ ExecutionMode.PLAN,
2444
+ ExecutionMode.SMART,
2445
+ ];
2115
2446
  async execute(params) {
2116
2447
  const { plan } = params;
2117
2448
  try {
2118
2449
  return {
2119
2450
  success: true,
2120
2451
  message: 'Plan completed and ready for execution',
2121
- plan
2452
+ plan,
2122
2453
  };
2123
2454
  }
2124
2455
  catch (error) {
@@ -2162,7 +2493,7 @@ export class XmlEscapeTool {
2162
2493
  { char: '<', replacement: '&lt;' },
2163
2494
  { char: '>', replacement: '&gt;' },
2164
2495
  { char: '"', replacement: '&quot;' },
2165
- { char: "'", replacement: '&apos;' }
2496
+ { char: "'", replacement: '&apos;' },
2166
2497
  ];
2167
2498
  let changes = 0;
2168
2499
  for (const { char, replacement } of specialChars) {
@@ -2177,7 +2508,7 @@ export class XmlEscapeTool {
2177
2508
  const additionalChars = [
2178
2509
  { char: '©', replacement: '&copy;' },
2179
2510
  { char: '®', replacement: '&reg;' },
2180
- { char: '€', replacement: '&euro;' }
2511
+ { char: '€', replacement: '&euro;' },
2181
2512
  ];
2182
2513
  for (const { char, replacement } of additionalChars) {
2183
2514
  const regex = new RegExp(this.escapeRegExp(char), 'g');
@@ -2192,7 +2523,7 @@ export class XmlEscapeTool {
2192
2523
  return {
2193
2524
  success: true,
2194
2525
  message: `Successfully escaped ${changes} character(s) in ${file_path}`,
2195
- changes
2526
+ changes,
2196
2527
  };
2197
2528
  }
2198
2529
  catch (error) {
@@ -2232,7 +2563,12 @@ export class ImageReadTool {
2232
2563
  - Provide clear prompts for what to look for
2233
2564
  - Use task_brief for context
2234
2565
  - Supports PNG, JPG, GIF, WEBP, SVG, BMP`;
2235
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2566
+ allowedModes = [
2567
+ ExecutionMode.YOLO,
2568
+ ExecutionMode.ACCEPT_EDITS,
2569
+ ExecutionMode.PLAN,
2570
+ ExecutionMode.SMART,
2571
+ ];
2236
2572
  async execute(params) {
2237
2573
  const { image_input, prompt, task_brief, input_type = 'file_path', mime_type } = params;
2238
2574
  try {
@@ -2245,16 +2581,11 @@ export class ImageReadTool {
2245
2581
  else {
2246
2582
  imageData = image_input;
2247
2583
  }
2248
- const { AIClient } = await import('./ai-client.js');
2249
2584
  const configManager = await import('./config.js');
2250
2585
  const { getConfigManager } = configManager;
2251
2586
  const config = getConfigManager();
2252
- const aiClient = new AIClient({
2253
- type: AuthType.OPENAI_COMPATIBLE,
2254
- apiKey: config.get('apiKey'),
2255
- baseUrl: config.get('baseUrl'),
2256
- modelName: config.get('modelName') || 'Qwen3-Coder'
2257
- });
2587
+ const authConfig = config.getAuthConfig();
2588
+ const aiClient = createAIClient(authConfig);
2258
2589
  const textContent = task_brief ? `${task_brief}\n\n${prompt}` : prompt;
2259
2590
  const messages = [
2260
2591
  {
@@ -2262,19 +2593,19 @@ export class ImageReadTool {
2262
2593
  content: [
2263
2594
  {
2264
2595
  type: 'text',
2265
- text: textContent
2596
+ text: textContent,
2266
2597
  },
2267
2598
  {
2268
2599
  type: 'image_url',
2269
2600
  image_url: {
2270
- url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`
2271
- }
2272
- }
2273
- ]
2274
- }
2601
+ url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`,
2602
+ },
2603
+ },
2604
+ ],
2605
+ },
2275
2606
  ];
2276
2607
  const result = await aiClient.chatCompletion(messages, {
2277
- temperature: 0.7
2608
+ temperature: 0.7,
2278
2609
  });
2279
2610
  const messageContent = result.choices[0]?.message?.content;
2280
2611
  const analysis = typeof messageContent === 'string' ? messageContent : '';
@@ -2283,8 +2614,8 @@ export class ImageReadTool {
2283
2614
  image_info: {
2284
2615
  input_type,
2285
2616
  prompt,
2286
- task_brief
2287
- }
2617
+ task_brief,
2618
+ },
2288
2619
  };
2289
2620
  }
2290
2621
  catch (error) {
@@ -2365,7 +2696,7 @@ export class InvokeSkillTool {
2365
2696
  const { skillId, taskDescription, inputFile, outputFile, options } = params;
2366
2697
  try {
2367
2698
  const { getSkillInvoker } = await import('./skill-invoker.js');
2368
- const { SkillExecutionParams } = await import('./skill-invoker.js');
2699
+ const { SkillExecutionParams } = (await import('./skill-invoker.js'));
2369
2700
  const skillInvoker = getSkillInvoker();
2370
2701
  await skillInvoker.initialize();
2371
2702
  // Verify skill exists
@@ -2380,7 +2711,7 @@ export class InvokeSkillTool {
2380
2711
  taskDescription,
2381
2712
  inputFile,
2382
2713
  outputFile,
2383
- options
2714
+ options,
2384
2715
  });
2385
2716
  if (result.success) {
2386
2717
  return {
@@ -2390,7 +2721,7 @@ export class InvokeSkillTool {
2390
2721
  task: taskDescription,
2391
2722
  result: result.output,
2392
2723
  files: result.files,
2393
- nextSteps: result.nextSteps
2724
+ nextSteps: result.nextSteps,
2394
2725
  };
2395
2726
  }
2396
2727
  else {
@@ -2404,7 +2735,7 @@ export class InvokeSkillTool {
2404
2735
  taskDescription,
2405
2736
  inputFile,
2406
2737
  outputFile,
2407
- options
2738
+ options,
2408
2739
  });
2409
2740
  if (result.success) {
2410
2741
  return {
@@ -2414,7 +2745,8 @@ export class InvokeSkillTool {
2414
2745
  task: taskDescription,
2415
2746
  result: result.output,
2416
2747
  files: result.files,
2417
- nextSteps: result.nextSteps
2748
+ nextSteps: result.nextSteps,
2749
+ skillPath: result.skillPath
2418
2750
  };
2419
2751
  }
2420
2752
  else {
@@ -2542,7 +2874,8 @@ export class ToolRegistry {
2542
2874
  let registeredCount = 0;
2543
2875
  for (const [fullName, tool] of mcpTools) {
2544
2876
  const firstUnderscoreIndex = fullName.indexOf('__');
2545
- if (firstUnderscoreIndex === -1 || firstUnderscoreIndex === 0 ||
2877
+ if (firstUnderscoreIndex === -1 ||
2878
+ firstUnderscoreIndex === 0 ||
2546
2879
  firstUnderscoreIndex === fullName.length - 2)
2547
2880
  continue;
2548
2881
  const serverName = fullName.substring(0, firstUnderscoreIndex);
@@ -2577,7 +2910,7 @@ export class ToolRegistry {
2577
2910
  const { getMCPManager } = await import('./mcp.js');
2578
2911
  const mcpManager = getMCPManager();
2579
2912
  return await mcpManager.callTool(fullName, params);
2580
- }
2913
+ },
2581
2914
  };
2582
2915
  this.tools.set(toolName, mcpTool);
2583
2916
  registeredCount++;
@@ -2621,11 +2954,11 @@ export class ToolRegistry {
2621
2954
  this.backgroundTasks.delete(taskId);
2622
2955
  }
2623
2956
  getToolDefinitions() {
2624
- return Array.from(this.tools.values()).map(tool => {
2957
+ return Array.from(this.tools.values()).map((tool) => {
2625
2958
  let parameters = {
2626
2959
  type: 'object',
2627
2960
  properties: {},
2628
- required: []
2961
+ required: [],
2629
2962
  };
2630
2963
  // Define specific parameters for each tool
2631
2964
  switch (tool.name) {
@@ -2635,18 +2968,18 @@ export class ToolRegistry {
2635
2968
  properties: {
2636
2969
  filePath: {
2637
2970
  type: 'string',
2638
- description: 'The absolute path to the file to read'
2971
+ description: 'The absolute path to the file to read',
2639
2972
  },
2640
2973
  offset: {
2641
2974
  type: 'number',
2642
- description: 'Optional: Line number to start reading from (0-based)'
2975
+ description: 'Optional: Line number to start reading from (0-based)',
2643
2976
  },
2644
2977
  limit: {
2645
2978
  type: 'number',
2646
- description: 'Optional: Maximum number of lines to read'
2647
- }
2979
+ description: 'Optional: Maximum number of lines to read',
2980
+ },
2648
2981
  },
2649
- required: ['filePath']
2982
+ required: ['filePath'],
2650
2983
  };
2651
2984
  break;
2652
2985
  case 'Write':
@@ -2655,14 +2988,14 @@ export class ToolRegistry {
2655
2988
  properties: {
2656
2989
  filePath: {
2657
2990
  type: 'string',
2658
- description: 'The absolute path to the file to write'
2991
+ description: 'The absolute path to the file to write',
2659
2992
  },
2660
2993
  content: {
2661
2994
  type: 'string',
2662
- description: 'The content to write to the file'
2663
- }
2995
+ description: 'The content to write to the file',
2996
+ },
2664
2997
  },
2665
- required: ['filePath', 'content']
2998
+ required: ['filePath', 'content'],
2666
2999
  };
2667
3000
  break;
2668
3001
  case 'Grep':
@@ -2671,26 +3004,34 @@ export class ToolRegistry {
2671
3004
  properties: {
2672
3005
  pattern: {
2673
3006
  type: 'string',
2674
- description: 'The regex pattern to search for'
3007
+ description: 'The regex pattern or literal string to search for',
2675
3008
  },
2676
3009
  path: {
2677
3010
  type: 'string',
2678
- description: 'Optional: The path to search in (default: current directory)'
3011
+ description: 'Optional: The path to search in (default: current directory)',
2679
3012
  },
2680
- include: {
3013
+ glob: {
2681
3014
  type: 'string',
2682
- description: 'Optional: Glob pattern to filter files'
3015
+ description: 'Optional: Glob pattern to filter files (e.g., "*.ts", "**/*.js")',
2683
3016
  },
2684
- case_sensitive: {
3017
+ ignoreCase: {
2685
3018
  type: 'boolean',
2686
- description: 'Optional: Case-sensitive search (default: false)'
3019
+ description: 'Optional: Case-insensitive search (default: false)',
3020
+ },
3021
+ literal: {
3022
+ type: 'boolean',
3023
+ description: 'Optional: Treat pattern as literal string (default: false)',
2687
3024
  },
2688
3025
  context: {
2689
3026
  type: 'number',
2690
- description: 'Optional: Number of context lines to show'
2691
- }
3027
+ description: 'Optional: Number of context lines to show before and after',
3028
+ },
3029
+ limit: {
3030
+ type: 'number',
3031
+ description: 'Optional: Maximum number of matches to return',
3032
+ },
2692
3033
  },
2693
- required: ['pattern']
3034
+ required: ['pattern'],
2694
3035
  };
2695
3036
  break;
2696
3037
  case 'Bash':
@@ -2699,26 +3040,26 @@ export class ToolRegistry {
2699
3040
  properties: {
2700
3041
  command: {
2701
3042
  type: 'string',
2702
- description: 'The shell command to execute'
3043
+ description: 'The shell command to execute',
2703
3044
  },
2704
3045
  cwd: {
2705
3046
  type: 'string',
2706
- description: 'Optional: Working directory'
3047
+ description: 'Optional: Working directory',
2707
3048
  },
2708
3049
  description: {
2709
3050
  type: 'string',
2710
- description: 'Optional: Brief description of the command'
3051
+ description: 'Optional: Brief description of the command',
2711
3052
  },
2712
3053
  timeout: {
2713
3054
  type: 'number',
2714
- description: 'Optional: Timeout in seconds (default: 120)'
3055
+ description: 'Optional: Timeout in seconds (default: 120)',
2715
3056
  },
2716
3057
  run_in_bg: {
2717
3058
  type: 'boolean',
2718
- description: 'Optional: Run in background (default: false)'
2719
- }
3059
+ description: 'Optional: Run in background (default: false)',
3060
+ },
2720
3061
  },
2721
- required: ['command']
3062
+ required: ['command'],
2722
3063
  };
2723
3064
  break;
2724
3065
  case 'ListDirectory':
@@ -2727,14 +3068,14 @@ export class ToolRegistry {
2727
3068
  properties: {
2728
3069
  path: {
2729
3070
  type: 'string',
2730
- description: 'Optional: The directory path to list (default: current directory)'
3071
+ description: 'Optional: The directory path to list (default: current directory)',
2731
3072
  },
2732
3073
  recursive: {
2733
3074
  type: 'boolean',
2734
- description: 'Optional: List recursively (default: false)'
2735
- }
3075
+ description: 'Optional: List recursively (default: false)',
3076
+ },
2736
3077
  },
2737
- required: []
3078
+ required: [],
2738
3079
  };
2739
3080
  break;
2740
3081
  case 'SearchFiles':
@@ -2743,18 +3084,18 @@ export class ToolRegistry {
2743
3084
  properties: {
2744
3085
  pattern: {
2745
3086
  type: 'string',
2746
- description: 'The glob pattern to match files'
3087
+ description: 'The glob pattern to match files',
2747
3088
  },
2748
3089
  path: {
2749
3090
  type: 'string',
2750
- description: 'Optional: The path to search in (default: current directory)'
3091
+ description: 'Optional: The path to search in (default: current directory)',
2751
3092
  },
2752
3093
  limit: {
2753
3094
  type: 'integer',
2754
- description: 'Optional: Maximum number of results to return (default: 1000)'
2755
- }
3095
+ description: 'Optional: Maximum number of results to return (default: 1000)',
3096
+ },
2756
3097
  },
2757
- required: ['pattern']
3098
+ required: ['pattern'],
2758
3099
  };
2759
3100
  break;
2760
3101
  case 'DeleteFile':
@@ -2763,10 +3104,10 @@ export class ToolRegistry {
2763
3104
  properties: {
2764
3105
  filePath: {
2765
3106
  type: 'string',
2766
- description: 'The path to the file to delete'
2767
- }
3107
+ description: 'The path to the file to delete',
3108
+ },
2768
3109
  },
2769
- required: ['filePath']
3110
+ required: ['filePath'],
2770
3111
  };
2771
3112
  break;
2772
3113
  case 'CreateDirectory':
@@ -2775,14 +3116,14 @@ export class ToolRegistry {
2775
3116
  properties: {
2776
3117
  dirPath: {
2777
3118
  type: 'string',
2778
- description: 'The directory path to create'
3119
+ description: 'The directory path to create',
2779
3120
  },
2780
3121
  recursive: {
2781
3122
  type: 'boolean',
2782
- description: 'Optional: Create parent directories (default: true)'
2783
- }
3123
+ description: 'Optional: Create parent directories (default: true)',
3124
+ },
2784
3125
  },
2785
- required: ['dirPath']
3126
+ required: ['dirPath'],
2786
3127
  };
2787
3128
  break;
2788
3129
  case 'Edit':
@@ -2791,22 +3132,22 @@ export class ToolRegistry {
2791
3132
  properties: {
2792
3133
  file_path: {
2793
3134
  type: 'string',
2794
- description: 'The absolute path to the file to edit'
3135
+ description: 'The absolute path to the file to edit',
2795
3136
  },
2796
3137
  instruction: {
2797
3138
  type: 'string',
2798
- description: 'Description of what needs to be changed'
3139
+ description: 'Description of what needs to be changed',
2799
3140
  },
2800
3141
  old_string: {
2801
3142
  type: 'string',
2802
- description: 'The exact text to replace (supports fuzzy matching)'
3143
+ description: 'The exact text to replace (supports fuzzy matching)',
2803
3144
  },
2804
3145
  new_string: {
2805
3146
  type: 'string',
2806
- description: 'The new text to replace with'
2807
- }
3147
+ description: 'The new text to replace with',
3148
+ },
2808
3149
  },
2809
- required: ['file_path', 'instruction', 'old_string', 'new_string']
3150
+ required: ['file_path', 'instruction', 'old_string', 'new_string'],
2810
3151
  };
2811
3152
  break;
2812
3153
  case 'web_search':
@@ -2815,10 +3156,10 @@ export class ToolRegistry {
2815
3156
  properties: {
2816
3157
  query: {
2817
3158
  type: 'string',
2818
- description: 'The search query'
2819
- }
3159
+ description: 'The search query',
3160
+ },
2820
3161
  },
2821
- required: ['query']
3162
+ required: ['query'],
2822
3163
  };
2823
3164
  break;
2824
3165
  case 'todo_write':
@@ -2833,21 +3174,24 @@ export class ToolRegistry {
2833
3174
  properties: {
2834
3175
  id: { type: 'string' },
2835
3176
  task: { type: 'string' },
2836
- status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed'] },
2837
- priority: { type: 'string', enum: ['high', 'medium', 'low'] }
3177
+ status: {
3178
+ type: 'string',
3179
+ enum: ['pending', 'in_progress', 'completed', 'failed'],
3180
+ },
3181
+ priority: { type: 'string', enum: ['high', 'medium', 'low'] },
2838
3182
  },
2839
- required: ['id', 'task', 'status']
2840
- }
2841
- }
3183
+ required: ['id', 'task', 'status'],
3184
+ },
3185
+ },
2842
3186
  },
2843
- required: ['todos']
3187
+ required: ['todos'],
2844
3188
  };
2845
3189
  break;
2846
3190
  case 'todo_read':
2847
3191
  parameters = {
2848
3192
  type: 'object',
2849
3193
  properties: {},
2850
- required: []
3194
+ required: [],
2851
3195
  };
2852
3196
  break;
2853
3197
  case 'task':
@@ -2856,7 +3200,7 @@ export class ToolRegistry {
2856
3200
  properties: {
2857
3201
  description: {
2858
3202
  type: 'string',
2859
- description: 'Brief description of the task (3-5 words)'
3203
+ description: 'Brief description of the task (3-5 words)',
2860
3204
  },
2861
3205
  agents: {
2862
3206
  type: 'array',
@@ -2866,50 +3210,66 @@ export class ToolRegistry {
2866
3210
  properties: {
2867
3211
  description: {
2868
3212
  type: 'string',
2869
- description: 'Brief description of the sub-agent task'
3213
+ description: 'Brief description of the sub-agent task',
2870
3214
  },
2871
3215
  prompt: {
2872
3216
  type: 'string',
2873
- description: 'The task for the sub-agent to perform'
3217
+ description: 'The task for the sub-agent to perform',
2874
3218
  },
2875
3219
  subagent_type: {
2876
3220
  type: 'string',
2877
- enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
2878
- description: 'The type of specialized agent'
3221
+ enum: [
3222
+ 'general-purpose',
3223
+ 'plan-agent',
3224
+ 'explore-agent',
3225
+ 'frontend-tester',
3226
+ 'code-reviewer',
3227
+ 'frontend-developer',
3228
+ 'backend-developer',
3229
+ ],
3230
+ description: 'The type of specialized agent',
2879
3231
  },
2880
3232
  constraints: {
2881
3233
  type: 'array',
2882
3234
  items: { type: 'string' },
2883
- description: 'Optional: Constraints or limitations'
2884
- }
3235
+ description: 'Optional: Constraints or limitations',
3236
+ },
2885
3237
  },
2886
- required: ['description', 'prompt', 'subagent_type']
2887
- }
3238
+ required: ['description', 'prompt', 'subagent_type'],
3239
+ },
2888
3240
  },
2889
3241
  prompt: {
2890
3242
  type: 'string',
2891
- description: 'Optional: The task for the agent to perform (use agents for parallel execution)'
3243
+ description: 'Optional: The task for the agent to perform (use agents for parallel execution)',
2892
3244
  },
2893
3245
  subagent_type: {
2894
3246
  type: 'string',
2895
- enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
2896
- description: 'Optional: The type of specialized agent (use agents for parallel execution)'
3247
+ enum: [
3248
+ 'general-purpose',
3249
+ 'plan-agent',
3250
+ 'explore-agent',
3251
+ 'frontend-tester',
3252
+ 'code-reviewer',
3253
+ 'frontend-developer',
3254
+ 'backend-developer',
3255
+ ],
3256
+ description: 'Optional: The type of specialized agent (use agents for parallel execution)',
2897
3257
  },
2898
3258
  useContext: {
2899
3259
  type: 'boolean',
2900
- description: 'Optional: Include main agent context'
3260
+ description: 'Optional: Include main agent context',
2901
3261
  },
2902
3262
  outputFormat: {
2903
3263
  type: 'string',
2904
- description: 'Optional: Output format template'
3264
+ description: 'Optional: Output format template',
2905
3265
  },
2906
3266
  constraints: {
2907
3267
  type: 'array',
2908
3268
  items: { type: 'string' },
2909
- description: 'Optional: Constraints or limitations'
2910
- }
3269
+ description: 'Optional: Constraints or limitations',
3270
+ },
2911
3271
  },
2912
- required: ['description']
3272
+ required: ['description'],
2913
3273
  };
2914
3274
  break;
2915
3275
  case 'ReadBashOutput':
@@ -2918,14 +3278,14 @@ export class ToolRegistry {
2918
3278
  properties: {
2919
3279
  task_id: {
2920
3280
  type: 'string',
2921
- description: 'The ID of the task'
3281
+ description: 'The ID of the task',
2922
3282
  },
2923
3283
  poll_interval: {
2924
3284
  type: 'number',
2925
- description: 'Optional: Polling interval in seconds (default: 10)'
2926
- }
3285
+ description: 'Optional: Polling interval in seconds (default: 10)',
3286
+ },
2927
3287
  },
2928
- required: ['task_id']
3288
+ required: ['task_id'],
2929
3289
  };
2930
3290
  break;
2931
3291
  case 'web_fetch':
@@ -2934,10 +3294,10 @@ export class ToolRegistry {
2934
3294
  properties: {
2935
3295
  prompt: {
2936
3296
  type: 'string',
2937
- description: 'Prompt containing URL(s) and processing instructions'
2938
- }
3297
+ description: 'Prompt containing URL(s) and processing instructions',
3298
+ },
2939
3299
  },
2940
- required: ['prompt']
3300
+ required: ['prompt'],
2941
3301
  };
2942
3302
  break;
2943
3303
  case 'ask_user_question':
@@ -2955,15 +3315,15 @@ export class ToolRegistry {
2955
3315
  options: {
2956
3316
  type: 'array',
2957
3317
  items: { type: 'string' },
2958
- description: 'Available choices (2-4 options)'
3318
+ description: 'Available choices (2-4 options)',
2959
3319
  },
2960
- multiSelect: { type: 'boolean' }
3320
+ multiSelect: { type: 'boolean' },
2961
3321
  },
2962
- required: ['question', 'header', 'options', 'multiSelect']
2963
- }
2964
- }
3322
+ required: ['question', 'header', 'options', 'multiSelect'],
3323
+ },
3324
+ },
2965
3325
  },
2966
- required: ['questions']
3326
+ required: ['questions'],
2967
3327
  };
2968
3328
  break;
2969
3329
  case 'save_memory':
@@ -2972,10 +3332,10 @@ export class ToolRegistry {
2972
3332
  properties: {
2973
3333
  fact: {
2974
3334
  type: 'string',
2975
- description: 'The specific fact to remember'
2976
- }
3335
+ description: 'The specific fact to remember',
3336
+ },
2977
3337
  },
2978
- required: ['fact']
3338
+ required: ['fact'],
2979
3339
  };
2980
3340
  break;
2981
3341
  case 'exit_plan_mode':
@@ -2984,10 +3344,10 @@ export class ToolRegistry {
2984
3344
  properties: {
2985
3345
  plan: {
2986
3346
  type: 'string',
2987
- description: 'The plan to present'
2988
- }
3347
+ description: 'The plan to present',
3348
+ },
2989
3349
  },
2990
- required: ['plan']
3350
+ required: ['plan'],
2991
3351
  };
2992
3352
  break;
2993
3353
  case 'xml_escape':
@@ -2996,14 +3356,14 @@ export class ToolRegistry {
2996
3356
  properties: {
2997
3357
  file_path: {
2998
3358
  type: 'string',
2999
- description: 'The absolute path to the XML/HTML file'
3359
+ description: 'The absolute path to the XML/HTML file',
3000
3360
  },
3001
3361
  escape_all: {
3002
3362
  type: 'boolean',
3003
- description: 'Optional: Escape all special characters (default: false)'
3004
- }
3363
+ description: 'Optional: Escape all special characters (default: false)',
3364
+ },
3005
3365
  },
3006
- required: ['file_path']
3366
+ required: ['file_path'],
3007
3367
  };
3008
3368
  break;
3009
3369
  case 'image_read':
@@ -3012,27 +3372,27 @@ export class ToolRegistry {
3012
3372
  properties: {
3013
3373
  image_input: {
3014
3374
  type: 'string',
3015
- description: 'Image file path or base64 data'
3375
+ description: 'Image file path or base64 data',
3016
3376
  },
3017
3377
  prompt: {
3018
3378
  type: 'string',
3019
- description: 'Comprehensive VLM instruction'
3379
+ description: 'Comprehensive VLM instruction',
3020
3380
  },
3021
3381
  task_brief: {
3022
3382
  type: 'string',
3023
- description: 'Brief task description (max 15 words)'
3383
+ description: 'Brief task description (max 15 words)',
3024
3384
  },
3025
3385
  input_type: {
3026
3386
  type: 'string',
3027
3387
  enum: ['file_path', 'base64'],
3028
- description: 'Input type (default: file_path)'
3388
+ description: 'Input type (default: file_path)',
3029
3389
  },
3030
3390
  mime_type: {
3031
3391
  type: 'string',
3032
- description: 'Optional: MIME type for base64 input'
3033
- }
3392
+ description: 'Optional: MIME type for base64 input',
3393
+ },
3034
3394
  },
3035
- required: ['image_input', 'prompt']
3395
+ required: ['image_input', 'prompt'],
3036
3396
  };
3037
3397
  break;
3038
3398
  case 'Skill':
@@ -3041,17 +3401,17 @@ export class ToolRegistry {
3041
3401
  properties: {
3042
3402
  skill: {
3043
3403
  type: 'string',
3044
- description: 'The skill name to execute'
3045
- }
3404
+ description: 'The skill name to execute',
3405
+ },
3046
3406
  },
3047
- required: ['skill']
3407
+ required: ['skill'],
3048
3408
  };
3049
3409
  break;
3050
3410
  case 'ListSkills':
3051
3411
  parameters = {
3052
3412
  type: 'object',
3053
3413
  properties: {},
3054
- required: []
3414
+ required: [],
3055
3415
  };
3056
3416
  break;
3057
3417
  case 'GetSkillDetails':
@@ -3060,10 +3420,10 @@ export class ToolRegistry {
3060
3420
  properties: {
3061
3421
  skill: {
3062
3422
  type: 'string',
3063
- description: 'The skill name/id to get details for'
3064
- }
3423
+ description: 'The skill name/id to get details for',
3424
+ },
3065
3425
  },
3066
- required: ['skill']
3426
+ required: ['skill'],
3067
3427
  };
3068
3428
  break;
3069
3429
  default:
@@ -3074,13 +3434,13 @@ export class ToolRegistry {
3074
3434
  parameters = {
3075
3435
  type: 'object',
3076
3436
  properties: {},
3077
- required: []
3437
+ required: [],
3078
3438
  };
3079
3439
  if (mcpTool.inputSchema.properties) {
3080
3440
  for (const [paramName, paramDef] of Object.entries(mcpTool.inputSchema.properties)) {
3081
3441
  parameters.properties[paramName] = {
3082
3442
  type: paramDef.type || 'string',
3083
- description: paramDef.description || ''
3443
+ description: paramDef.description || '',
3084
3444
  };
3085
3445
  }
3086
3446
  }
@@ -3092,7 +3452,7 @@ export class ToolRegistry {
3092
3452
  parameters = {
3093
3453
  type: 'object',
3094
3454
  properties: {},
3095
- required: []
3455
+ required: [],
3096
3456
  };
3097
3457
  }
3098
3458
  }
@@ -3101,8 +3461,8 @@ export class ToolRegistry {
3101
3461
  function: {
3102
3462
  name: tool.name,
3103
3463
  description: tool.description,
3104
- parameters
3105
- }
3464
+ parameters,
3465
+ },
3106
3466
  };
3107
3467
  });
3108
3468
  }
@@ -3128,7 +3488,7 @@ export class ToolRegistry {
3128
3488
  continue;
3129
3489
  const [serverName, actualToolName] = [
3130
3490
  fullName.substring(0, firstUnderscoreIndex),
3131
- fullName.substring(firstUnderscoreIndex + 2)
3491
+ fullName.substring(firstUnderscoreIndex + 2),
3132
3492
  ];
3133
3493
  if (actualToolName === toolName) {
3134
3494
  return await this.executeMCPTool(fullName, params, executionMode, indent);
@@ -3179,7 +3539,7 @@ export class ToolRegistry {
3179
3539
  const result = await approvalEngine.evaluate({
3180
3540
  toolName,
3181
3541
  params,
3182
- timestamp: Date.now()
3542
+ timestamp: Date.now(),
3183
3543
  });
3184
3544
  // Decide whether to execute based on approval result
3185
3545
  if (result.decision === 'approved') {
@@ -3254,7 +3614,7 @@ export class ToolRegistry {
3254
3614
  const result = await approvalEngine.evaluate({
3255
3615
  toolName: `MCP[${serverName}]::${actualToolName}`,
3256
3616
  params,
3257
- timestamp: Date.now()
3617
+ timestamp: Date.now(),
3258
3618
  });
3259
3619
  if (result.decision === 'approved') {
3260
3620
  console.log(`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`);