@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/src/tools.ts CHANGED
@@ -1,21 +1,23 @@
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, ChildProcess } from 'child_process';
7
7
  import { glob } from 'glob';
8
8
  import axios from 'axios';
9
- import inquirer from 'inquirer';
10
9
  import { Tool, ExecutionMode, AuthType } from './types.js';
11
- import type { Message, ToolDefinition } from './ai-client.js';
10
+ import type { Message, ToolDefinition } from './ai-client/types.js';
11
+ import type { AIClientInterface } from './ai-client-factory.js';
12
12
  import { colors, icons, styleHelpers } from './theme.js';
13
13
  import { getLogger } from './logger.js';
14
14
  import { getCancellationManager } from './cancellation.js';
15
15
  import { SystemPromptGenerator } from './system-prompt-generator.js';
16
- import { InteractiveSession } from './session.js';
17
-
18
- const execAsync = promisify(exec);
16
+ import { getSingletonSession } from './session.js';
17
+ import { ripgrep, fdFind } from './ripgrep.js';
18
+ import { getShellConfig, killProcessTree, quoteShellCommand } from './shell.js';
19
+ import { truncateTail, buildTruncationNotice } from './truncate.js';
20
+ import { createAIClient } from './ai-client-factory.js';
19
21
 
20
22
  //
21
23
  // Tool Description Pattern
@@ -70,9 +72,17 @@ export class ReadTool implements Tool {
70
72
  - Combine with ListDirectory to explore project structure first
71
73
  - Don't re-read files unnecessarily`;
72
74
 
73
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
75
+ allowedModes = [
76
+ ExecutionMode.YOLO,
77
+ ExecutionMode.ACCEPT_EDITS,
78
+ ExecutionMode.PLAN,
79
+ ExecutionMode.SMART,
80
+ ];
74
81
 
75
82
  async execute(params: { filePath: string; offset?: number; limit?: number }): Promise<string> {
83
+ if (!params || typeof params.filePath !== 'string') {
84
+ throw new Error('filePath is required and must be a string');
85
+ }
76
86
  const { filePath, offset = 0, limit } = params;
77
87
 
78
88
  try {
@@ -89,13 +99,24 @@ export class ReadTool implements Tool {
89
99
  }
90
100
  const absolutePath = path.resolve(resolvedPath);
91
101
  const content = await fs.readFile(absolutePath, 'utf-8');
92
-
102
+
93
103
  const lines = content.split('\n');
104
+ const totalLines = lines.length;
94
105
  const startLine = Math.max(0, offset);
95
- const endLine = limit !== undefined ? Math.min(lines.length, startLine + limit) : lines.length;
106
+ const endLine = limit !== undefined ? Math.min(totalLines, startLine + limit) : totalLines;
96
107
  const selectedLines = lines.slice(startLine, endLine);
97
-
98
- return selectedLines.join('\n');
108
+ const result = selectedLines.join('\n');
109
+
110
+ // Add truncation notice if content is limited
111
+ if (limit !== undefined && endLine < totalLines) {
112
+ const remaining = totalLines - endLine;
113
+ const nextOffset = endLine;
114
+ return (
115
+ result + `\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`
116
+ );
117
+ }
118
+
119
+ return result;
99
120
  } catch (error: any) {
100
121
  // Show user-friendly path in error message
101
122
  let displayPath = filePath;
@@ -142,7 +163,16 @@ export class WriteTool implements Tool {
142
163
  - For partial edits, use Edit tool instead`;
143
164
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
144
165
 
145
- async execute(params: { filePath: string; content: string }): Promise<{ success: boolean; message: string; filePath: string; lineCount: number; preview?: string }> {
166
+ async execute(params: {
167
+ filePath: string;
168
+ content: string;
169
+ }): Promise<{
170
+ success: boolean;
171
+ message: string;
172
+ filePath: string;
173
+ lineCount: number;
174
+ preview?: string;
175
+ }> {
146
176
  const { filePath, content } = params;
147
177
 
148
178
  try {
@@ -161,7 +191,7 @@ export class WriteTool implements Tool {
161
191
  message: `Successfully wrote to ${filePath}`,
162
192
  filePath,
163
193
  lineCount,
164
- preview: isTruncated ? preview + '\n...' : preview
194
+ preview: isTruncated ? preview + '\n...' : preview,
165
195
  };
166
196
  } catch (error: any) {
167
197
  throw new Error(`Failed to write file ${filePath}: ${error.message}`);
@@ -171,7 +201,7 @@ export class WriteTool implements Tool {
171
201
 
172
202
  export class GrepTool implements Tool {
173
203
  name = 'Grep';
174
- 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.
204
+ description = `Search for text patterns within files using ripgrep. This is your PRIMARY tool for finding specific code, functions, or content.
175
205
 
176
206
  # When to Use
177
207
  - Finding specific function definitions or calls
@@ -188,122 +218,60 @@ export class GrepTool implements Tool {
188
218
  # Parameters
189
219
  - \`pattern\`: Regex or literal string to search for
190
220
  - \`path\`: (Optional) Directory to search in, default: "."
191
- - \`include\`: (Optional) File glob pattern to include
192
- - \`exclude\`: (Optional) File glob pattern to exclude
193
- - \`case_sensitive\`: (Optional) Case-sensitive search, default: false
194
- - \`fixed_strings\`: (Optional) Treat pattern as literal string, default: false
221
+ - \`glob\`: (Optional) File glob pattern to include (e.g., "*.ts", "**/*.js")
222
+ - \`ignoreCase\`: (Optional) Case-insensitive search, default: false
223
+ - \`literal\`: (Optional) Treat pattern as literal string, default: false
195
224
  - \`context\`: (Optional) Lines of context before/after matches
196
- - \`no_ignore\`: (Optional) Don't ignore node_modules/.git, default: false
197
225
 
198
226
  # Examples
199
227
  - Find function: Grep(pattern="function myFunction")
200
228
  - Find with context: Grep(pattern="TODO", context=3)
201
- - TypeScript only: Grep(pattern="interface", include="*.ts")
229
+ - TypeScript only: Grep(pattern="interface", glob="*.ts")
230
+ - Case-insensitive: Grep(pattern="error", ignoreCase=true)
202
231
 
203
232
  # Best Practices
204
- - Use case_sensitive=true for short patterns to reduce false positives
205
- - Use fixed_strings=true if your pattern has special regex characters
233
+ - Use ignoreCase=true for short patterns to reduce false positives
234
+ - Use literal=true if your pattern has special regex characters
206
235
  - Use context to see the surrounding code for each match
207
- - Combine with include/exclude to narrow down file types`;
208
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
236
+ - Combine with glob to narrow down file types`;
237
+ allowedModes = [
238
+ ExecutionMode.YOLO,
239
+ ExecutionMode.ACCEPT_EDITS,
240
+ ExecutionMode.PLAN,
241
+ ExecutionMode.SMART,
242
+ ];
209
243
 
210
244
  async execute(params: {
211
245
  pattern: string;
212
246
  path?: string;
213
- include?: string;
214
- exclude?: string;
215
- case_sensitive?: boolean;
216
- fixed_strings?: boolean;
247
+ glob?: string;
248
+ ignoreCase?: boolean;
249
+ literal?: boolean;
217
250
  context?: number;
218
- after?: number;
219
- before?: number;
220
- no_ignore?: boolean;
251
+ limit?: number;
221
252
  }): Promise<string[]> {
222
253
  const {
223
254
  pattern,
224
255
  path: searchPath = '.',
225
- include,
226
- exclude,
227
- case_sensitive = false,
228
- fixed_strings = false,
256
+ glob: includeGlob,
257
+ ignoreCase = false,
258
+ literal = false,
229
259
  context,
230
- after,
231
- before,
232
- no_ignore = false
260
+ limit,
233
261
  } = params;
234
-
262
+
235
263
  try {
236
- const ignorePatterns = no_ignore ? [] : ['node_modules/**', '.git/**', 'dist/**', 'build/**'];
237
- if (exclude) {
238
- ignorePatterns.push(exclude);
239
- }
240
-
241
- const absolutePath = path.resolve(searchPath);
242
- const files = await glob('**/*', {
243
- cwd: absolutePath,
244
- nodir: true,
245
- ignore: ignorePatterns
264
+ const result = await ripgrep({
265
+ pattern,
266
+ path: searchPath,
267
+ glob: includeGlob,
268
+ ignoreCase,
269
+ literal,
270
+ context,
271
+ limit,
246
272
  });
247
273
 
248
- const results: string[] = [];
249
-
250
- for (const file of files) {
251
- const fullPath = path.join(absolutePath, file);
252
- if (include && !file.match(include)) {
253
- continue;
254
- }
255
-
256
- try {
257
- const content = await fs.readFile(fullPath, 'utf-8');
258
- const lines = content.split('\n');
259
-
260
- lines.forEach((line, index) => {
261
- let matches = false;
262
-
263
- if (fixed_strings) {
264
- matches = case_sensitive
265
- ? line.includes(pattern)
266
- : line.toLowerCase().includes(pattern.toLowerCase());
267
- } else {
268
- try {
269
- const flags = case_sensitive ? 'g' : 'gi';
270
- const regex = new RegExp(pattern, flags);
271
- matches = regex.test(line);
272
- } catch (e) {
273
- matches = case_sensitive
274
- ? line.includes(pattern)
275
- : line.toLowerCase().includes(pattern.toLowerCase());
276
- }
277
- }
278
-
279
- if (matches) {
280
- const contextLines: string[] = [];
281
-
282
- if (before || context) {
283
- const beforeCount = before || context || 0;
284
- for (let i = Math.max(0, index - beforeCount); i < index; i++) {
285
- contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
286
- }
287
- }
288
-
289
- contextLines.push(`${fullPath}:${index + 1}:${line.trim()}`);
290
-
291
- if (after || context) {
292
- const afterCount = after || context || 0;
293
- for (let i = index + 1; i < Math.min(lines.length, index + 1 + afterCount); i++) {
294
- contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
295
- }
296
- }
297
-
298
- results.push(...contextLines);
299
- }
300
- });
301
- } catch (error) {
302
- continue;
303
- }
304
- }
305
-
306
- return results;
274
+ return result.split('\n').filter((line) => line.trim());
307
275
  } catch (error: any) {
308
276
  throw new Error(`Grep failed: ${error.message}`);
309
277
  }
@@ -335,13 +303,23 @@ export class BashTool implements Tool {
335
303
  - \`description\`: (Optional) Description of what the command does
336
304
  - \`timeout\`: (Optional) Timeout in seconds, default: 120
337
305
  - \`run_in_bg\`: (Optional) Run in background, default: false
306
+ - \`skillPath\`: (Optional) Skill directory path - when provided, NODE_PATH will include the skill's node_modules for dependency resolution
338
307
 
339
308
  # Examples
340
309
  - Install dependencies: Bash(command="npm install", description="Install npm dependencies")
310
+ - Run in skill directory with local deps: Bash(command="npm install docx", skillPath="~/.xagent/skills/docx")
311
+
312
+ # NODE_PATH Resolution
313
+ When \`skillPath\` is provided, the command will have access to:
314
+ - \`<skillPath>/node_modules\` (skill's local dependencies)
315
+ - xAgent's global node_modules
316
+
317
+ This is useful when working with skills that have local dependencies.
341
318
  - Run tests: Bash(command="npm test", description="Run unit tests")
342
319
  - Build project: Bash(command="npm run build", description="Build the project")
343
320
 
344
321
  # Best Practices
322
+ - To install npm packages that persist across sessions, use: \`XAGENT_USER_NPM=1 npm install <package>\`
345
323
  - Always provide a description for context
346
324
  - Set appropriate timeout for long-running commands
347
325
  - Use run_in_bg=true for commands that take a long time
@@ -355,14 +333,23 @@ export class BashTool implements Tool {
355
333
  description?: string;
356
334
  timeout?: number;
357
335
  run_in_bg?: boolean;
358
- }): Promise<{ stdout: string; stderr: string; exitCode: number; taskId?: string }> {
359
- const { command, cwd, description, timeout = 120, run_in_bg = false } = params;
360
-
336
+ skillPath?: string; // Skill directory path for NODE_PATH resolution
337
+ }): Promise<{
338
+ stdout: string;
339
+ stderr: string;
340
+ exitCode: number;
341
+ taskId?: string;
342
+ truncated?: boolean;
343
+ truncationNotice?: string;
344
+ skillPath?: string;
345
+ }> {
346
+ const { command, cwd, description, timeout = 120, run_in_bg = false, skillPath } = params;
347
+
361
348
  // Determine effective working directory
362
349
  // Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
363
350
  let effectiveCwd: string | undefined;
364
351
  const hasCdCommand = /cd\s+["']?[^"&|;]+["']?/.test(command);
365
-
352
+
366
353
  if (cwd && !hasCdCommand) {
367
354
  // Command doesn't control its own directory, use provided cwd
368
355
  effectiveCwd = cwd;
@@ -373,82 +360,270 @@ export class BashTool implements Tool {
373
360
  // No cwd provided, use default
374
361
  effectiveCwd = undefined;
375
362
  }
376
-
377
- // Set up environment with NODE_PATH only for node commands
378
- const nodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
379
- const env = {
363
+
364
+ // Resolve actual working directory
365
+ const actualCwd = effectiveCwd || process.cwd();
366
+
367
+ // Set up environment with NODE_PATH for node commands
368
+ const builtinNodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
369
+
370
+ // Get user skills path from config (unified path: ~/.xagent/skills)
371
+ const { getConfigManager } = await import('./config.js');
372
+ const configManager = getConfigManager();
373
+ const userSkillsPath = configManager.getUserSkillsPath();
374
+
375
+ // Skill deps path: ~/.xagent/skills/{skillName}/node_modules
376
+ const builtinDepsPath = userSkillsPath ? path.join(userSkillsPath, 'builtin-deps') : null;
377
+
378
+ // Determine which node_modules to use
379
+ let skillNodeModulesPath: string | null = null;
380
+
381
+ // Priority 1: skillPath parameter (workspace scenario - LLM works in workspace, not skill dir)
382
+ if (skillPath) {
383
+ if (skillPath.includes('/builtin-deps/')) {
384
+ // Skill with deps in builtin-deps directory
385
+ const match = skillPath.match(/\/builtin-deps\/([^/]+)/);
386
+ if (match) {
387
+ skillNodeModulesPath = path.join(builtinDepsPath!, match[1], 'node_modules');
388
+ }
389
+ } else {
390
+ // Regular skill
391
+ skillNodeModulesPath = path.join(skillPath, 'node_modules');
392
+ }
393
+ }
394
+ // Priority 2: Check if we're inside a skill directory
395
+ else if (userSkillsPath && userSkillsPath.trim() && actualCwd.startsWith(userSkillsPath)) {
396
+ const relativePath = actualCwd.substring(userSkillsPath.length);
397
+ const pathParts = relativePath.split(path.sep).filter(Boolean);
398
+
399
+ if (pathParts.length > 0) {
400
+ if (pathParts[0] === 'builtin-deps' && pathParts.length > 1) {
401
+ // Skill with local deps in builtin-deps
402
+ const skillName = pathParts[1];
403
+ skillNodeModulesPath = path.join(builtinDepsPath!, skillName, 'node_modules');
404
+ } else {
405
+ // Regular skill
406
+ const skillName = pathParts[0];
407
+ const skillRoot = path.join(userSkillsPath, skillName);
408
+ try {
409
+ const skillMdPath = path.join(skillRoot, 'SKILL.md');
410
+ await fs.access(skillMdPath);
411
+ skillNodeModulesPath = path.join(skillRoot, 'node_modules');
412
+ } catch {
413
+ // Not a skill directory, skip
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ // Build NODE_PATH - skill's node_modules takes precedence (last-wins)
420
+ let nodePath: string;
421
+ if (skillNodeModulesPath) {
422
+ nodePath = `${skillNodeModulesPath}${path.delimiter}${builtinNodeModulesPath}`;
423
+ } else {
424
+ nodePath = builtinNodeModulesPath;
425
+ }
426
+
427
+ const env: Record<string, string> = {
380
428
  ...process.env,
381
- NODE_PATH: nodeModulesPath
429
+ NODE_PATH: nodePath
382
430
  };
383
-
384
- // Only add NODE_PATH prefix for node commands
385
- const isNodeCommand = /\bnode\b/.test(command);
386
- const finalCommand = isNodeCommand
387
- ? `set NODE_PATH=${nodeModulesPath} && ${command}`
388
- : command;
389
-
431
+
432
+ // Handle npm install commands
433
+ const isNpmInstall = /\bnpm\s+install\b/i.test(command);
434
+ let finalCommand = command;
435
+
436
+ if (isNpmInstall && skillNodeModulesPath) {
437
+ // Install to skill's own node_modules
438
+ await fs.mkdir(skillNodeModulesPath, { recursive: true }).catch(() => {});
439
+ finalCommand = command.replace(/\bnpm\s+install\b/i, `npm install --prefix "${skillNodeModulesPath}"`);
440
+ }
441
+
442
+ // Get shell configuration (Windows Git Bash detection, etc.)
443
+ const { shell, args } = getShellConfig();
444
+
445
+ // Set up cross-platform encoding environment for command execution
446
+ if (process.platform === 'win32') {
447
+ // Windows: set code page to UTF-8 and ensure console output encoding
448
+ // chcp 65001 sets the console code page to UTF-8
449
+ // Use *>$null to suppress output (PowerShell-style, not CMD-style)
450
+ finalCommand = `chcp 65001 *>$null; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ${finalCommand}`;
451
+ } else {
452
+ // Unix/macOS: set locale to UTF-8 for proper encoding handling
453
+ finalCommand = `export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8; ${finalCommand}`;
454
+ }
455
+
456
+ const shellArgs = [...args, quoteShellCommand(finalCommand)];
457
+
390
458
  try {
391
459
  if (run_in_bg) {
392
460
  const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
393
-
394
- const childProcess = spawn(finalCommand, {
461
+
462
+ const spawnOptions: any = {
395
463
  cwd: effectiveCwd || process.cwd(),
396
- shell: true,
397
- detached: true,
398
- env
399
- });
400
-
464
+ env,
465
+ stdio: ['pipe', 'pipe', 'pipe'],
466
+ };
467
+
468
+ // On Windows, don't use detached mode for PowerShell as it breaks output piping
469
+ if (process.platform !== 'win32') {
470
+ spawnOptions.detached = true;
471
+ }
472
+
473
+ const childProcess = spawn(shell, shellArgs, spawnOptions);
474
+
401
475
  const output: string[] = [];
402
-
476
+
403
477
  childProcess.stdout?.on('data', (data: Buffer) => {
404
478
  const text = data.toString();
405
479
  output.push(text);
406
480
  });
407
-
481
+
408
482
  childProcess.stderr?.on('data', (data: Buffer) => {
409
483
  const text = data.toString();
410
484
  output.push(text);
411
485
  });
412
-
486
+
413
487
  childProcess.on('close', (code: number) => {
414
- console.log(`Background task ${taskId} exited with code ${code}`);
488
+ // Silent cleanup - don't log to avoid noise during normal operation
489
+ // Note: On Windows with PowerShell, the shell process exits after
490
+ // the command completes
415
491
  });
416
-
492
+
417
493
  const toolRegistry = getToolRegistry();
418
494
  (toolRegistry as any).addBackgroundTask(taskId, {
419
495
  process: childProcess,
420
496
  startTime: Date.now(),
421
- output
497
+ output,
422
498
  });
423
-
499
+
424
500
  return {
425
501
  stdout: '',
426
502
  stderr: '',
427
503
  exitCode: 0,
428
- taskId
504
+ taskId,
429
505
  };
430
506
  } else {
431
- const { stdout, stderr } = await execAsync(finalCommand, {
507
+ // Execute command with spawn for better control
508
+ const result = await this.spawnWithTimeout(shell, shellArgs, {
432
509
  cwd: effectiveCwd || process.cwd(),
433
- maxBuffer: 1024 * 1024 * 10,
434
- timeout: timeout * 1000,
435
- env
510
+ env,
511
+ timeout,
436
512
  });
437
513
 
514
+ // Apply truncation to stdout and stderr separately
515
+ const stdoutResult = truncateTail(result.stdout);
516
+ const stderrResult = truncateTail(result.stderr);
517
+
518
+ let stdout = stdoutResult.content;
519
+ let stderr = stderrResult.content;
520
+ let truncationNotice = '';
521
+
522
+ if (stdoutResult.truncated) {
523
+ truncationNotice += buildTruncationNotice(stdoutResult) + '\n';
524
+ }
525
+ if (stderrResult.truncated) {
526
+ truncationNotice += buildTruncationNotice(stderrResult) + '\n';
527
+ }
528
+
438
529
  return {
439
530
  stdout,
440
531
  stderr,
441
- exitCode: 0
532
+ exitCode: result.exitCode,
533
+ truncated: stdoutResult.truncated || stderrResult.truncated,
534
+ truncationNotice: truncationNotice || undefined,
442
535
  };
443
536
  }
444
537
  } catch (error: any) {
538
+ // Check if this was a timeout
539
+ if (error.message === 'timeout') {
540
+ return {
541
+ stdout: '',
542
+ stderr: 'Command timed out',
543
+ exitCode: -1,
544
+ truncated: false,
545
+ };
546
+ }
445
547
  return {
446
548
  stdout: error.stdout || '',
447
549
  stderr: error.stderr || error.message,
448
- exitCode: error.code || 1
550
+ exitCode: error.code || 1,
449
551
  };
450
552
  }
451
553
  }
554
+
555
+ /**
556
+ * Execute a command with timeout support and proper process termination.
557
+ */
558
+ private spawnWithTimeout(
559
+ shell: string,
560
+ args: string[],
561
+ options: { cwd: string; env: NodeJS.ProcessEnv; timeout: number }
562
+ ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
563
+ return new Promise((resolve, reject) => {
564
+ const { cwd, env, timeout } = options;
565
+ let timedOut = false;
566
+ let timeoutHandle: NodeJS.Timeout | undefined;
567
+
568
+ const spawnOptions: any = {
569
+ cwd,
570
+ env,
571
+ stdio: ['pipe', 'pipe', 'pipe'],
572
+ };
573
+
574
+ // On Windows, don't use detached mode for PowerShell as it breaks output piping
575
+ if (process.platform !== 'win32') {
576
+ spawnOptions.detached = true;
577
+ }
578
+
579
+ const child = spawn(shell, args, spawnOptions);
580
+
581
+ const stdoutChunks: Buffer[] = [];
582
+ const stderrChunks: Buffer[] = [];
583
+
584
+ // Set timeout if provided
585
+ if (timeout > 0) {
586
+ timeoutHandle = setTimeout(() => {
587
+ timedOut = true;
588
+ if (child.pid) {
589
+ killProcessTree(child.pid);
590
+ }
591
+ }, timeout * 1000);
592
+ }
593
+
594
+ // Stream stdout
595
+ child.stdout?.on('data', (data: Buffer) => {
596
+ stdoutChunks.push(data);
597
+ });
598
+
599
+ // Stream stderr
600
+ child.stderr?.on('data', (data: Buffer) => {
601
+ stderrChunks.push(data);
602
+ });
603
+
604
+ // Handle process exit
605
+ child.on('close', (code: number) => {
606
+ if (timeoutHandle) clearTimeout(timeoutHandle);
607
+
608
+ if (timedOut) {
609
+ reject(new Error('timeout'));
610
+ return;
611
+ }
612
+
613
+ resolve({
614
+ stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
615
+ stderr: Buffer.concat(stderrChunks).toString('utf-8'),
616
+ exitCode: code ?? -1,
617
+ });
618
+ });
619
+
620
+ // Handle spawn errors
621
+ child.on('error', (err) => {
622
+ if (timeoutHandle) clearTimeout(timeoutHandle);
623
+ reject(err);
624
+ });
625
+ });
626
+ }
452
627
  }
453
628
 
454
629
  export class ListDirectoryTool implements Tool {
@@ -481,27 +656,32 @@ export class ListDirectoryTool implements Tool {
481
656
  - Results are absolute paths
482
657
  - Ignores node_modules and .git by default
483
658
  - Combine with Read to examine file contents`;
484
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
659
+ allowedModes = [
660
+ ExecutionMode.YOLO,
661
+ ExecutionMode.ACCEPT_EDITS,
662
+ ExecutionMode.PLAN,
663
+ ExecutionMode.SMART,
664
+ ];
485
665
 
486
666
  async execute(params: { path?: string; recursive?: boolean }): Promise<string[]> {
487
667
  const { path: dirPath = '.', recursive = false } = params;
488
-
668
+
489
669
  try {
490
670
  const absolutePath = path.resolve(dirPath);
491
-
671
+
492
672
  const stats = await fs.stat(absolutePath).catch(() => null);
493
673
  if (!stats || !stats.isDirectory()) {
494
674
  throw new Error(`Directory does not exist: ${dirPath}`);
495
675
  }
496
-
676
+
497
677
  const pattern = recursive ? '**/*' : '*';
498
678
  const files = await glob(pattern, {
499
679
  cwd: absolutePath,
500
680
  nodir: false,
501
- ignore: ['node_modules/**', '.git/**']
681
+ ignore: ['node_modules/**', '.git/**'],
502
682
  });
503
683
 
504
- return files.map(file => path.join(absolutePath, file));
684
+ return files.map((file) => path.join(absolutePath, file));
505
685
  } catch (error: any) {
506
686
  throw new Error(`Failed to list directory: ${error.message}`);
507
687
  }
@@ -519,7 +699,7 @@ export interface SearchFilesResult {
519
699
 
520
700
  export class SearchFilesTool implements Tool {
521
701
  name = 'SearchFiles';
522
- description = `Search for files matching a glob pattern. This is your PRIMARY tool for finding files by name or extension.
702
+ description = `Search for files matching a glob pattern using fd. This is your PRIMARY tool for finding files by name or extension.
523
703
 
524
704
  # When to Use
525
705
  - Finding all files of a certain type (*.ts, *.json, *.md)
@@ -554,17 +734,37 @@ export class SearchFilesTool implements Tool {
554
734
  - Combine with path parameter to search specific directories
555
735
  - Use limit parameter to avoid huge result sets
556
736
  - Results are file paths, not content (use Grep on results if needed)`;
557
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
737
+ allowedModes = [
738
+ ExecutionMode.YOLO,
739
+ ExecutionMode.ACCEPT_EDITS,
740
+ ExecutionMode.PLAN,
741
+ ExecutionMode.SMART,
742
+ ];
558
743
 
559
- async execute(params: { pattern: string; path?: string; limit?: number }): Promise<SearchFilesResult> {
744
+ async execute(params: {
745
+ pattern: string;
746
+ path?: string;
747
+ limit?: number;
748
+ }): Promise<SearchFilesResult> {
560
749
  const { pattern, path: searchPath = '.', limit = 1000 } = params;
561
750
 
562
751
  try {
563
- const files = await glob(pattern, {
564
- cwd: searchPath,
565
- ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
752
+ const output = await fdFind({
753
+ pattern,
754
+ path: searchPath,
755
+ limit,
566
756
  });
567
757
 
758
+ if (output === 'No files found') {
759
+ return {
760
+ files: [],
761
+ total: 0,
762
+ truncated: false,
763
+ };
764
+ }
765
+
766
+ const files = output.split('\n').filter((line) => line.trim());
767
+
568
768
  const total = files.length;
569
769
  const truncated = total > limit;
570
770
  const result = truncated ? files.slice(0, limit) : files;
@@ -572,7 +772,7 @@ export class SearchFilesTool implements Tool {
572
772
  return {
573
773
  files: result,
574
774
  total,
575
- truncated
775
+ truncated,
576
776
  };
577
777
  } catch (error: any) {
578
778
  throw new Error(`Search failed: ${error.message}`);
@@ -608,7 +808,9 @@ export class DeleteFileTool implements Tool {
608
808
  - This action is irreversible - be certain before executing`;
609
809
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
610
810
 
611
- async execute(params: { filePath: string }): Promise<{ success: boolean; message: string; filePath: string }> {
811
+ async execute(params: {
812
+ filePath: string;
813
+ }): Promise<{ success: boolean; message: string; filePath: string }> {
612
814
  const { filePath } = params;
613
815
 
614
816
  try {
@@ -618,7 +820,7 @@ export class DeleteFileTool implements Tool {
618
820
  return {
619
821
  success: true,
620
822
  message: `Successfully deleted ${filePath}`,
621
- filePath
823
+ filePath,
622
824
  };
623
825
  } catch (error: any) {
624
826
  throw new Error(`Failed to delete file ${filePath}: ${error.message}`);
@@ -654,16 +856,19 @@ export class CreateDirectoryTool implements Tool {
654
856
  - Consider the overall project structure before creating`;
655
857
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
656
858
 
657
- async execute(params: { dirPath: string; recursive?: boolean }): Promise<{ success: boolean; message: string }> {
859
+ async execute(params: {
860
+ dirPath: string;
861
+ recursive?: boolean;
862
+ }): Promise<{ success: boolean; message: string }> {
658
863
  const { dirPath, recursive = true } = params;
659
-
864
+
660
865
  try {
661
866
  const absolutePath = path.resolve(dirPath);
662
867
  await fs.mkdir(absolutePath, { recursive });
663
-
868
+
664
869
  return {
665
870
  success: true,
666
- message: `Successfully created directory ${dirPath}`
871
+ message: `Successfully created directory ${dirPath}`,
667
872
  };
668
873
  } catch (error: any) {
669
874
  throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
@@ -672,165 +877,169 @@ export class CreateDirectoryTool implements Tool {
672
877
  }
673
878
 
674
879
  // 编辑工具辅助函数
675
- function detectLineEnding(content: string): "\r\n" | "\n" {
676
- const crlfIdx = content.indexOf("\r\n");
677
- const lfIdx = content.indexOf("\n");
678
- if (lfIdx === -1) return "\n";
679
- if (crlfIdx === -1) return "\n";
680
- return crlfIdx < lfIdx ? "\r\n" : "\n";
880
+ function detectLineEnding(content: string): '\r\n' | '\n' {
881
+ const crlfIdx = content.indexOf('\r\n');
882
+ const lfIdx = content.indexOf('\n');
883
+ if (lfIdx === -1) return '\n';
884
+ if (crlfIdx === -1) return '\n';
885
+ return crlfIdx < lfIdx ? '\r\n' : '\n';
681
886
  }
682
887
 
683
888
  function normalizeToLF(text: string): string {
684
- return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
889
+ return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
685
890
  }
686
891
 
687
- function restoreLineEndings(text: string, ending: "\r\n" | "\n"): string {
688
- return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
892
+ function restoreLineEndings(text: string, ending: '\r\n' | '\n'): string {
893
+ return ending === '\r\n' ? text.replace(/\n/g, '\r\n') : text;
689
894
  }
690
895
 
691
896
  function normalizeForFuzzyMatch(text: string): string {
692
- return (
693
- text
694
- .split("\n")
695
- .map((line) => line.trimEnd())
696
- .join("\n")
697
- .replace(/['‘’""]/g, "'")
698
- .replace(/["""]/g, '"')
699
- .replace(/[—–‑−]/g, "-")
700
- .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " ")
701
- );
897
+ return text
898
+ .split('\n')
899
+ .map((line) => line.trimEnd())
900
+ .join('\n')
901
+ .replace(/['‘’""]/g, "'")
902
+ .replace(/["""]/g, '"')
903
+ .replace(/[—–‑−]/g, '-')
904
+ .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, ' ');
702
905
  }
703
906
 
704
907
  interface FuzzyMatchResult {
705
- found: boolean;
706
- index: number;
707
- matchLength: number;
708
- usedFuzzyMatch: boolean;
709
- contentForReplacement: string;
908
+ found: boolean;
909
+ index: number;
910
+ matchLength: number;
911
+ usedFuzzyMatch: boolean;
912
+ contentForReplacement: string;
710
913
  }
711
914
 
712
915
  function fuzzyFindText(content: string, oldText: string): FuzzyMatchResult {
713
- const exactIndex = content.indexOf(oldText);
714
- if (exactIndex !== -1) {
715
- return {
716
- found: true,
717
- index: exactIndex,
718
- matchLength: oldText.length,
719
- usedFuzzyMatch: false,
720
- contentForReplacement: content,
721
- };
722
- }
723
-
724
- const fuzzyContent = normalizeForFuzzyMatch(content);
725
- const fuzzyOldText = normalizeForFuzzyMatch(oldText);
726
- const fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);
727
-
728
- if (fuzzyIndex === -1) {
729
- return {
730
- found: false,
731
- index: -1,
732
- matchLength: 0,
733
- usedFuzzyMatch: false,
734
- contentForReplacement: content,
735
- };
736
- }
737
-
738
- return {
739
- found: true,
740
- index: fuzzyIndex,
741
- matchLength: fuzzyOldText.length,
742
- usedFuzzyMatch: true,
743
- contentForReplacement: fuzzyContent,
744
- };
916
+ const exactIndex = content.indexOf(oldText);
917
+ if (exactIndex !== -1) {
918
+ return {
919
+ found: true,
920
+ index: exactIndex,
921
+ matchLength: oldText.length,
922
+ usedFuzzyMatch: false,
923
+ contentForReplacement: content,
924
+ };
925
+ }
926
+
927
+ const fuzzyContent = normalizeForFuzzyMatch(content);
928
+ const fuzzyOldText = normalizeForFuzzyMatch(oldText);
929
+ const fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);
930
+
931
+ if (fuzzyIndex === -1) {
932
+ return {
933
+ found: false,
934
+ index: -1,
935
+ matchLength: 0,
936
+ usedFuzzyMatch: false,
937
+ contentForReplacement: content,
938
+ };
939
+ }
940
+
941
+ return {
942
+ found: true,
943
+ index: fuzzyIndex,
944
+ matchLength: fuzzyOldText.length,
945
+ usedFuzzyMatch: true,
946
+ contentForReplacement: fuzzyContent,
947
+ };
745
948
  }
746
949
 
747
950
  function stripBom(content: string): { bom: string; text: string } {
748
- return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
951
+ return content.startsWith('\uFEFF')
952
+ ? { bom: '\uFEFF', text: content.slice(1) }
953
+ : { bom: '', text: content };
749
954
  }
750
955
 
751
- async function generateDiffString(oldContent: string, newContent: string, contextLines = 4): Promise<{ diff: string; firstChangedLine: number | undefined }> {
752
- const diffModule = await import("diff");
753
- const parts = diffModule.diffLines(oldContent, newContent);
754
- const output: string[] = [];
755
-
756
- const oldLines = oldContent.split("\n");
757
- const newLines = newContent.split("\n");
758
- const maxLineNum = Math.max(oldLines.length, newLines.length);
759
- const lineNumWidth = String(maxLineNum).length;
760
-
761
- let oldLineNum = 1;
762
- let newLineNum = 1;
763
- let lastWasChange = false;
764
- let firstChangedLine: number | undefined;
765
-
766
- for (let i = 0; i < parts.length; i++) {
767
- const part = parts[i];
768
- const raw = part.value.split("\n");
769
- if (raw[raw.length - 1] === "") {
770
- raw.pop();
771
- }
772
-
773
- if (part.added || part.removed) {
774
- if (firstChangedLine === undefined) {
775
- firstChangedLine = newLineNum;
776
- }
777
-
778
- for (const line of raw) {
779
- if (part.added) {
780
- const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
781
- output.push(`+${lineNum} ${line}`);
782
- newLineNum++;
783
- } else {
784
- const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
785
- output.push(`-${lineNum} ${line}`);
786
- oldLineNum++;
787
- }
788
- }
789
- lastWasChange = true;
790
- } else {
791
- const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
792
-
793
- if (lastWasChange || nextPartIsChange) {
794
- let linesToShow = raw;
795
- let skipStart = 0;
796
- let skipEnd = 0;
797
-
798
- if (!lastWasChange) {
799
- skipStart = Math.max(0, raw.length - contextLines);
800
- linesToShow = raw.slice(skipStart);
801
- }
802
-
803
- if (!nextPartIsChange && linesToShow.length > contextLines) {
804
- skipEnd = linesToShow.length - contextLines;
805
- linesToShow = linesToShow.slice(0, contextLines);
806
- }
807
-
808
- if (skipStart > 0) {
809
- output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
810
- oldLineNum += skipStart;
811
- newLineNum += skipStart;
812
- }
813
-
814
- for (const line of linesToShow) {
815
- const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
816
- output.push(` ${lineNum} ${line}`);
817
- oldLineNum++;
818
- newLineNum++;
819
- }
820
-
821
- if (skipEnd > 0) {
822
- output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
823
- }
824
- } else {
825
- oldLineNum += raw.length;
826
- newLineNum += raw.length;
827
- }
828
-
829
- lastWasChange = false;
830
- }
831
- }
832
-
833
- return { diff: output.join("\n"), firstChangedLine };
956
+ async function generateDiffString(
957
+ oldContent: string,
958
+ newContent: string,
959
+ contextLines = 4
960
+ ): Promise<{ diff: string; firstChangedLine: number | undefined }> {
961
+ const diffModule = await import('diff');
962
+ const parts = diffModule.diffLines(oldContent, newContent);
963
+ const output: string[] = [];
964
+
965
+ const oldLines = oldContent.split('\n');
966
+ const newLines = newContent.split('\n');
967
+ const maxLineNum = Math.max(oldLines.length, newLines.length);
968
+ const lineNumWidth = String(maxLineNum).length;
969
+
970
+ let oldLineNum = 1;
971
+ let newLineNum = 1;
972
+ let lastWasChange = false;
973
+ let firstChangedLine: number | undefined;
974
+
975
+ for (let i = 0; i < parts.length; i++) {
976
+ const part = parts[i];
977
+ const raw = part.value.split('\n');
978
+ if (raw[raw.length - 1] === '') {
979
+ raw.pop();
980
+ }
981
+
982
+ if (part.added || part.removed) {
983
+ if (firstChangedLine === undefined) {
984
+ firstChangedLine = newLineNum;
985
+ }
986
+
987
+ for (const line of raw) {
988
+ if (part.added) {
989
+ const lineNum = String(newLineNum).padStart(lineNumWidth, ' ');
990
+ output.push(`+${lineNum} ${line}`);
991
+ newLineNum++;
992
+ } else {
993
+ const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
994
+ output.push(`-${lineNum} ${line}`);
995
+ oldLineNum++;
996
+ }
997
+ }
998
+ lastWasChange = true;
999
+ } else {
1000
+ const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
1001
+
1002
+ if (lastWasChange || nextPartIsChange) {
1003
+ let linesToShow = raw;
1004
+ let skipStart = 0;
1005
+ let skipEnd = 0;
1006
+
1007
+ if (!lastWasChange) {
1008
+ skipStart = Math.max(0, raw.length - contextLines);
1009
+ linesToShow = raw.slice(skipStart);
1010
+ }
1011
+
1012
+ if (!nextPartIsChange && linesToShow.length > contextLines) {
1013
+ skipEnd = linesToShow.length - contextLines;
1014
+ linesToShow = linesToShow.slice(0, contextLines);
1015
+ }
1016
+
1017
+ if (skipStart > 0) {
1018
+ output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
1019
+ oldLineNum += skipStart;
1020
+ newLineNum += skipStart;
1021
+ }
1022
+
1023
+ for (const line of linesToShow) {
1024
+ const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
1025
+ output.push(` ${lineNum} ${line}`);
1026
+ oldLineNum++;
1027
+ newLineNum++;
1028
+ }
1029
+
1030
+ if (skipEnd > 0) {
1031
+ output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
1032
+ }
1033
+ } else {
1034
+ oldLineNum += raw.length;
1035
+ newLineNum += raw.length;
1036
+ }
1037
+
1038
+ lastWasChange = false;
1039
+ }
1040
+ }
1041
+
1042
+ return { diff: output.join('\n'), firstChangedLine };
834
1043
  }
835
1044
 
836
1045
  export class EditTool implements Tool {
@@ -888,10 +1097,10 @@ edit(
888
1097
  new_string: string;
889
1098
  }): Promise<{ success: boolean; message: string; diff?: string; firstChangedLine?: number }> {
890
1099
  const { file_path, instruction, old_string, new_string } = params;
891
-
1100
+
892
1101
  try {
893
1102
  const absolutePath = path.resolve(file_path);
894
-
1103
+
895
1104
  // Check if file exists
896
1105
  try {
897
1106
  await fs.access(absolutePath);
@@ -904,7 +1113,7 @@ edit(
904
1113
 
905
1114
  // Read the file
906
1115
  const buffer = await fs.readFile(absolutePath);
907
- const rawContent = buffer.toString("utf-8");
1116
+ const rawContent = buffer.toString('utf-8');
908
1117
 
909
1118
  // Strip BOM before matching
910
1119
  const { bom, text: content } = stripBom(rawContent);
@@ -952,7 +1161,7 @@ edit(
952
1161
  }
953
1162
 
954
1163
  const finalContent = bom + restoreLineEndings(newContent, originalEnding);
955
- await fs.writeFile(absolutePath, finalContent, "utf-8");
1164
+ await fs.writeFile(absolutePath, finalContent, 'utf-8');
956
1165
 
957
1166
  const diffResult = await generateDiffString(baseContent, newContent);
958
1167
 
@@ -1000,38 +1209,43 @@ export class WebSearchTool implements Tool {
1000
1209
  - Combine with web_fetch to get full content from relevant URLs
1001
1210
  - Use quotes for exact phrase matching
1002
1211
  - Consider adding context like year or version in query`;
1003
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1212
+ allowedModes = [
1213
+ ExecutionMode.YOLO,
1214
+ ExecutionMode.ACCEPT_EDITS,
1215
+ ExecutionMode.PLAN,
1216
+ ExecutionMode.SMART,
1217
+ ];
1004
1218
 
1005
1219
  async execute(params: { query: string }): Promise<{ results: any[]; message: string }> {
1006
1220
  const { query } = params;
1007
-
1221
+
1008
1222
  try {
1009
1223
  const configManager = await import('./config.js');
1010
1224
  const { getConfigManager } = configManager;
1011
1225
  const config = getConfigManager();
1012
-
1226
+
1013
1227
  const searchApiKey = config.get('searchApiKey');
1014
1228
  const baseUrl = config.get('baseUrl') || 'https://apis.xagent.cn/v1';
1015
-
1229
+
1016
1230
  if (!searchApiKey) {
1017
1231
  throw new Error('Search API key not configured. Please set searchApiKey in settings.');
1018
1232
  }
1019
-
1233
+
1020
1234
  const response = await axios.post(
1021
1235
  `${baseUrl}/search`,
1022
1236
  { query },
1023
1237
  {
1024
1238
  headers: {
1025
- 'Authorization': `Bearer ${searchApiKey}`,
1026
- 'Content-Type': 'application/json'
1239
+ Authorization: `Bearer ${searchApiKey}`,
1240
+ 'Content-Type': 'application/json',
1027
1241
  },
1028
- timeout: 30000
1242
+ timeout: 30000,
1029
1243
  }
1030
1244
  );
1031
-
1245
+
1032
1246
  return {
1033
1247
  results: response.data.results || [],
1034
- message: `Found ${response.data.results?.length || 0} results for "${query}"`
1248
+ message: `Found ${response.data.results?.length || 0} results for "${query}"`,
1035
1249
  };
1036
1250
  } catch (error: any) {
1037
1251
  throw new Error(`Web search failed: ${error.message}`);
@@ -1087,29 +1301,44 @@ Each task needs:
1087
1301
  - Don't batch multiple completions - update as you go
1088
1302
  - Keep task descriptions clear and actionable
1089
1303
  - Use appropriate priority levels to indicate urgency`;
1090
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1091
-
1092
- private todoList: Array<{ id: string; task: string; status: 'pending' | 'in_progress' | 'completed' | 'failed'; priority: 'high' | 'medium' | 'low' }> = [];
1304
+ allowedModes = [
1305
+ ExecutionMode.YOLO,
1306
+ ExecutionMode.ACCEPT_EDITS,
1307
+ ExecutionMode.PLAN,
1308
+ ExecutionMode.SMART,
1309
+ ];
1310
+
1311
+ private todoList: Array<{
1312
+ id: string;
1313
+ task: string;
1314
+ status: 'pending' | 'in_progress' | 'completed' | 'failed';
1315
+ priority: 'high' | 'medium' | 'low';
1316
+ }> = [];
1093
1317
 
1094
1318
  async execute(params: {
1095
- todos: Array<{ id: string; task: string; status: 'pending' | 'in_progress' | 'completed' | 'failed'; priority: 'high' | 'medium' | 'low' }>;
1319
+ todos: Array<{
1320
+ id: string;
1321
+ task: string;
1322
+ status: 'pending' | 'in_progress' | 'completed' | 'failed';
1323
+ priority: 'high' | 'medium' | 'low';
1324
+ }>;
1096
1325
  }): Promise<{ success: boolean; message: string; todos: any[] }> {
1097
1326
  const { todos } = params;
1098
-
1327
+
1099
1328
  try {
1100
1329
  this.todoList = todos;
1101
-
1330
+
1102
1331
  const summary = {
1103
- pending: todos.filter(t => t.status === 'pending').length,
1104
- in_progress: todos.filter(t => t.status === 'in_progress').length,
1105
- completed: todos.filter(t => t.status === 'completed').length,
1106
- failed: todos.filter(t => t.status === 'failed').length
1332
+ pending: todos.filter((t) => t.status === 'pending').length,
1333
+ in_progress: todos.filter((t) => t.status === 'in_progress').length,
1334
+ completed: todos.filter((t) => t.status === 'completed').length,
1335
+ failed: todos.filter((t) => t.status === 'failed').length,
1107
1336
  };
1108
-
1337
+
1109
1338
  return {
1110
1339
  success: true,
1111
1340
  message: `Updated todo list: ${summary.pending} pending, ${summary.in_progress} in progress, ${summary.completed} completed, ${summary.failed} failed`,
1112
- todos: this.todoList
1341
+ todos: this.todoList,
1113
1342
  };
1114
1343
  } catch (error: any) {
1115
1344
  throw new Error(`Failed to update todo list: ${error.message}`);
@@ -1142,7 +1371,12 @@ export class TodoReadTool implements Tool {
1142
1371
  # Best Practices
1143
1372
  - Use todo_write to modify the list, not todo_read
1144
1373
  - Check todo_read after todo_write to verify updates`;
1145
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1374
+ allowedModes = [
1375
+ ExecutionMode.YOLO,
1376
+ ExecutionMode.ACCEPT_EDITS,
1377
+ ExecutionMode.PLAN,
1378
+ ExecutionMode.SMART,
1379
+ ];
1146
1380
 
1147
1381
  private todoWriteTool: TodoWriteTool;
1148
1382
 
@@ -1153,18 +1387,18 @@ export class TodoReadTool implements Tool {
1153
1387
  async execute(): Promise<{ todos: any[]; summary: any }> {
1154
1388
  try {
1155
1389
  const todos = this.todoWriteTool.getTodos();
1156
-
1390
+
1157
1391
  const summary = {
1158
1392
  total: todos.length,
1159
- pending: todos.filter(t => t.status === 'pending').length,
1160
- in_progress: todos.filter(t => t.status === 'in_progress').length,
1161
- completed: todos.filter(t => t.status === 'completed').length,
1162
- failed: todos.filter(t => t.status === 'failed').length
1393
+ pending: todos.filter((t) => t.status === 'pending').length,
1394
+ in_progress: todos.filter((t) => t.status === 'in_progress').length,
1395
+ completed: todos.filter((t) => t.status === 'completed').length,
1396
+ failed: todos.filter((t) => t.status === 'failed').length,
1163
1397
  };
1164
-
1398
+
1165
1399
  return {
1166
1400
  todos,
1167
- summary
1401
+ summary,
1168
1402
  };
1169
1403
  } catch (error: any) {
1170
1404
  throw new Error(`Failed to read todo list: ${error.message}`);
@@ -1175,7 +1409,15 @@ export class TodoReadTool implements Tool {
1175
1409
  export interface SubAgentTask {
1176
1410
  description: string;
1177
1411
  prompt: string;
1178
- subagent_type: 'general-purpose' | 'plan-agent' | 'explore-agent' | 'frontend-tester' | 'code-reviewer' | 'frontend-developer' | 'backend-developer' | 'gui-subagent';
1412
+ subagent_type:
1413
+ | 'general-purpose'
1414
+ | 'plan-agent'
1415
+ | 'explore-agent'
1416
+ | 'frontend-tester'
1417
+ | 'code-reviewer'
1418
+ | 'frontend-developer'
1419
+ | 'backend-developer'
1420
+ | 'gui-subagent';
1179
1421
  useContext?: boolean;
1180
1422
  outputFormat?: string;
1181
1423
  constraints?: string[];
@@ -1225,39 +1467,50 @@ export class TaskTool implements Tool {
1225
1467
  - Include relevant context (file paths, requirements, constraints)
1226
1468
  - Set appropriate executionMode if needed
1227
1469
  - For parallel execution, ensure tasks are truly independent`;
1228
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1229
-
1230
- async execute(params: {
1231
- description: string;
1232
- prompt?: string;
1233
- query?: string; // Support both prompt and query (tool definition uses query)
1234
- subagent_type?: 'general-purpose' | 'plan-agent' | 'explore-agent' | 'frontend-tester' | 'code-reviewer' | 'frontend-developer' | 'backend-developer' | 'gui-subagent';
1235
- agents?: SubAgentTask[];
1236
- useContext?: boolean;
1237
- outputFormat?: string;
1238
- constraints?: string[];
1239
- executionMode?: ExecutionMode;
1240
- parallel?: boolean;
1241
- }, _executionMode?: ExecutionMode): Promise<{ success: boolean; message: string; result?: any }> {
1470
+ allowedModes = [
1471
+ ExecutionMode.YOLO,
1472
+ ExecutionMode.ACCEPT_EDITS,
1473
+ ExecutionMode.PLAN,
1474
+ ExecutionMode.SMART,
1475
+ ];
1476
+
1477
+ async execute(
1478
+ params: {
1479
+ description: string;
1480
+ prompt?: string;
1481
+ query?: string; // Support both prompt and query (tool definition uses query)
1482
+ subagent_type?:
1483
+ | 'general-purpose'
1484
+ | 'plan-agent'
1485
+ | 'explore-agent'
1486
+ | 'frontend-tester'
1487
+ | 'code-reviewer'
1488
+ | 'frontend-developer'
1489
+ | 'backend-developer'
1490
+ | 'gui-subagent';
1491
+ agents?: SubAgentTask[];
1492
+ useContext?: boolean;
1493
+ outputFormat?: string;
1494
+ constraints?: string[];
1495
+ executionMode?: ExecutionMode;
1496
+ parallel?: boolean;
1497
+ },
1498
+ _executionMode?: ExecutionMode
1499
+ ): Promise<{ success: boolean; message: string; result?: any }> {
1242
1500
  const mode = params.executionMode || _executionMode || ExecutionMode.YOLO;
1243
-
1501
+
1244
1502
  try {
1245
1503
  const { getAgentManager } = await import('./agents.js');
1246
1504
  const agentManager = getAgentManager(process.cwd());
1247
-
1505
+
1248
1506
  const { getConfigManager } = await import('./config.js');
1249
1507
  const config = getConfigManager();
1250
-
1251
- const { AIClient } = await import('./ai-client.js');
1252
- const aiClient = new AIClient({
1253
- type: AuthType.OPENAI_COMPATIBLE,
1254
- apiKey: config.get('apiKey'),
1255
- baseUrl: config.get('baseUrl'),
1256
- modelName: config.get('modelName') || 'Qwen3-Coder'
1257
- });
1258
-
1508
+
1509
+ const authConfig = config.getAuthConfig();
1510
+ const aiClient = createAIClient(authConfig);
1511
+
1259
1512
  const toolRegistry = getToolRegistry();
1260
-
1513
+
1261
1514
  if (params.agents && params.agents.length > 0) {
1262
1515
  return await this.executeParallelAgents(
1263
1516
  params.agents,
@@ -1268,7 +1521,7 @@ export class TaskTool implements Tool {
1268
1521
  aiClient
1269
1522
  );
1270
1523
  }
1271
-
1524
+
1272
1525
  if (!params.subagent_type) {
1273
1526
  throw new Error('subagent_type is required for Task tool');
1274
1527
  }
@@ -1276,13 +1529,16 @@ export class TaskTool implements Tool {
1276
1529
  // Support both 'prompt' and 'query' parameter names (tool definition uses 'query')
1277
1530
  const prompt = params.prompt || params.query;
1278
1531
  if (!prompt) {
1279
- throw new Error('Task query/prompt is required. Received params: ' + JSON.stringify({
1280
- subagent_type: params.subagent_type,
1281
- prompt: params.prompt,
1282
- query: params.query,
1283
- description: params.description,
1284
- agents: params.agents?.length
1285
- }));
1532
+ throw new Error(
1533
+ 'Task query/prompt is required. Received params: ' +
1534
+ JSON.stringify({
1535
+ subagent_type: params.subagent_type,
1536
+ prompt: params.prompt,
1537
+ query: params.query,
1538
+ description: params.description,
1539
+ agents: params.agents?.length,
1540
+ })
1541
+ );
1286
1542
  }
1287
1543
 
1288
1544
  const result = await this.executeSingleAgent(
@@ -1297,7 +1553,7 @@ export class TaskTool implements Tool {
1297
1553
  aiClient,
1298
1554
  config
1299
1555
  );
1300
-
1556
+
1301
1557
  return result;
1302
1558
  } catch (error: any) {
1303
1559
  throw new Error(`Task execution failed: ${error.message}`);
@@ -1308,16 +1564,27 @@ export class TaskTool implements Tool {
1308
1564
  * Create unified VLM caller
1309
1565
  * Uses remote VLM if remoteAIClient is provided, otherwise uses local VLM
1310
1566
  * Both modes receive full messages array for consistent behavior
1567
+ * @param remoteAIClient - Remote AI client for VLM calls
1568
+ * @param taskId - Task identifier for backend tracking
1569
+ * @param localConfig - Local VLM configuration
1570
+ * @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
1571
+ * @param signal - Abort signal for cancellation
1311
1572
  */
1312
1573
  private createRemoteVlmCaller(
1313
1574
  remoteAIClient: any,
1314
1575
  taskId: string | null,
1315
1576
  localConfig: { baseUrl: string; apiKey: string; modelName: string },
1577
+ isFirstVlmCallRef: { current: boolean },
1316
1578
  signal?: AbortSignal
1317
- ): (messages: any[], systemPrompt: string) => Promise<string> {
1579
+ ): (
1580
+ messages: any[],
1581
+ systemPrompt: string,
1582
+ taskId: string,
1583
+ isFirstVlmCallRef: { current: boolean }
1584
+ ) => Promise<string> {
1318
1585
  // Remote mode: use RemoteAIClient
1319
1586
  if (remoteAIClient) {
1320
- return this.createRemoteVLMCaller(remoteAIClient, taskId, signal);
1587
+ return this.createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal);
1321
1588
  }
1322
1589
 
1323
1590
  // Local mode: use local API
@@ -1327,11 +1594,39 @@ export class TaskTool implements Tool {
1327
1594
  /**
1328
1595
  * Create remote VLM caller using RemoteAIClient
1329
1596
  * Now receives full messages array for consistent behavior with local mode
1597
+ * @param remoteAIClient - Remote AI client
1598
+ * @param taskId - Task identifier for backend tracking
1599
+ * @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
1600
+ * @param signal - Abort signal for cancellation
1330
1601
  */
1331
- private createRemoteVLMCaller(remoteAIClient: any, taskId: string | null, signal?: AbortSignal) {
1332
- return async (messages: any[], systemPrompt: string): Promise<string> => {
1602
+ private createRemoteVLMCaller(
1603
+ remoteAIClient: any,
1604
+ taskId: string | null,
1605
+ isFirstVlmCallRef: { current: boolean },
1606
+ signal?: AbortSignal
1607
+ ): (
1608
+ messages: any[],
1609
+ systemPrompt: string,
1610
+ taskId: string,
1611
+ isFirstVlmCallRef: { current: boolean }
1612
+ ) => Promise<string> {
1613
+ return async (
1614
+ messages: any[],
1615
+ systemPrompt: string,
1616
+ _taskId: string,
1617
+ _isFirstVlmCallRef: { current: boolean }
1618
+ ): Promise<string> => {
1333
1619
  try {
1334
- return await remoteAIClient.invokeVLM(messages, systemPrompt, { signal, taskId });
1620
+ // Use the ref to track first call status for the backend
1621
+ const status = isFirstVlmCallRef.current ? 'begin' : 'continue';
1622
+ const result = await remoteAIClient.invokeVLM(messages, systemPrompt, {
1623
+ signal,
1624
+ taskId,
1625
+ status,
1626
+ });
1627
+ // Update ref after call so subsequent calls use 'continue'
1628
+ isFirstVlmCallRef.current = false;
1629
+ return result;
1335
1630
  } catch (error: any) {
1336
1631
  throw new Error(`Remote VLM call failed: ${error.message}`);
1337
1632
  }
@@ -1368,7 +1663,7 @@ export class TaskTool implements Tool {
1368
1663
  method: 'POST',
1369
1664
  headers: {
1370
1665
  'Content-Type': 'application/json',
1371
- 'Authorization': `Bearer ${apiKey}`,
1666
+ Authorization: `Bearer ${apiKey}`,
1372
1667
  },
1373
1668
  body: JSON.stringify(requestBody),
1374
1669
  signal: abortSignal,
@@ -1379,7 +1674,9 @@ export class TaskTool implements Tool {
1379
1674
  throw new Error(`VLM API error: ${errorText}`);
1380
1675
  }
1381
1676
 
1382
- const result = await response.json() as { choices?: Array<{ message?: { content?: string } }> };
1677
+ const result = (await response.json()) as {
1678
+ choices?: Array<{ message?: { content?: string } }>;
1679
+ };
1383
1680
  return result.choices?.[0]?.message?.content || '';
1384
1681
  };
1385
1682
  }
@@ -1400,13 +1697,16 @@ export class TaskTool implements Tool {
1400
1697
  const indent = ' '.repeat(indentLevel);
1401
1698
 
1402
1699
  console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
1403
- console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
1700
+ console.log(
1701
+ `${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
1702
+ );
1404
1703
  console.log('');
1405
1704
 
1406
- // Get VLM configuration (used for local mode fallback)
1407
- const baseUrl = config.get('guiSubagentBaseUrl') || config.get('baseUrl') || '';
1408
- const apiKey = config.get('guiSubagentApiKey') || config.get('apiKey') || '';
1409
- const modelName = config.get('guiSubagentModel') || config.get('modelName') || '';
1705
+ // Get VLM configuration for local mode
1706
+ // NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
1707
+ const baseUrl = config.get('guiSubagentBaseUrl') || '';
1708
+ const apiKey = config.get('guiSubagentApiKey') || '';
1709
+ const modelName = config.get('guiSubagentModel') || '';
1410
1710
 
1411
1711
  // Determine mode: remote if remoteAIClient exists, otherwise local
1412
1712
  const isRemoteMode = !!remoteAIClient;
@@ -1416,11 +1716,11 @@ export class TaskTool implements Tool {
1416
1716
  console.log(`${indent}${colors.info(`${icons.brain} Using remote VLM service`)}`);
1417
1717
  } else {
1418
1718
  console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
1419
- // Local mode requires configuration check
1420
- if (!baseUrl) {
1719
+ // Local mode requires explicit VLM configuration
1720
+ if (!baseUrl || !apiKey || !modelName) {
1421
1721
  return {
1422
1722
  success: false,
1423
- message: `GUI task "${description}" failed: No valid API URL configured`
1723
+ message: `GUI task "${description}" failed: VLM not configured. Please run /model to configure Vision-Language Model first.`,
1424
1724
  };
1425
1725
  }
1426
1726
  console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
@@ -1440,8 +1740,16 @@ export class TaskTool implements Tool {
1440
1740
  }
1441
1741
  }
1442
1742
 
1743
+ // Track first VLM call for proper status management
1744
+ const isFirstVlmCallRef = { current: true };
1745
+
1443
1746
  // Create remoteVlmCaller using the unified method (handles both local and remote modes)
1444
- const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName });
1747
+ const remoteVlmCaller = this.createRemoteVlmCaller(
1748
+ remoteAIClient,
1749
+ taskId,
1750
+ { baseUrl, apiKey, modelName },
1751
+ isFirstVlmCallRef
1752
+ );
1445
1753
 
1446
1754
  // Set up stdin polling for ESC cancellation
1447
1755
  let rawModeEnabled = false;
@@ -1450,14 +1758,16 @@ export class TaskTool implements Tool {
1450
1758
  const logger = getLogger();
1451
1759
 
1452
1760
  const setupStdinPolling = () => {
1761
+ logger.debug(`[GUIAgent ESC] setupStdinPolling called, process.stdin.isTTY: ${process.stdin.isTTY}`);
1453
1762
  if (process.stdin.isTTY) {
1454
1763
  try {
1455
1764
  process.stdin.setRawMode(true);
1456
1765
  rawModeEnabled = true;
1457
1766
  process.stdin.resume();
1458
1767
  readline.emitKeypressEvents(process.stdin);
1459
- } catch (e) {
1460
- logger.debug(`[GUIAgent] Could not set raw mode: ${e}`);
1768
+ logger.debug(`[GUIAgent ESC] Raw mode enabled successfully`);
1769
+ } catch (e: any) {
1770
+ logger.debug(`[GUIAgent ESC] Could not set raw mode: ${e.message}`);
1461
1771
  }
1462
1772
 
1463
1773
  stdinPollingInterval = setInterval(() => {
@@ -1466,13 +1776,20 @@ export class TaskTool implements Tool {
1466
1776
  const chunk = process.stdin.read(1);
1467
1777
  if (chunk && chunk.length > 0) {
1468
1778
  const code = chunk[0];
1469
- if (code === 0x1B) { // ESC
1470
- logger.debug('[GUIAgent] ESC detected!');
1779
+ if (code === 0x1b) {
1780
+ // ESC
1781
+ logger.debug('[GUIAgent ESC Polling] ESC detected! Code: 0x1b');
1471
1782
  cancellationManager.cancel();
1783
+ } else {
1784
+ // Log other key codes for debugging
1785
+ logger.debug(`[GUIAgent ESC Polling] Key code: 0x${code.toString(16)}`);
1472
1786
  }
1473
1787
  }
1788
+ } else {
1789
+ logger.debug('[GUIAgent ESC Polling] rawModeEnabled is false');
1474
1790
  }
1475
- } catch (e) {
1791
+ } catch (e: any) {
1792
+ logger.debug(`[GUIAgent ESC Polling] Error: ${e.message}`);
1476
1793
  // Ignore polling errors
1477
1794
  }
1478
1795
  }, 10);
@@ -1494,7 +1811,9 @@ export class TaskTool implements Tool {
1494
1811
  cancellationManager.on('cancelled', cancelHandler);
1495
1812
 
1496
1813
  // Start polling for ESC
1814
+ logger.debug(`[GUIAgent ESC] About to call setupStdinPolling`);
1497
1815
  setupStdinPolling();
1816
+ logger.debug(`[GUIAgent ESC] setupStdinPolling called`);
1498
1817
 
1499
1818
  try {
1500
1819
  // Import and create GUIAgent
@@ -1504,11 +1823,14 @@ export class TaskTool implements Tool {
1504
1823
  model: !isRemoteMode ? modelName : undefined,
1505
1824
  modelBaseUrl: !isRemoteMode ? baseUrl : undefined,
1506
1825
  modelApiKey: !isRemoteMode ? apiKey : undefined,
1826
+ taskId: taskId || undefined,
1827
+ isFirstVlmCallRef,
1507
1828
  remoteVlmCaller,
1508
1829
  isLocalMode: !isRemoteMode,
1509
- maxLoopCount: 30,
1830
+ maxLoopCount: 100,
1510
1831
  loopIntervalInMs: 500,
1511
1832
  showAIDebugInfo: config.get('showAIDebugInfo') || false,
1833
+ indentLevel: indentLevel,
1512
1834
  });
1513
1835
 
1514
1836
  // Add constraints to prompt if any
@@ -1528,9 +1850,9 @@ export class TaskTool implements Tool {
1528
1850
  process.stdout.write('\n');
1529
1851
  return {
1530
1852
  success: true,
1531
- cancelled: true, // Mark as cancelled so main agent won't continue
1853
+ cancelled: true, // Mark as cancelled so main agent won't continue
1532
1854
  message: `GUI task "${description}" cancelled by user`,
1533
- result: 'Task cancelled'
1855
+ result: 'Task cancelled',
1534
1856
  };
1535
1857
  }
1536
1858
 
@@ -1541,26 +1863,85 @@ export class TaskTool implements Tool {
1541
1863
  process.stdout.write('\n');
1542
1864
 
1543
1865
  // Return result based on GUIAgent status
1866
+ // Always return all info except screenshots (base64) to avoid huge payload
1867
+ const conversationsWithoutScreenshots = result.conversations.map((conv: any) => ({
1868
+ ...conv,
1869
+ screenshotBase64: undefined, // Remove screenshots to avoid huge payload
1870
+ }));
1871
+
1544
1872
  if (result.status === 'end') {
1545
- const iterations = result.conversations.filter(c => c.from === 'human' && c.screenshotBase64).length;
1546
- console.log(`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`);
1873
+ const iterations = conversationsWithoutScreenshots.filter(
1874
+ (c: any) => c.from === 'human' && c.screenshotContext
1875
+ ).length;
1876
+ console.log(
1877
+ `${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`
1878
+ );
1547
1879
  return {
1548
1880
  success: true,
1549
1881
  message: `GUI task "${description}" completed`,
1550
- result: `Completed in ${iterations} iterations`
1882
+ result: {
1883
+ status: result.status,
1884
+ iterations,
1885
+ actions: conversationsWithoutScreenshots
1886
+ .filter((c: any) => c.from === 'assistant' && c.actionType)
1887
+ .map((c: any) => c.actionType),
1888
+ conversations: conversationsWithoutScreenshots,
1889
+ error: result.error,
1890
+ },
1891
+ };
1892
+ } else if (result.status === 'call_llm') {
1893
+ // Empty action or needs LLM decision - return to main agent with full context
1894
+ console.log(
1895
+ `${indent}${colors.warning(`${icons.warning} GUI agent returned to main agent for LLM decision`)}`
1896
+ );
1897
+ return {
1898
+ success: true,
1899
+ message: `GUI task "${description}" returned for LLM decision`,
1900
+ result: {
1901
+ status: result.status,
1902
+ iterations: conversationsWithoutScreenshots.filter(
1903
+ (c: any) => c.from === 'human' && c.screenshotContext
1904
+ ).length,
1905
+ actions: conversationsWithoutScreenshots
1906
+ .filter((c: any) => c.from === 'assistant' && c.actionType)
1907
+ .map((c: any) => c.actionType),
1908
+ conversations: conversationsWithoutScreenshots,
1909
+ error: result.error,
1910
+ },
1551
1911
  };
1552
1912
  } else if (result.status === 'user_stopped') {
1553
1913
  return {
1554
1914
  success: true,
1555
1915
  message: `GUI task "${description}" stopped by user`,
1556
- result: 'User stopped'
1916
+ result: {
1917
+ status: result.status,
1918
+ iterations: conversationsWithoutScreenshots.filter(
1919
+ (c: any) => c.from === 'human' && c.screenshotContext
1920
+ ).length,
1921
+ actions: conversationsWithoutScreenshots
1922
+ .filter((c: any) => c.from === 'assistant' && c.actionType)
1923
+ .map((c: any) => c.actionType),
1924
+ conversations: conversationsWithoutScreenshots,
1925
+ stopped: true,
1926
+ },
1557
1927
  };
1558
1928
  } else {
1559
1929
  // status is 'error' or other non-success status
1560
1930
  const errorMsg = result.error || 'Unknown error';
1561
1931
  return {
1562
1932
  success: false,
1563
- message: `GUI task "${description}" failed: ${errorMsg}`
1933
+ message: `GUI task "${description}" failed: ${errorMsg}`,
1934
+ result: {
1935
+ status: result.status,
1936
+ iterations: conversationsWithoutScreenshots.filter(
1937
+ (c: any) => c.from === 'human' && c.screenshotContext
1938
+ ).length,
1939
+ actions: conversationsWithoutScreenshots
1940
+ .filter((c: any) => c.from === 'assistant' && c.actionType)
1941
+ .map((c: any) => c.actionType),
1942
+ conversations: conversationsWithoutScreenshots,
1943
+ error: result.error,
1944
+ },
1564
1945
  };
1565
1946
  }
1566
1947
  } catch (error: any) {
@@ -1575,9 +1956,9 @@ export class TaskTool implements Tool {
1575
1956
  if (cancelled || cancellationManager.isOperationCancelled()) {
1576
1957
  return {
1577
1958
  success: true,
1578
- cancelled: true, // Mark as cancelled so main agent won't continue
1959
+ cancelled: true, // Mark as cancelled so main agent won't continue
1579
1960
  message: `GUI task "${description}" cancelled by user`,
1580
- result: 'Task cancelled'
1961
+ result: 'Task cancelled',
1581
1962
  };
1582
1963
  }
1583
1964
 
@@ -1585,14 +1966,14 @@ export class TaskTool implements Tool {
1585
1966
  return {
1586
1967
  success: true,
1587
1968
  message: `GUI task "${description}" cancelled by user`,
1588
- result: 'Task cancelled'
1969
+ result: 'Task cancelled',
1589
1970
  };
1590
1971
  }
1591
1972
 
1592
1973
  // Return failure without throwing - let the main agent handle it
1593
1974
  return {
1594
1975
  success: false,
1595
- message: `GUI task "${description}" failed: ${error.message}`
1976
+ message: `GUI task "${description}" failed: ${error.message}`,
1596
1977
  };
1597
1978
  }
1598
1979
  }
@@ -1646,7 +2027,7 @@ export class TaskTool implements Tool {
1646
2027
  let modelName = config.get('modelName') || 'Qwen3-Coder';
1647
2028
  let baseUrl = config.get('baseUrl') || 'https://apis.xagent.cn/v1';
1648
2029
  let apiKey = config.get('apiKey') || '';
1649
-
2030
+
1650
2031
  if (agent.model) {
1651
2032
  // If agent has a model field, it can be a model name or a config reference like 'guiSubagentModel'
1652
2033
  if (typeof agent.model === 'string' && agent.model.endsWith('Model')) {
@@ -1666,16 +2047,38 @@ export class TaskTool implements Tool {
1666
2047
  }
1667
2048
  }
1668
2049
 
1669
- // Create a new AIClient for this subagent with its specific model
1670
- const { AIClient: SubAgentAIClient } = await import('./ai-client.js');
1671
- const subAgentClient = new SubAgentAIClient({
1672
- type: AuthType.OPENAI_COMPATIBLE,
1673
- apiKey: apiKey,
1674
- baseUrl: baseUrl,
1675
- modelName: modelName,
1676
- showAIDebugInfo: config.get('showAIDebugInfo') || false
1677
- });
1678
-
2050
+ // Create AI client for this subagent
2051
+ let subAgentClient;
2052
+ let isRemoteMode = false;
2053
+ let mainTaskId: string | null = null;
2054
+ const authConfig = config.getAuthConfig();
2055
+
2056
+ if (authConfig.type === AuthType.OAUTH_XAGENT) {
2057
+ // Remote mode: try to reuse session's RemoteAIClient first
2058
+ const session = getSingletonSession();
2059
+ const existingClient = session?.getRemoteAIClient();
2060
+
2061
+ if (existingClient) {
2062
+ subAgentClient = existingClient;
2063
+ isRemoteMode = true;
2064
+ // Get the main taskId from session - subagent shares the same taskId as the parent task
2065
+ mainTaskId = session?.getTaskId() || null;
2066
+ } else {
2067
+ subAgentClient = createAIClient(authConfig);
2068
+ }
2069
+ } else {
2070
+ // Local mode: create client with subagent-specific model config
2071
+ const subAuthConfig = {
2072
+ ...authConfig,
2073
+ type: AuthType.OPENAI_COMPATIBLE,
2074
+ apiKey: apiKey,
2075
+ baseUrl: baseUrl,
2076
+ modelName: modelName,
2077
+ showAIDebugInfo: config.get('showAIDebugInfo') || false,
2078
+ };
2079
+ subAgentClient = createAIClient(subAuthConfig);
2080
+ }
2081
+
1679
2082
  const indent = ' '.repeat(indentLevel);
1680
2083
  const indentNext = ' '.repeat(indentLevel + 1);
1681
2084
  const agentName = agent.name || subagent_type;
@@ -1684,23 +2087,29 @@ export class TaskTool implements Tool {
1684
2087
  const executionHistory: Array<{
1685
2088
  tool: string;
1686
2089
  status: 'success' | 'error';
1687
- params: any; // 工具调用参数
1688
- result?: any; // 工具执行结果(成功时)
1689
- error?: string; // 错误信息(失败时)
2090
+ params: any; // 工具调用参数
2091
+ result?: any; // 工具执行结果(成功时)
2092
+ error?: string; // 错误信息(失败时)
1690
2093
  timestamp: string;
1691
2094
  }> = [];
1692
2095
 
1693
2096
  // Helper function to indent multi-line content
1694
2097
  const indentMultiline = (content: string, baseIndent: string): string => {
1695
- return content.split('\n').map(line => `${baseIndent} ${line}`).join('\n');
2098
+ return content
2099
+ .split('\n')
2100
+ .map((line) => `${baseIndent} ${line}`)
2101
+ .join('\n');
1696
2102
  };
1697
-
2103
+
1698
2104
  const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, mode, agent);
1699
- const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(agent.systemPrompt);
1700
-
1701
- const fullPrompt = constraints.length > 0
1702
- ? `${prompt}\n\nConstraints:\n${constraints.map(c => `- ${c}`).join('\n')}`
1703
- : prompt;
2105
+ const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(
2106
+ agent.systemPrompt
2107
+ );
2108
+
2109
+ const fullPrompt =
2110
+ constraints.length > 0
2111
+ ? `${prompt}\n\nConstraints:\n${constraints.map((c) => `- ${c}`).join('\n')}`
2112
+ : prompt;
1704
2113
 
1705
2114
  // Set up raw mode and stdin polling for ESC detection
1706
2115
  const cancellationManager = getCancellationManager();
@@ -1728,7 +2137,8 @@ export class TaskTool implements Tool {
1728
2137
  const chunk = process.stdin.read(1);
1729
2138
  if (chunk && chunk.length > 0) {
1730
2139
  const code = chunk[0];
1731
- if (code === 0x1B) { // ESC
2140
+ if (code === 0x1b) {
2141
+ // ESC
1732
2142
  logger.debug('[TaskTool] ESC detected via polling!');
1733
2143
  cancellationManager.cancel();
1734
2144
  }
@@ -1765,15 +2175,15 @@ export class TaskTool implements Tool {
1765
2175
  throw new Error('Operation cancelled by user');
1766
2176
  }
1767
2177
  };
1768
-
2178
+
1769
2179
  let messages: Message[] = [
1770
2180
  { role: 'system', content: enhancedSystemPrompt },
1771
- { role: 'user', content: fullPrompt }
2181
+ { role: 'user', content: fullPrompt },
1772
2182
  ];
1773
-
2183
+
1774
2184
  const availableTools = agentManager.getAvailableToolsForAgent(agent, mode);
1775
2185
  const allToolDefinitions = toolRegistry.getToolDefinitions();
1776
-
2186
+
1777
2187
  const toolDefinitions: ToolDefinition[] = availableTools.map((toolName: string) => {
1778
2188
  const fullDef = allToolDefinitions.find((def: any) => def.function.name === toolName);
1779
2189
  if (fullDef) {
@@ -1784,8 +2194,8 @@ export class TaskTool implements Tool {
1784
2194
  function: {
1785
2195
  name: toolName,
1786
2196
  description: `Tool: ${toolName}`,
1787
- parameters: { type: 'object', properties: {}, required: [] }
1788
- }
2197
+ parameters: { type: 'object', properties: {}, required: [] },
2198
+ },
1789
2199
  };
1790
2200
  });
1791
2201
 
@@ -1794,18 +2204,31 @@ export class TaskTool implements Tool {
1794
2204
 
1795
2205
  while (iteration < maxIterations) {
1796
2206
  iteration++;
1797
-
2207
+
1798
2208
  // Check for cancellation before each iteration
1799
2209
  checkCancellation();
1800
-
2210
+
2211
+ // Prepare chat options with taskId and model names for remote mode
2212
+ const chatOptions: any = {
2213
+ tools: toolDefinitions,
2214
+ temperature: 0.7,
2215
+ };
2216
+
2217
+ // Pass taskId, status, and model names for remote mode subagent calls
2218
+ // Subagent shares the same taskId as the main task
2219
+ if (isRemoteMode && mainTaskId) {
2220
+ chatOptions.taskId = mainTaskId;
2221
+ chatOptions.status = iteration === 1 ? 'begin' : 'continue';
2222
+ // Pass model names to ensure subagent uses the same models as main task
2223
+ chatOptions.llmModelName = config.get('remote_llmModelName');
2224
+ chatOptions.vlmModelName = config.get('remote_vlmModelName');
2225
+ }
2226
+
1801
2227
  // Use withCancellation to make API call cancellable
1802
- const result = await cancellationManager.withCancellation(
1803
- subAgentClient.chatCompletion(messages, {
1804
- tools: toolDefinitions,
1805
- temperature: 0.7
1806
- }),
2228
+ const result = (await cancellationManager.withCancellation(
2229
+ subAgentClient.chatCompletion(messages, chatOptions),
1807
2230
  `api-${subagent_type}-${iteration}`
1808
- ) as any;
2231
+ )) as any;
1809
2232
 
1810
2233
  // Check for cancellation after API call
1811
2234
  checkCancellation();
@@ -1827,8 +2250,8 @@ export class TaskTool implements Tool {
1827
2250
  hasValidContent = messageContent.trim() !== '';
1828
2251
  } else if (Array.isArray(messageContent)) {
1829
2252
  const textParts = messageContent
1830
- .filter(item => typeof item?.text === 'string' && item.text.trim() !== '')
1831
- .map(item => item.text);
2253
+ .filter((item) => typeof item?.text === 'string' && item.text.trim() !== '')
2254
+ .map((item) => item.text);
1832
2255
  contentStr = textParts.join('');
1833
2256
  hasValidContent = textParts.length > 0;
1834
2257
  } else {
@@ -1846,13 +2269,23 @@ export class TaskTool implements Tool {
1846
2269
  throw new Error(`Sub-agent ${subagent_type} response truncated due to length limits`);
1847
2270
  }
1848
2271
 
1849
- // Add assistant message to conversation
1850
- messages.push({ role: 'assistant', content: contentStr });
2272
+ // Add assistant message to conversation (必须包含 tool_calls,否则 tool_result 无法匹配)
2273
+ const assistantMessage: any = { role: 'assistant', content: contentStr };
2274
+ if (toolCalls && toolCalls.length > 0) {
2275
+ assistantMessage.tool_calls = toolCalls;
2276
+ }
2277
+ if (reasoningContent) {
2278
+ assistantMessage.reasoning_content = reasoningContent;
2279
+ }
2280
+ messages.push(assistantMessage as Message);
1851
2281
 
1852
2282
  // Display reasoning content if present
1853
2283
  if (reasoningContent) {
1854
2284
  console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
1855
- const truncatedReasoning = reasoningContent.length > 500 ? reasoningContent.substring(0, 500) + '...' : reasoningContent;
2285
+ const truncatedReasoning =
2286
+ reasoningContent.length > 500
2287
+ ? reasoningContent.substring(0, 500) + '...'
2288
+ : reasoningContent;
1856
2289
  const indentedReasoning = indentMultiline(truncatedReasoning, indent);
1857
2290
  console.log(`${indentedReasoning}\n`);
1858
2291
  }
@@ -1860,7 +2293,8 @@ export class TaskTool implements Tool {
1860
2293
  // Display assistant response (if there's any text content) with proper indentation
1861
2294
  if (contentStr) {
1862
2295
  console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
1863
- const truncatedContent = contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
2296
+ const truncatedContent =
2297
+ contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
1864
2298
  const indentedContent = indentMultiline(truncatedContent, indent);
1865
2299
  console.log(`${indentedContent}\n`);
1866
2300
  }
@@ -1882,7 +2316,7 @@ export class TaskTool implements Tool {
1882
2316
  try {
1883
2317
  // Check cancellation before tool execution
1884
2318
  checkCancellation();
1885
-
2319
+
1886
2320
  const toolResult: any = await cancellationManager.withCancellation(
1887
2321
  toolRegistry.execute(name, parsedParams, mode, indent),
1888
2322
  `subagent-${subagent_type}-${name}-${iteration}`
@@ -1892,8 +2326,10 @@ export class TaskTool implements Tool {
1892
2326
  const showToolDetails = config.get('showToolDetails') || false;
1893
2327
 
1894
2328
  // Prepare result preview for history
1895
- const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
1896
- const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
2329
+ const resultPreview =
2330
+ typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
2331
+ const truncatedPreview =
2332
+ resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
1897
2333
 
1898
2334
  // Special handling for different tools (consistent with session.ts display logic)
1899
2335
  const isTodoTool = name === 'todo_write' || name === 'todo_read';
@@ -1914,15 +2350,20 @@ export class TaskTool implements Tool {
1914
2350
  if (todos.length === 0) {
1915
2351
  console.log(`${indent} ${colors.textMuted('No tasks')}`);
1916
2352
  } else {
1917
- const statusConfig: Record<string, { icon: string; color: (text: string) => string; label: string }> = {
1918
- 'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1919
- 'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1920
- 'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
1921
- 'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
2353
+ const statusConfig: Record<
2354
+ string,
2355
+ { icon: string; color: (text: string) => string; label: string }
2356
+ > = {
2357
+ pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
2358
+ in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
2359
+ completed: { icon: icons.success, color: colors.success, label: 'Completed' },
2360
+ failed: { icon: icons.error, color: colors.error, label: 'Failed' },
1922
2361
  };
1923
2362
  for (const todo of todos) {
1924
2363
  const status = statusConfig[todo.status] || statusConfig['pending'];
1925
- console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
2364
+ console.log(
2365
+ `${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`
2366
+ );
1926
2367
  }
1927
2368
  }
1928
2369
  if (toolResult?.message) {
@@ -1933,7 +2374,10 @@ export class TaskTool implements Tool {
1933
2374
  // Display edit result with diff
1934
2375
  console.log('');
1935
2376
  const diffOutput = renderDiff(toolResult.diff);
1936
- const indentedDiff = diffOutput.split('\n').map(line => `${indent} ${line}`).join('\n');
2377
+ const indentedDiff = diffOutput
2378
+ .split('\n')
2379
+ .map((line) => `${indent} ${line}`)
2380
+ .join('\n');
1937
2381
  console.log(`${indentedDiff}\n`);
1938
2382
  } else if (hasFilePreview) {
1939
2383
  // Display new file content in preview style
@@ -1946,19 +2390,27 @@ export class TaskTool implements Tool {
1946
2390
  } else if (hasDeleteInfo) {
1947
2391
  // Display DeleteFile result
1948
2392
  console.log('');
1949
- console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
2393
+ console.log(
2394
+ `${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`
2395
+ );
1950
2396
  console.log('');
1951
2397
  } else if (showToolDetails) {
1952
2398
  // Show full result details
1953
2399
  const indentedPreview = indentMultiline(resultPreview, indent);
1954
- console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
2400
+ console.log(
2401
+ `${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`
2402
+ );
1955
2403
  } else if (toolResult && toolResult.success === false) {
1956
2404
  // Tool failed
1957
- console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
2405
+ console.log(
2406
+ `${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`
2407
+ );
1958
2408
  } else if (toolResult) {
1959
2409
  // Show brief preview by default
1960
2410
  const indentedPreview = indentMultiline(truncatedPreview, indent);
1961
- console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
2411
+ console.log(
2412
+ `${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`
2413
+ );
1962
2414
  } else {
1963
2415
  console.log(`${indent}${colors.textDim('(no result)')}\n`);
1964
2416
  }
@@ -1969,20 +2421,21 @@ export class TaskTool implements Tool {
1969
2421
  status: 'success',
1970
2422
  params: parsedParams,
1971
2423
  result: truncatedPreview,
1972
- timestamp: new Date().toISOString()
2424
+ timestamp: new Date().toISOString(),
1973
2425
  });
1974
2426
 
1975
2427
  messages.push({
1976
2428
  role: 'tool',
1977
2429
  content: JSON.stringify(toolResult),
1978
- tool_call_id: toolCall.id
2430
+ tool_call_id: toolCall.id,
1979
2431
  });
1980
2432
  } catch (error: any) {
1981
2433
  if (error.message === 'Operation cancelled by user') {
1982
2434
  console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
1983
2435
  cancellationManager.off('cancelled', cancelHandler);
1984
2436
  cleanupStdinPolling();
1985
- const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
2437
+ const summaryPreview =
2438
+ contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
1986
2439
  return {
1987
2440
  success: false,
1988
2441
  message: `Task "${description}" cancelled by user`,
@@ -1991,12 +2444,12 @@ export class TaskTool implements Tool {
1991
2444
  executionHistory: {
1992
2445
  totalIterations: iteration,
1993
2446
  toolsExecuted: executionHistory.length,
1994
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1995
- failedTools: executionHistory.filter(t => t.status === 'error').length,
2447
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2448
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
1996
2449
  history: executionHistory,
1997
- cancelled: true
1998
- }
1999
- }
2450
+ cancelled: true,
2451
+ },
2452
+ },
2000
2453
  };
2001
2454
  }
2002
2455
  console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error.message}\n`);
@@ -2007,13 +2460,13 @@ export class TaskTool implements Tool {
2007
2460
  status: 'error',
2008
2461
  params: parsedParams,
2009
2462
  error: error.message,
2010
- timestamp: new Date().toISOString()
2463
+ timestamp: new Date().toISOString(),
2011
2464
  });
2012
2465
 
2013
2466
  messages.push({
2014
2467
  role: 'tool',
2015
2468
  content: JSON.stringify({ error: error.message }),
2016
- tool_call_id: toolCall.id
2469
+ tool_call_id: toolCall.id,
2017
2470
  });
2018
2471
  }
2019
2472
  }
@@ -2025,7 +2478,8 @@ export class TaskTool implements Tool {
2025
2478
  cancellationManager.off('cancelled', cancelHandler);
2026
2479
  cleanupStdinPolling();
2027
2480
 
2028
- const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
2481
+ const summaryPreview =
2482
+ contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
2029
2483
  return {
2030
2484
  success: true,
2031
2485
  message: `Task "${description}" completed by ${subagent_type}`,
@@ -2034,25 +2488,27 @@ export class TaskTool implements Tool {
2034
2488
  executionHistory: {
2035
2489
  totalIterations: iteration,
2036
2490
  toolsExecuted: executionHistory.length,
2037
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
2038
- failedTools: executionHistory.filter(t => t.status === 'error').length,
2039
- history: executionHistory
2040
- }
2041
- }
2491
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2492
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
2493
+ history: executionHistory,
2494
+ },
2495
+ },
2042
2496
  };
2043
2497
  }
2044
2498
 
2045
2499
  // Max iterations reached - return accumulated results instead of throwing error
2046
2500
  // Get the last assistant message content
2047
- const lastAssistantMsg = messages.filter(m => m.role === 'assistant').pop();
2048
- const lastContentStr = typeof lastAssistantMsg?.content === 'string'
2049
- ? lastAssistantMsg.content
2050
- : JSON.stringify(lastAssistantMsg?.content || '');
2501
+ const lastAssistantMsg = messages.filter((m) => m.role === 'assistant').pop();
2502
+ const lastContentStr =
2503
+ typeof lastAssistantMsg?.content === 'string'
2504
+ ? lastAssistantMsg.content
2505
+ : JSON.stringify(lastAssistantMsg?.content || '');
2051
2506
 
2052
2507
  cancellationManager.off('cancelled', cancelHandler);
2053
2508
  cleanupStdinPolling();
2054
2509
 
2055
- const summaryPreview = lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
2510
+ const summaryPreview =
2511
+ lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
2056
2512
  return {
2057
2513
  success: true,
2058
2514
  message: `Task "${description}" completed (max iterations reached) by ${subagent_type}`,
@@ -2061,15 +2517,15 @@ export class TaskTool implements Tool {
2061
2517
  executionHistory: {
2062
2518
  totalIterations: iteration,
2063
2519
  toolsExecuted: executionHistory.length,
2064
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
2065
- failedTools: executionHistory.filter(t => t.status === 'error').length,
2520
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2521
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
2066
2522
  history: executionHistory,
2067
- maxIterationsReached: true
2068
- }
2069
- }
2523
+ maxIterationsReached: true,
2524
+ },
2525
+ },
2070
2526
  };
2071
2527
  }
2072
-
2528
+
2073
2529
  private async executeParallelAgents(
2074
2530
  agents: SubAgentTask[],
2075
2531
  description: string,
@@ -2105,7 +2561,8 @@ export class TaskTool implements Tool {
2105
2561
  const chunk = process.stdin.read(1);
2106
2562
  if (chunk && chunk.length > 0) {
2107
2563
  const code = chunk[0];
2108
- if (code === 0x1B) { // ESC
2564
+ if (code === 0x1b) {
2565
+ // ESC
2109
2566
  logger.debug('[ParallelAgents] ESC detected via polling!');
2110
2567
  cancellationManager.cancel();
2111
2568
  }
@@ -2135,7 +2592,9 @@ export class TaskTool implements Tool {
2135
2592
  };
2136
2593
  cancellationManager.on('cancelled', cancelHandler);
2137
2594
 
2138
- console.log(`\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`);
2595
+ console.log(
2596
+ `\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`
2597
+ );
2139
2598
 
2140
2599
  const startTime = Date.now();
2141
2600
 
@@ -2146,7 +2605,7 @@ export class TaskTool implements Tool {
2146
2605
  success: false,
2147
2606
  agent: agentTask.subagent_type,
2148
2607
  description: agentTask.description,
2149
- error: 'Operation cancelled by user'
2608
+ error: 'Operation cancelled by user',
2150
2609
  };
2151
2610
  }
2152
2611
 
@@ -2163,32 +2622,36 @@ export class TaskTool implements Tool {
2163
2622
  aiClient,
2164
2623
  indentLevel + 1
2165
2624
  );
2166
-
2625
+
2167
2626
  return {
2168
2627
  success: true,
2169
2628
  agent: agentTask.subagent_type,
2170
2629
  description: agentTask.description,
2171
- result: result.result
2630
+ result: result.result,
2172
2631
  };
2173
2632
  } catch (error: any) {
2174
2633
  return {
2175
2634
  success: false,
2176
2635
  agent: agentTask.subagent_type,
2177
2636
  description: agentTask.description,
2178
- error: error.message
2637
+ error: error.message,
2179
2638
  };
2180
2639
  }
2181
2640
  });
2182
-
2641
+
2183
2642
  const results = await Promise.all(agentPromises);
2184
-
2643
+
2185
2644
  const duration = Date.now() - startTime;
2186
-
2187
- const successfulAgents = results.filter(r => r.success);
2188
- const failedAgents = results.filter(r => !r.success);
2189
-
2190
- console.log(`${indent}${colors.success('✔')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`);
2191
- console.log(`${indent}${colors.info('')} Success: ${successfulAgents.length}/${agents.length} agents\n`);
2645
+
2646
+ const successfulAgents = results.filter((r) => r.success);
2647
+ const failedAgents = results.filter((r) => !r.success);
2648
+
2649
+ console.log(
2650
+ `${indent}${colors.success('')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`
2651
+ );
2652
+ console.log(
2653
+ `${indent}${colors.info('ℹ')} Success: ${successfulAgents.length}/${agents.length} agents\n`
2654
+ );
2192
2655
 
2193
2656
  if (failedAgents.length > 0) {
2194
2657
  console.log(`${indent}${colors.error('✖')} Failed agents:`);
@@ -2205,16 +2668,16 @@ export class TaskTool implements Tool {
2205
2668
  return {
2206
2669
  success: failedAgents.length === 0,
2207
2670
  message: `Parallel task "${description}" completed: ${successfulAgents.length}/${agents.length} successful`,
2208
- results: successfulAgents.map(r => ({
2671
+ results: successfulAgents.map((r) => ({
2209
2672
  agent: r.agent,
2210
2673
  description: r.description,
2211
- result: r.result
2674
+ result: r.result,
2212
2675
  })),
2213
- errors: failedAgents.map(r => ({
2676
+ errors: failedAgents.map((r) => ({
2214
2677
  agent: r.agent,
2215
2678
  description: r.description,
2216
- error: r.error
2217
- }))
2679
+ error: r.error,
2680
+ })),
2218
2681
  };
2219
2682
  }
2220
2683
  }
@@ -2247,34 +2710,39 @@ export class ReadBashOutputTool implements Tool {
2247
2710
  - Use appropriate poll_interval based on expected task duration
2248
2711
  - Check status to see if task is still running or completed
2249
2712
  - Combine with todo_write to track background task progress`;
2250
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2713
+ allowedModes = [
2714
+ ExecutionMode.YOLO,
2715
+ ExecutionMode.ACCEPT_EDITS,
2716
+ ExecutionMode.PLAN,
2717
+ ExecutionMode.SMART,
2718
+ ];
2251
2719
 
2252
2720
  async execute(params: {
2253
2721
  task_id: string;
2254
2722
  poll_interval?: number;
2255
2723
  }): Promise<{ taskId: string; output: string; status: string; duration: number }> {
2256
2724
  const { task_id, poll_interval = 10 } = params;
2257
-
2725
+
2258
2726
  try {
2259
2727
  const toolRegistry = getToolRegistry();
2260
2728
  const task = (toolRegistry as any).getBackgroundTask(task_id);
2261
-
2729
+
2262
2730
  if (!task) {
2263
2731
  throw new Error(`Task ${task_id} not found`);
2264
2732
  }
2265
-
2733
+
2266
2734
  const interval = Math.min(Math.max(poll_interval, 1), 120);
2267
- await new Promise(resolve => setTimeout(resolve, interval * 1000));
2268
-
2735
+ await new Promise((resolve) => setTimeout(resolve, interval * 1000));
2736
+
2269
2737
  const duration = Date.now() - task.startTime;
2270
2738
  const output = task.output.join('');
2271
2739
  const status = task.process.exitCode === null ? 'running' : 'completed';
2272
-
2740
+
2273
2741
  return {
2274
2742
  taskId: task_id,
2275
2743
  output,
2276
2744
  status,
2277
- duration: Math.floor(duration / 1000)
2745
+ duration: Math.floor(duration / 1000),
2278
2746
  };
2279
2747
  } catch (error: any) {
2280
2748
  throw new Error(`Failed to read bash output: ${error.message}`);
@@ -2310,36 +2778,43 @@ export class WebFetchTool implements Tool {
2310
2778
  - Use specific prompts to extract relevant information
2311
2779
  - Check if the page is accessible if you get errors
2312
2780
  - Large pages may be truncated due to size limits`;
2313
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2781
+ allowedModes = [
2782
+ ExecutionMode.YOLO,
2783
+ ExecutionMode.ACCEPT_EDITS,
2784
+ ExecutionMode.PLAN,
2785
+ ExecutionMode.SMART,
2786
+ ];
2314
2787
 
2315
- async execute(params: { prompt: string }): Promise<{ content: string; url: string; status: number }> {
2788
+ async execute(params: {
2789
+ prompt: string;
2790
+ }): Promise<{ content: string; url: string; status: number }> {
2316
2791
  const { prompt } = params;
2317
-
2792
+
2318
2793
  try {
2319
2794
  const urlMatch = prompt.match(/https?:\/\/[^\s]+/i);
2320
-
2795
+
2321
2796
  if (!urlMatch) {
2322
2797
  throw new Error('No URL found in prompt');
2323
2798
  }
2324
-
2799
+
2325
2800
  const url = urlMatch[0];
2326
-
2801
+
2327
2802
  const response = await axios.get(url, {
2328
2803
  timeout: 30000,
2329
2804
  maxContentLength: 10 * 1024 * 1024,
2330
- validateStatus: () => true
2805
+ validateStatus: () => true,
2331
2806
  });
2332
-
2807
+
2333
2808
  let content = response.data;
2334
-
2809
+
2335
2810
  if (typeof content === 'object') {
2336
2811
  content = JSON.stringify(content, null, 2);
2337
2812
  }
2338
-
2813
+
2339
2814
  return {
2340
2815
  content,
2341
2816
  url,
2342
- status: response.status
2817
+ status: response.status,
2343
2818
  };
2344
2819
  } catch (error: any) {
2345
2820
  throw new Error(`Failed to fetch URL: ${error.message}`);
@@ -2380,7 +2855,12 @@ export class AskUserQuestionTool implements Tool {
2380
2855
  - Provide options when possible for faster response
2381
2856
  - Use multiSelect=true when multiple answers are valid
2382
2857
  - Be clear and concise in question wording`;
2383
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2858
+ allowedModes = [
2859
+ ExecutionMode.YOLO,
2860
+ ExecutionMode.ACCEPT_EDITS,
2861
+ ExecutionMode.PLAN,
2862
+ ExecutionMode.SMART,
2863
+ ];
2384
2864
 
2385
2865
  async execute(params: {
2386
2866
  questions: Array<{
@@ -2391,40 +2871,32 @@ export class AskUserQuestionTool implements Tool {
2391
2871
  }>;
2392
2872
  }): Promise<{ answers: string[] }> {
2393
2873
  const { questions } = params;
2394
-
2874
+
2395
2875
  try {
2396
2876
  if (questions.length === 0 || questions.length > 4) {
2397
2877
  throw new Error('Must provide 1-4 questions');
2398
2878
  }
2399
-
2879
+
2400
2880
  const answers: string[] = [];
2401
-
2881
+
2402
2882
  for (const q of questions) {
2403
2883
  if (q.options && q.options.length > 0) {
2404
- const result = await inquirer.prompt([
2405
- {
2406
- type: q.multiSelect ? 'checkbox' : 'list',
2407
- name: 'answer',
2408
- message: q.question,
2409
- choices: q.options,
2410
- default: q.multiSelect ? [] : q.options[0]
2411
- }
2412
- ]);
2413
-
2414
- answers.push(Array.isArray(result.answer) ? result.answer.join(', ') : result.answer);
2884
+ const options = q.options.map((opt) => ({ value: opt, label: opt }));
2885
+ const result = await select({
2886
+ message: q.question,
2887
+ options,
2888
+ });
2889
+
2890
+ answers.push(result as string);
2415
2891
  } else {
2416
- const result = await inquirer.prompt([
2417
- {
2418
- type: 'input',
2419
- name: 'answer',
2420
- message: q.question
2421
- }
2422
- ]);
2423
-
2424
- answers.push(result.answer);
2892
+ const result = (await text({
2893
+ message: q.question,
2894
+ })) as string;
2895
+
2896
+ answers.push(result);
2425
2897
  }
2426
2898
  }
2427
-
2899
+
2428
2900
  return { answers };
2429
2901
  } catch (error: any) {
2430
2902
  throw new Error(`Failed to ask user questions: ${error.message}`);
@@ -2461,20 +2933,25 @@ export class SaveMemoryTool implements Tool {
2461
2933
  - Keep facts concise and specific
2462
2934
  - Remember project-specific conventions for consistency
2463
2935
  - This persists across sessions (global memory)`;
2464
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2936
+ allowedModes = [
2937
+ ExecutionMode.YOLO,
2938
+ ExecutionMode.ACCEPT_EDITS,
2939
+ ExecutionMode.PLAN,
2940
+ ExecutionMode.SMART,
2941
+ ];
2465
2942
 
2466
2943
  async execute(params: { fact: string }): Promise<{ success: boolean; message: string }> {
2467
2944
  const { fact } = params;
2468
-
2945
+
2469
2946
  try {
2470
2947
  const { getMemoryManager } = await import('./memory.js');
2471
2948
  const memoryManager = getMemoryManager(process.cwd());
2472
-
2949
+
2473
2950
  await memoryManager.saveMemory(fact, 'global');
2474
-
2951
+
2475
2952
  return {
2476
2953
  success: true,
2477
- message: `Successfully saved fact to memory`
2954
+ message: `Successfully saved fact to memory`,
2478
2955
  };
2479
2956
  } catch (error: any) {
2480
2957
  throw new Error(`Failed to save memory: ${error.message}`);
@@ -2510,16 +2987,23 @@ export class ExitPlanModeTool implements Tool {
2510
2987
  - Include all necessary steps and considerations
2511
2988
  - The plan will be saved for reference during execution
2512
2989
  - Use this only when truly ready to start coding`;
2513
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2990
+ allowedModes = [
2991
+ ExecutionMode.YOLO,
2992
+ ExecutionMode.ACCEPT_EDITS,
2993
+ ExecutionMode.PLAN,
2994
+ ExecutionMode.SMART,
2995
+ ];
2514
2996
 
2515
- async execute(params: { plan: string }): Promise<{ success: boolean; message: string; plan: string }> {
2997
+ async execute(params: {
2998
+ plan: string;
2999
+ }): Promise<{ success: boolean; message: string; plan: string }> {
2516
3000
  const { plan } = params;
2517
-
3001
+
2518
3002
  try {
2519
3003
  return {
2520
3004
  success: true,
2521
3005
  message: 'Plan completed and ready for execution',
2522
- plan
3006
+ plan,
2523
3007
  };
2524
3008
  } catch (error: any) {
2525
3009
  throw new Error(`Failed to exit plan mode: ${error.message}`);
@@ -2559,21 +3043,21 @@ export class XmlEscapeTool implements Tool {
2559
3043
  escape_all?: boolean;
2560
3044
  }): Promise<{ success: boolean; message: string; changes: number }> {
2561
3045
  const { file_path, escape_all = false } = params;
2562
-
3046
+
2563
3047
  try {
2564
3048
  const absolutePath = path.resolve(file_path);
2565
3049
  let content = await fs.readFile(absolutePath, 'utf-8');
2566
-
3050
+
2567
3051
  const specialChars = [
2568
3052
  { char: '&', replacement: '&amp;' },
2569
3053
  { char: '<', replacement: '&lt;' },
2570
3054
  { char: '>', replacement: '&gt;' },
2571
3055
  { char: '"', replacement: '&quot;' },
2572
- { char: "'", replacement: '&apos;' }
3056
+ { char: "'", replacement: '&apos;' },
2573
3057
  ];
2574
-
3058
+
2575
3059
  let changes = 0;
2576
-
3060
+
2577
3061
  for (const { char, replacement } of specialChars) {
2578
3062
  const regex = new RegExp(this.escapeRegExp(char), 'g');
2579
3063
  const matches = content.match(regex);
@@ -2582,14 +3066,14 @@ export class XmlEscapeTool implements Tool {
2582
3066
  content = content.replace(regex, replacement);
2583
3067
  }
2584
3068
  }
2585
-
3069
+
2586
3070
  if (escape_all) {
2587
3071
  const additionalChars = [
2588
3072
  { char: '©', replacement: '&copy;' },
2589
3073
  { char: '®', replacement: '&reg;' },
2590
- { char: '€', replacement: '&euro;' }
3074
+ { char: '€', replacement: '&euro;' },
2591
3075
  ];
2592
-
3076
+
2593
3077
  for (const { char, replacement } of additionalChars) {
2594
3078
  const regex = new RegExp(this.escapeRegExp(char), 'g');
2595
3079
  const matches = content.match(regex);
@@ -2599,13 +3083,13 @@ export class XmlEscapeTool implements Tool {
2599
3083
  }
2600
3084
  }
2601
3085
  }
2602
-
3086
+
2603
3087
  await fs.writeFile(absolutePath, content, 'utf-8');
2604
-
3088
+
2605
3089
  return {
2606
3090
  success: true,
2607
3091
  message: `Successfully escaped ${changes} character(s) in ${file_path}`,
2608
- changes
3092
+ changes,
2609
3093
  };
2610
3094
  } catch (error: any) {
2611
3095
  throw new Error(`Failed to escape XML/HTML in file ${file_path}: ${error.message}`);
@@ -2646,7 +3130,12 @@ export class ImageReadTool implements Tool {
2646
3130
  - Provide clear prompts for what to look for
2647
3131
  - Use task_brief for context
2648
3132
  - Supports PNG, JPG, GIF, WEBP, SVG, BMP`;
2649
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
3133
+ allowedModes = [
3134
+ ExecutionMode.YOLO,
3135
+ ExecutionMode.ACCEPT_EDITS,
3136
+ ExecutionMode.PLAN,
3137
+ ExecutionMode.SMART,
3138
+ ];
2650
3139
 
2651
3140
  async execute(params: {
2652
3141
  image_input: string;
@@ -2656,10 +3145,10 @@ export class ImageReadTool implements Tool {
2656
3145
  mime_type?: string;
2657
3146
  }): Promise<{ analysis: string; image_info: any }> {
2658
3147
  const { image_input, prompt, task_brief, input_type = 'file_path', mime_type } = params;
2659
-
3148
+
2660
3149
  try {
2661
3150
  let imageData: string;
2662
-
3151
+
2663
3152
  if (input_type === 'file_path') {
2664
3153
  const absolutePath = path.resolve(image_input);
2665
3154
  const imageBuffer = await fs.readFile(absolutePath);
@@ -2667,19 +3156,14 @@ export class ImageReadTool implements Tool {
2667
3156
  } else {
2668
3157
  imageData = image_input;
2669
3158
  }
2670
-
2671
- const { AIClient } = await import('./ai-client.js');
3159
+
2672
3160
  const configManager = await import('./config.js');
2673
3161
  const { getConfigManager } = configManager;
2674
3162
  const config = getConfigManager();
2675
-
2676
- const aiClient = new AIClient({
2677
- type: AuthType.OPENAI_COMPATIBLE,
2678
- apiKey: config.get('apiKey'),
2679
- baseUrl: config.get('baseUrl'),
2680
- modelName: config.get('modelName') || 'Qwen3-Coder'
2681
- });
2682
-
3163
+ const authConfig = config.getAuthConfig();
3164
+
3165
+ const aiClient = createAIClient(authConfig);
3166
+
2683
3167
  const textContent = task_brief ? `${task_brief}\n\n${prompt}` : prompt;
2684
3168
  const messages: Message[] = [
2685
3169
  {
@@ -2687,32 +3171,32 @@ export class ImageReadTool implements Tool {
2687
3171
  content: [
2688
3172
  {
2689
3173
  type: 'text',
2690
- text: textContent
3174
+ text: textContent,
2691
3175
  },
2692
3176
  {
2693
3177
  type: 'image_url' as const,
2694
3178
  image_url: {
2695
- url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`
2696
- }
2697
- }
2698
- ]
2699
- }
3179
+ url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`,
3180
+ },
3181
+ },
3182
+ ],
3183
+ },
2700
3184
  ];
2701
-
3185
+
2702
3186
  const result = await aiClient.chatCompletion(messages, {
2703
- temperature: 0.7
3187
+ temperature: 0.7,
2704
3188
  });
2705
-
3189
+
2706
3190
  const messageContent = result.choices[0]?.message?.content;
2707
3191
  const analysis = typeof messageContent === 'string' ? messageContent : '';
2708
-
3192
+
2709
3193
  return {
2710
3194
  analysis,
2711
3195
  image_info: {
2712
3196
  input_type,
2713
3197
  prompt,
2714
- task_brief
2715
- }
3198
+ task_brief,
3199
+ },
2716
3200
  };
2717
3201
  } catch (error: any) {
2718
3202
  throw new Error(`Failed to read image: ${error.message}`);
@@ -2802,13 +3286,16 @@ export class InvokeSkillTool implements Tool {
2802
3286
  - Skills will guide you through their specific workflows`;
2803
3287
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
2804
3288
 
2805
- async execute(params: {
2806
- skillId: string;
2807
- taskDescription: string;
2808
- inputFile?: string;
2809
- outputFile?: string;
2810
- options?: Record<string, any>;
2811
- }, _executionMode?: ExecutionMode): Promise<{
3289
+ async execute(
3290
+ params: {
3291
+ skillId: string;
3292
+ taskDescription: string;
3293
+ inputFile?: string;
3294
+ outputFile?: string;
3295
+ options?: Record<string, any>;
3296
+ },
3297
+ _executionMode?: ExecutionMode
3298
+ ): Promise<{
2812
3299
  success: boolean;
2813
3300
  message: string;
2814
3301
  skill: string;
@@ -2825,12 +3312,14 @@ export class InvokeSkillTool implements Tool {
2825
3312
  reason: string;
2826
3313
  }>;
2827
3314
  guidance?: string;
3315
+ /** Skill directory path for dependency management */
3316
+ skillPath?: string;
2828
3317
  }> {
2829
3318
  const { skillId, taskDescription, inputFile, outputFile, options } = params;
2830
3319
 
2831
3320
  try {
2832
3321
  const { getSkillInvoker } = await import('./skill-invoker.js');
2833
- const { SkillExecutionParams } = await import('./skill-invoker.js') as any;
3322
+ const { SkillExecutionParams } = (await import('./skill-invoker.js')) as any;
2834
3323
  const skillInvoker = getSkillInvoker();
2835
3324
 
2836
3325
  await skillInvoker.initialize();
@@ -2847,7 +3336,7 @@ export class InvokeSkillTool implements Tool {
2847
3336
  taskDescription,
2848
3337
  inputFile,
2849
3338
  outputFile,
2850
- options
3339
+ options,
2851
3340
  });
2852
3341
 
2853
3342
  if (result.success) {
@@ -2858,7 +3347,7 @@ export class InvokeSkillTool implements Tool {
2858
3347
  task: taskDescription,
2859
3348
  result: result.output,
2860
3349
  files: result.files,
2861
- nextSteps: result.nextSteps
3350
+ nextSteps: result.nextSteps,
2862
3351
  };
2863
3352
  } else {
2864
3353
  throw new Error(result.error || 'Failed to execute matched skill');
@@ -2872,7 +3361,7 @@ export class InvokeSkillTool implements Tool {
2872
3361
  taskDescription,
2873
3362
  inputFile,
2874
3363
  outputFile,
2875
- options
3364
+ options,
2876
3365
  });
2877
3366
 
2878
3367
  if (result.success) {
@@ -2883,7 +3372,8 @@ export class InvokeSkillTool implements Tool {
2883
3372
  task: taskDescription,
2884
3373
  result: result.output,
2885
3374
  files: result.files,
2886
- nextSteps: result.nextSteps
3375
+ nextSteps: result.nextSteps,
3376
+ skillPath: result.skillPath
2887
3377
  };
2888
3378
  } else {
2889
3379
  throw new Error(result.error);
@@ -2943,7 +3433,7 @@ export class InvokeSkillTool implements Tool {
2943
3433
 
2944
3434
  // async execute(params: { skill: string }): Promise<{ success: boolean; details: any }> {
2945
3435
  // const { skill } = params;
2946
-
3436
+
2947
3437
  // if (!skill) {
2948
3438
  // throw new Error('Skill parameter is required');
2949
3439
  // }
@@ -2976,7 +3466,8 @@ export class InvokeSkillTool implements Tool {
2976
3466
  export class ToolRegistry {
2977
3467
  private tools: Map<string, Tool> = new Map();
2978
3468
  private todoWriteTool: TodoWriteTool;
2979
- private backgroundTasks: Map<string, { process: any; startTime: number; output: string[] }> = new Map();
3469
+ private backgroundTasks: Map<string, { process: any; startTime: number; output: string[] }> =
3470
+ new Map();
2980
3471
 
2981
3472
  constructor() {
2982
3473
  this.todoWriteTool = new TodoWriteTool();
@@ -2989,7 +3480,7 @@ export class ToolRegistry {
2989
3480
  this.register(new GrepTool());
2990
3481
  this.register(new BashTool());
2991
3482
  this.register(new ListDirectoryTool());
2992
- this.register(new SearchFilesTool());
3483
+ this.register(new SearchFilesTool());
2993
3484
  this.register(new DeleteFileTool());
2994
3485
  this.register(new CreateDirectoryTool());
2995
3486
  this.register(new EditTool());
@@ -3027,23 +3518,21 @@ export class ToolRegistry {
3027
3518
  registerMCPTools(mcpTools: Map<string, any>): void {
3028
3519
  let registeredCount = 0;
3029
3520
 
3030
- for (const [fullName, tool] of mcpTools) {
3031
-
3032
- const firstUnderscoreIndex = fullName.indexOf('__');
3033
-
3034
- if (firstUnderscoreIndex === -1 || firstUnderscoreIndex === 0 ||
3035
-
3036
- firstUnderscoreIndex === fullName.length - 2) continue;
3037
-
3038
-
3521
+ for (const [fullName, tool] of mcpTools) {
3522
+ const firstUnderscoreIndex = fullName.indexOf('__');
3039
3523
 
3040
- const serverName = fullName.substring(0, firstUnderscoreIndex);
3524
+ if (
3525
+ firstUnderscoreIndex === -1 ||
3526
+ firstUnderscoreIndex === 0 ||
3527
+ firstUnderscoreIndex === fullName.length - 2
3528
+ )
3529
+ continue;
3041
3530
 
3042
- const originalName = fullName.substring(firstUnderscoreIndex + 2);
3531
+ const serverName = fullName.substring(0, firstUnderscoreIndex);
3043
3532
 
3044
-
3533
+ const originalName = fullName.substring(firstUnderscoreIndex + 2);
3045
3534
 
3046
- if (!originalName || originalName.trim() === '') continue;
3535
+ if (!originalName || originalName.trim() === '') continue;
3047
3536
 
3048
3537
  // Auto-rename if conflict, ensure unique name
3049
3538
  let toolName = originalName;
@@ -3077,7 +3566,7 @@ export class ToolRegistry {
3077
3566
  const { getMCPManager } = await import('./mcp.js');
3078
3567
  const mcpManager = getMCPManager();
3079
3568
  return await mcpManager.callTool(fullName, params);
3080
- }
3569
+ },
3081
3570
  };
3082
3571
  this.tools.set(toolName, mcpTool);
3083
3572
  registeredCount++;
@@ -3118,11 +3607,16 @@ export class ToolRegistry {
3118
3607
  return Array.from(this.tools.values());
3119
3608
  }
3120
3609
 
3121
- addBackgroundTask(taskId: string, task: { process: any; startTime: number; output: string[] }): void {
3610
+ addBackgroundTask(
3611
+ taskId: string,
3612
+ task: { process: any; startTime: number; output: string[] }
3613
+ ): void {
3122
3614
  this.backgroundTasks.set(taskId, task);
3123
3615
  }
3124
3616
 
3125
- getBackgroundTask(taskId: string): { process: any; startTime: number; output: string[] } | undefined {
3617
+ getBackgroundTask(
3618
+ taskId: string
3619
+ ): { process: any; startTime: number; output: string[] } | undefined {
3126
3620
  return this.backgroundTasks.get(taskId);
3127
3621
  }
3128
3622
 
@@ -3131,11 +3625,11 @@ export class ToolRegistry {
3131
3625
  }
3132
3626
 
3133
3627
  getToolDefinitions(): any[] {
3134
- return Array.from(this.tools.values()).map(tool => {
3628
+ return Array.from(this.tools.values()).map((tool) => {
3135
3629
  let parameters: any = {
3136
3630
  type: 'object',
3137
3631
  properties: {},
3138
- required: []
3632
+ required: [],
3139
3633
  };
3140
3634
 
3141
3635
  // Define specific parameters for each tool
@@ -3146,18 +3640,18 @@ export class ToolRegistry {
3146
3640
  properties: {
3147
3641
  filePath: {
3148
3642
  type: 'string',
3149
- description: 'The absolute path to the file to read'
3643
+ description: 'The absolute path to the file to read',
3150
3644
  },
3151
3645
  offset: {
3152
3646
  type: 'number',
3153
- description: 'Optional: Line number to start reading from (0-based)'
3647
+ description: 'Optional: Line number to start reading from (0-based)',
3154
3648
  },
3155
3649
  limit: {
3156
3650
  type: 'number',
3157
- description: 'Optional: Maximum number of lines to read'
3158
- }
3651
+ description: 'Optional: Maximum number of lines to read',
3652
+ },
3159
3653
  },
3160
- required: ['filePath']
3654
+ required: ['filePath'],
3161
3655
  };
3162
3656
  break;
3163
3657
 
@@ -3167,14 +3661,14 @@ export class ToolRegistry {
3167
3661
  properties: {
3168
3662
  filePath: {
3169
3663
  type: 'string',
3170
- description: 'The absolute path to the file to write'
3664
+ description: 'The absolute path to the file to write',
3171
3665
  },
3172
3666
  content: {
3173
3667
  type: 'string',
3174
- description: 'The content to write to the file'
3175
- }
3668
+ description: 'The content to write to the file',
3669
+ },
3176
3670
  },
3177
- required: ['filePath', 'content']
3671
+ required: ['filePath', 'content'],
3178
3672
  };
3179
3673
  break;
3180
3674
 
@@ -3184,26 +3678,34 @@ export class ToolRegistry {
3184
3678
  properties: {
3185
3679
  pattern: {
3186
3680
  type: 'string',
3187
- description: 'The regex pattern to search for'
3681
+ description: 'The regex pattern or literal string to search for',
3188
3682
  },
3189
3683
  path: {
3190
3684
  type: 'string',
3191
- description: 'Optional: The path to search in (default: current directory)'
3685
+ description: 'Optional: The path to search in (default: current directory)',
3192
3686
  },
3193
- include: {
3687
+ glob: {
3194
3688
  type: 'string',
3195
- description: 'Optional: Glob pattern to filter files'
3689
+ description: 'Optional: Glob pattern to filter files (e.g., "*.ts", "**/*.js")',
3690
+ },
3691
+ ignoreCase: {
3692
+ type: 'boolean',
3693
+ description: 'Optional: Case-insensitive search (default: false)',
3196
3694
  },
3197
- case_sensitive: {
3695
+ literal: {
3198
3696
  type: 'boolean',
3199
- description: 'Optional: Case-sensitive search (default: false)'
3697
+ description: 'Optional: Treat pattern as literal string (default: false)',
3200
3698
  },
3201
3699
  context: {
3202
3700
  type: 'number',
3203
- description: 'Optional: Number of context lines to show'
3204
- }
3701
+ description: 'Optional: Number of context lines to show before and after',
3702
+ },
3703
+ limit: {
3704
+ type: 'number',
3705
+ description: 'Optional: Maximum number of matches to return',
3706
+ },
3205
3707
  },
3206
- required: ['pattern']
3708
+ required: ['pattern'],
3207
3709
  };
3208
3710
  break;
3209
3711
 
@@ -3213,26 +3715,26 @@ export class ToolRegistry {
3213
3715
  properties: {
3214
3716
  command: {
3215
3717
  type: 'string',
3216
- description: 'The shell command to execute'
3718
+ description: 'The shell command to execute',
3217
3719
  },
3218
3720
  cwd: {
3219
3721
  type: 'string',
3220
- description: 'Optional: Working directory'
3722
+ description: 'Optional: Working directory',
3221
3723
  },
3222
3724
  description: {
3223
3725
  type: 'string',
3224
- description: 'Optional: Brief description of the command'
3726
+ description: 'Optional: Brief description of the command',
3225
3727
  },
3226
3728
  timeout: {
3227
3729
  type: 'number',
3228
- description: 'Optional: Timeout in seconds (default: 120)'
3730
+ description: 'Optional: Timeout in seconds (default: 120)',
3229
3731
  },
3230
3732
  run_in_bg: {
3231
3733
  type: 'boolean',
3232
- description: 'Optional: Run in background (default: false)'
3233
- }
3734
+ description: 'Optional: Run in background (default: false)',
3735
+ },
3234
3736
  },
3235
- required: ['command']
3737
+ required: ['command'],
3236
3738
  };
3237
3739
  break;
3238
3740
 
@@ -3242,14 +3744,14 @@ export class ToolRegistry {
3242
3744
  properties: {
3243
3745
  path: {
3244
3746
  type: 'string',
3245
- description: 'Optional: The directory path to list (default: current directory)'
3747
+ description: 'Optional: The directory path to list (default: current directory)',
3246
3748
  },
3247
3749
  recursive: {
3248
3750
  type: 'boolean',
3249
- description: 'Optional: List recursively (default: false)'
3250
- }
3751
+ description: 'Optional: List recursively (default: false)',
3752
+ },
3251
3753
  },
3252
- required: []
3754
+ required: [],
3253
3755
  };
3254
3756
  break;
3255
3757
 
@@ -3259,18 +3761,18 @@ export class ToolRegistry {
3259
3761
  properties: {
3260
3762
  pattern: {
3261
3763
  type: 'string',
3262
- description: 'The glob pattern to match files'
3764
+ description: 'The glob pattern to match files',
3263
3765
  },
3264
3766
  path: {
3265
3767
  type: 'string',
3266
- description: 'Optional: The path to search in (default: current directory)'
3768
+ description: 'Optional: The path to search in (default: current directory)',
3267
3769
  },
3268
3770
  limit: {
3269
3771
  type: 'integer',
3270
- description: 'Optional: Maximum number of results to return (default: 1000)'
3271
- }
3772
+ description: 'Optional: Maximum number of results to return (default: 1000)',
3773
+ },
3272
3774
  },
3273
- required: ['pattern']
3775
+ required: ['pattern'],
3274
3776
  };
3275
3777
  break;
3276
3778
 
@@ -3280,10 +3782,10 @@ export class ToolRegistry {
3280
3782
  properties: {
3281
3783
  filePath: {
3282
3784
  type: 'string',
3283
- description: 'The path to the file to delete'
3284
- }
3785
+ description: 'The path to the file to delete',
3786
+ },
3285
3787
  },
3286
- required: ['filePath']
3788
+ required: ['filePath'],
3287
3789
  };
3288
3790
  break;
3289
3791
 
@@ -3293,14 +3795,14 @@ export class ToolRegistry {
3293
3795
  properties: {
3294
3796
  dirPath: {
3295
3797
  type: 'string',
3296
- description: 'The directory path to create'
3798
+ description: 'The directory path to create',
3297
3799
  },
3298
3800
  recursive: {
3299
3801
  type: 'boolean',
3300
- description: 'Optional: Create parent directories (default: true)'
3301
- }
3802
+ description: 'Optional: Create parent directories (default: true)',
3803
+ },
3302
3804
  },
3303
- required: ['dirPath']
3805
+ required: ['dirPath'],
3304
3806
  };
3305
3807
  break;
3306
3808
 
@@ -3310,22 +3812,22 @@ export class ToolRegistry {
3310
3812
  properties: {
3311
3813
  file_path: {
3312
3814
  type: 'string',
3313
- description: 'The absolute path to the file to edit'
3815
+ description: 'The absolute path to the file to edit',
3314
3816
  },
3315
3817
  instruction: {
3316
3818
  type: 'string',
3317
- description: 'Description of what needs to be changed'
3819
+ description: 'Description of what needs to be changed',
3318
3820
  },
3319
3821
  old_string: {
3320
3822
  type: 'string',
3321
- description: 'The exact text to replace (supports fuzzy matching)'
3823
+ description: 'The exact text to replace (supports fuzzy matching)',
3322
3824
  },
3323
3825
  new_string: {
3324
3826
  type: 'string',
3325
- description: 'The new text to replace with'
3326
- }
3827
+ description: 'The new text to replace with',
3828
+ },
3327
3829
  },
3328
- required: ['file_path', 'instruction', 'old_string', 'new_string']
3830
+ required: ['file_path', 'instruction', 'old_string', 'new_string'],
3329
3831
  };
3330
3832
  break;
3331
3833
 
@@ -3335,10 +3837,10 @@ export class ToolRegistry {
3335
3837
  properties: {
3336
3838
  query: {
3337
3839
  type: 'string',
3338
- description: 'The search query'
3339
- }
3840
+ description: 'The search query',
3841
+ },
3340
3842
  },
3341
- required: ['query']
3843
+ required: ['query'],
3342
3844
  };
3343
3845
  break;
3344
3846
 
@@ -3354,14 +3856,17 @@ export class ToolRegistry {
3354
3856
  properties: {
3355
3857
  id: { type: 'string' },
3356
3858
  task: { type: 'string' },
3357
- status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed'] },
3358
- priority: { type: 'string', enum: ['high', 'medium', 'low'] }
3859
+ status: {
3860
+ type: 'string',
3861
+ enum: ['pending', 'in_progress', 'completed', 'failed'],
3862
+ },
3863
+ priority: { type: 'string', enum: ['high', 'medium', 'low'] },
3359
3864
  },
3360
- required: ['id', 'task', 'status']
3361
- }
3362
- }
3865
+ required: ['id', 'task', 'status'],
3866
+ },
3867
+ },
3363
3868
  },
3364
- required: ['todos']
3869
+ required: ['todos'],
3365
3870
  };
3366
3871
  break;
3367
3872
 
@@ -3369,7 +3874,7 @@ export class ToolRegistry {
3369
3874
  parameters = {
3370
3875
  type: 'object',
3371
3876
  properties: {},
3372
- required: []
3877
+ required: [],
3373
3878
  };
3374
3879
  break;
3375
3880
 
@@ -3379,60 +3884,79 @@ export class ToolRegistry {
3379
3884
  properties: {
3380
3885
  description: {
3381
3886
  type: 'string',
3382
- description: 'Brief description of the task (3-5 words)'
3887
+ description: 'Brief description of the task (3-5 words)',
3383
3888
  },
3384
3889
  agents: {
3385
3890
  type: 'array',
3386
- description: 'Optional: Array of agents to run in parallel for comprehensive analysis',
3891
+ description:
3892
+ 'Optional: Array of agents to run in parallel for comprehensive analysis',
3387
3893
  items: {
3388
3894
  type: 'object',
3389
3895
  properties: {
3390
3896
  description: {
3391
3897
  type: 'string',
3392
- description: 'Brief description of the sub-agent task'
3898
+ description: 'Brief description of the sub-agent task',
3393
3899
  },
3394
3900
  prompt: {
3395
3901
  type: 'string',
3396
- description: 'The task for the sub-agent to perform'
3902
+ description: 'The task for the sub-agent to perform',
3397
3903
  },
3398
3904
  subagent_type: {
3399
3905
  type: 'string',
3400
- enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
3401
- description: 'The type of specialized agent'
3906
+ enum: [
3907
+ 'general-purpose',
3908
+ 'plan-agent',
3909
+ 'explore-agent',
3910
+ 'frontend-tester',
3911
+ 'code-reviewer',
3912
+ 'frontend-developer',
3913
+ 'backend-developer',
3914
+ ],
3915
+ description: 'The type of specialized agent',
3402
3916
  },
3403
3917
  constraints: {
3404
3918
  type: 'array',
3405
3919
  items: { type: 'string' },
3406
- description: 'Optional: Constraints or limitations'
3407
- }
3920
+ description: 'Optional: Constraints or limitations',
3921
+ },
3408
3922
  },
3409
- required: ['description', 'prompt', 'subagent_type']
3410
- }
3923
+ required: ['description', 'prompt', 'subagent_type'],
3924
+ },
3411
3925
  },
3412
3926
  prompt: {
3413
3927
  type: 'string',
3414
- description: 'Optional: The task for the agent to perform (use agents for parallel execution)'
3928
+ description:
3929
+ 'Optional: The task for the agent to perform (use agents for parallel execution)',
3415
3930
  },
3416
3931
  subagent_type: {
3417
3932
  type: 'string',
3418
- enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
3419
- description: 'Optional: The type of specialized agent (use agents for parallel execution)'
3933
+ enum: [
3934
+ 'general-purpose',
3935
+ 'plan-agent',
3936
+ 'explore-agent',
3937
+ 'frontend-tester',
3938
+ 'code-reviewer',
3939
+ 'frontend-developer',
3940
+ 'backend-developer',
3941
+ ],
3942
+ description:
3943
+ 'Optional: The type of specialized agent (use agents for parallel execution)',
3420
3944
  },
3421
3945
  useContext: {
3422
3946
  type: 'boolean',
3423
- description: 'Optional: Include main agent context'
3947
+ description: 'Optional: Include main agent context',
3424
3948
  },
3425
3949
  outputFormat: {
3426
3950
  type: 'string',
3427
- description: 'Optional: Output format template'
3951
+ description: 'Optional: Output format template',
3428
3952
  },
3429
3953
  constraints: {
3430
3954
  type: 'array',
3431
3955
  items: { type: 'string' },
3432
- description: 'Optional: Constraints or limitations'
3433
- }
3956
+ description: 'Optional: Constraints or limitations',
3957
+ },
3434
3958
  },
3435
- required: ['description']
3959
+ required: ['description'],
3436
3960
  };
3437
3961
  break;
3438
3962
 
@@ -3442,14 +3966,14 @@ export class ToolRegistry {
3442
3966
  properties: {
3443
3967
  task_id: {
3444
3968
  type: 'string',
3445
- description: 'The ID of the task'
3969
+ description: 'The ID of the task',
3446
3970
  },
3447
3971
  poll_interval: {
3448
3972
  type: 'number',
3449
- description: 'Optional: Polling interval in seconds (default: 10)'
3450
- }
3973
+ description: 'Optional: Polling interval in seconds (default: 10)',
3974
+ },
3451
3975
  },
3452
- required: ['task_id']
3976
+ required: ['task_id'],
3453
3977
  };
3454
3978
  break;
3455
3979
 
@@ -3459,10 +3983,10 @@ export class ToolRegistry {
3459
3983
  properties: {
3460
3984
  prompt: {
3461
3985
  type: 'string',
3462
- description: 'Prompt containing URL(s) and processing instructions'
3463
- }
3986
+ description: 'Prompt containing URL(s) and processing instructions',
3987
+ },
3464
3988
  },
3465
- required: ['prompt']
3989
+ required: ['prompt'],
3466
3990
  };
3467
3991
  break;
3468
3992
 
@@ -3481,15 +4005,15 @@ export class ToolRegistry {
3481
4005
  options: {
3482
4006
  type: 'array',
3483
4007
  items: { type: 'string' },
3484
- description: 'Available choices (2-4 options)'
4008
+ description: 'Available choices (2-4 options)',
3485
4009
  },
3486
- multiSelect: { type: 'boolean' }
4010
+ multiSelect: { type: 'boolean' },
3487
4011
  },
3488
- required: ['question', 'header', 'options', 'multiSelect']
3489
- }
3490
- }
4012
+ required: ['question', 'header', 'options', 'multiSelect'],
4013
+ },
4014
+ },
3491
4015
  },
3492
- required: ['questions']
4016
+ required: ['questions'],
3493
4017
  };
3494
4018
  break;
3495
4019
 
@@ -3499,10 +4023,10 @@ export class ToolRegistry {
3499
4023
  properties: {
3500
4024
  fact: {
3501
4025
  type: 'string',
3502
- description: 'The specific fact to remember'
3503
- }
4026
+ description: 'The specific fact to remember',
4027
+ },
3504
4028
  },
3505
- required: ['fact']
4029
+ required: ['fact'],
3506
4030
  };
3507
4031
  break;
3508
4032
 
@@ -3512,10 +4036,10 @@ export class ToolRegistry {
3512
4036
  properties: {
3513
4037
  plan: {
3514
4038
  type: 'string',
3515
- description: 'The plan to present'
3516
- }
4039
+ description: 'The plan to present',
4040
+ },
3517
4041
  },
3518
- required: ['plan']
4042
+ required: ['plan'],
3519
4043
  };
3520
4044
  break;
3521
4045
 
@@ -3525,14 +4049,14 @@ export class ToolRegistry {
3525
4049
  properties: {
3526
4050
  file_path: {
3527
4051
  type: 'string',
3528
- description: 'The absolute path to the XML/HTML file'
4052
+ description: 'The absolute path to the XML/HTML file',
3529
4053
  },
3530
4054
  escape_all: {
3531
4055
  type: 'boolean',
3532
- description: 'Optional: Escape all special characters (default: false)'
3533
- }
4056
+ description: 'Optional: Escape all special characters (default: false)',
4057
+ },
3534
4058
  },
3535
- required: ['file_path']
4059
+ required: ['file_path'],
3536
4060
  };
3537
4061
  break;
3538
4062
 
@@ -3542,27 +4066,27 @@ export class ToolRegistry {
3542
4066
  properties: {
3543
4067
  image_input: {
3544
4068
  type: 'string',
3545
- description: 'Image file path or base64 data'
4069
+ description: 'Image file path or base64 data',
3546
4070
  },
3547
4071
  prompt: {
3548
4072
  type: 'string',
3549
- description: 'Comprehensive VLM instruction'
4073
+ description: 'Comprehensive VLM instruction',
3550
4074
  },
3551
4075
  task_brief: {
3552
4076
  type: 'string',
3553
- description: 'Brief task description (max 15 words)'
4077
+ description: 'Brief task description (max 15 words)',
3554
4078
  },
3555
4079
  input_type: {
3556
4080
  type: 'string',
3557
4081
  enum: ['file_path', 'base64'],
3558
- description: 'Input type (default: file_path)'
4082
+ description: 'Input type (default: file_path)',
3559
4083
  },
3560
4084
  mime_type: {
3561
4085
  type: 'string',
3562
- description: 'Optional: MIME type for base64 input'
3563
- }
4086
+ description: 'Optional: MIME type for base64 input',
4087
+ },
3564
4088
  },
3565
- required: ['image_input', 'prompt']
4089
+ required: ['image_input', 'prompt'],
3566
4090
  };
3567
4091
  break;
3568
4092
 
@@ -3572,10 +4096,10 @@ export class ToolRegistry {
3572
4096
  properties: {
3573
4097
  skill: {
3574
4098
  type: 'string',
3575
- description: 'The skill name to execute'
3576
- }
4099
+ description: 'The skill name to execute',
4100
+ },
3577
4101
  },
3578
- required: ['skill']
4102
+ required: ['skill'],
3579
4103
  };
3580
4104
  break;
3581
4105
 
@@ -3583,7 +4107,7 @@ export class ToolRegistry {
3583
4107
  parameters = {
3584
4108
  type: 'object',
3585
4109
  properties: {},
3586
- required: []
4110
+ required: [],
3587
4111
  };
3588
4112
  break;
3589
4113
 
@@ -3593,10 +4117,10 @@ export class ToolRegistry {
3593
4117
  properties: {
3594
4118
  skill: {
3595
4119
  type: 'string',
3596
- description: 'The skill name/id to get details for'
3597
- }
4120
+ description: 'The skill name/id to get details for',
4121
+ },
3598
4122
  },
3599
- required: ['skill']
4123
+ required: ['skill'],
3600
4124
  };
3601
4125
  break;
3602
4126
 
@@ -3608,13 +4132,15 @@ export class ToolRegistry {
3608
4132
  parameters = {
3609
4133
  type: 'object',
3610
4134
  properties: {},
3611
- required: []
4135
+ required: [],
3612
4136
  };
3613
4137
  if (mcpTool.inputSchema.properties) {
3614
- for (const [paramName, paramDef] of Object.entries<any>(mcpTool.inputSchema.properties)) {
4138
+ for (const [paramName, paramDef] of Object.entries<any>(
4139
+ mcpTool.inputSchema.properties
4140
+ )) {
3615
4141
  parameters.properties[paramName] = {
3616
4142
  type: paramDef.type || 'string',
3617
- description: paramDef.description || ''
4143
+ description: paramDef.description || '',
3618
4144
  };
3619
4145
  }
3620
4146
  }
@@ -3625,7 +4151,7 @@ export class ToolRegistry {
3625
4151
  parameters = {
3626
4152
  type: 'object',
3627
4153
  properties: {},
3628
- required: []
4154
+ required: [],
3629
4155
  };
3630
4156
  }
3631
4157
  }
@@ -3635,13 +4161,18 @@ export class ToolRegistry {
3635
4161
  function: {
3636
4162
  name: tool.name,
3637
4163
  description: tool.description,
3638
- parameters
3639
- }
4164
+ parameters,
4165
+ },
3640
4166
  };
3641
4167
  });
3642
4168
  }
3643
4169
 
3644
- async execute(toolName: string, params: any, executionMode: ExecutionMode, indent: string = ''): Promise<any> {
4170
+ async execute(
4171
+ toolName: string,
4172
+ params: any,
4173
+ executionMode: ExecutionMode,
4174
+ indent: string = ''
4175
+ ): Promise<any> {
3645
4176
  // First try to execute as local tool
3646
4177
  const localTool = this.tools.get(toolName);
3647
4178
  if (localTool) {
@@ -3665,7 +4196,7 @@ export class ToolRegistry {
3665
4196
  if (firstUnderscoreIndex === -1) continue;
3666
4197
  const [serverName, actualToolName] = [
3667
4198
  fullName.substring(0, firstUnderscoreIndex),
3668
- fullName.substring(firstUnderscoreIndex + 2)
4199
+ fullName.substring(firstUnderscoreIndex + 2),
3669
4200
  ];
3670
4201
  if (actualToolName === toolName) {
3671
4202
  return await this.executeMCPTool(fullName, params, executionMode, indent);
@@ -3679,7 +4210,12 @@ export class ToolRegistry {
3679
4210
  /**
3680
4211
  * Execute local tool (extracted for reuse)
3681
4212
  */
3682
- private async executeLocalTool(toolName: string, params: any, executionMode: ExecutionMode, indent: string): Promise<any> {
4213
+ private async executeLocalTool(
4214
+ toolName: string,
4215
+ params: any,
4216
+ executionMode: ExecutionMode,
4217
+ indent: string
4218
+ ): Promise<any> {
3683
4219
  const tool = this.get(toolName);
3684
4220
  const cancellationManager = getCancellationManager();
3685
4221
 
@@ -3688,9 +4224,7 @@ export class ToolRegistry {
3688
4224
  }
3689
4225
 
3690
4226
  if (!tool.allowedModes.includes(executionMode)) {
3691
- throw new Error(
3692
- `Tool ${toolName} is not allowed in ${executionMode} mode`
3693
- );
4227
+ throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
3694
4228
  }
3695
4229
 
3696
4230
  // Smart approval mode
@@ -3702,7 +4236,9 @@ export class ToolRegistry {
3702
4236
  if (debugMode) {
3703
4237
  const { getLogger } = await import('./logger.js');
3704
4238
  const logger = getLogger();
3705
- logger.debug(`[SmartApprovalEngine] Tool '${toolName}' bypassed smart approval completely`);
4239
+ logger.debug(
4240
+ `[SmartApprovalEngine] Tool '${toolName}' bypassed smart approval completely`
4241
+ );
3706
4242
  }
3707
4243
  return await cancellationManager.withCancellation(
3708
4244
  tool.execute(params, executionMode),
@@ -3718,7 +4254,9 @@ export class ToolRegistry {
3718
4254
  const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
3719
4255
  if (isRemoteMode && toolName === 'InvokeSkill') {
3720
4256
  console.log('');
3721
- console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`);
4257
+ console.log(
4258
+ `${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`
4259
+ );
3722
4260
  console.log('');
3723
4261
  return await cancellationManager.withCancellation(
3724
4262
  tool.execute(params, executionMode),
@@ -3734,15 +4272,19 @@ export class ToolRegistry {
3734
4272
  const result = await approvalEngine.evaluate({
3735
4273
  toolName,
3736
4274
  params,
3737
- timestamp: Date.now()
4275
+ timestamp: Date.now(),
3738
4276
  });
3739
4277
 
3740
4278
  // Decide whether to execute based on approval result
3741
4279
  if (result.decision === 'approved') {
3742
4280
  // Whitelist or AI approval passed, execute directly
3743
4281
  console.log('');
3744
- console.log(`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`);
3745
- console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
4282
+ console.log(
4283
+ `${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`
4284
+ );
4285
+ console.log(
4286
+ `${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`
4287
+ );
3746
4288
  console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
3747
4289
  console.log('');
3748
4290
  return await cancellationManager.withCancellation(
@@ -3755,7 +4297,9 @@ export class ToolRegistry {
3755
4297
 
3756
4298
  if (confirmed) {
3757
4299
  console.log('');
3758
- console.log(`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`);
4300
+ console.log(
4301
+ `${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`
4302
+ );
3759
4303
  console.log('');
3760
4304
  return await cancellationManager.withCancellation(
3761
4305
  tool.execute(params, executionMode),
@@ -3763,14 +4307,18 @@ export class ToolRegistry {
3763
4307
  );
3764
4308
  } else {
3765
4309
  console.log('');
3766
- console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`);
4310
+ console.log(
4311
+ `${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`
4312
+ );
3767
4313
  console.log('');
3768
4314
  throw new Error(`Tool execution cancelled by user: ${toolName}`);
3769
4315
  }
3770
4316
  } else {
3771
4317
  // Rejected execution
3772
4318
  console.log('');
3773
- console.log(`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`);
4319
+ console.log(
4320
+ `${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`
4321
+ );
3774
4322
  console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
3775
4323
  console.log('');
3776
4324
  throw new Error(`Tool execution rejected: ${toolName}`);
@@ -3787,7 +4335,12 @@ export class ToolRegistry {
3787
4335
  /**
3788
4336
  * Execute an MCP tool call
3789
4337
  */
3790
- private async executeMCPTool(toolName: string, params: any, executionMode: ExecutionMode, indent: string = ''): Promise<any> {
4338
+ private async executeMCPTool(
4339
+ toolName: string,
4340
+ params: any,
4341
+ executionMode: ExecutionMode,
4342
+ indent: string = ''
4343
+ ): Promise<any> {
3791
4344
  const { getMCPManager } = await import('./mcp.js');
3792
4345
  const mcpManager = getMCPManager();
3793
4346
  const cancellationManager = getCancellationManager();
@@ -3796,14 +4349,16 @@ export class ToolRegistry {
3796
4349
  const firstUnderscoreIndex = toolName.indexOf('__');
3797
4350
  const serverName = toolName.substring(0, firstUnderscoreIndex);
3798
4351
  const actualToolName = toolName.substring(firstUnderscoreIndex + 2);
3799
-
4352
+
3800
4353
  // Get server info for display
3801
4354
  const server = mcpManager.getServer(serverName);
3802
4355
  const serverTools = server?.getToolNames() || [];
3803
-
4356
+
3804
4357
  // Display tool call info
3805
4358
  console.log('');
3806
- console.log(`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`);
4359
+ console.log(
4360
+ `${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`
4361
+ );
3807
4362
 
3808
4363
  // Smart approval mode for MCP tools
3809
4364
  if (executionMode === ExecutionMode.SMART) {
@@ -3816,7 +4371,9 @@ export class ToolRegistry {
3816
4371
 
3817
4372
  // Remote mode: remote LLM has already approved the tool, auto-approve
3818
4373
  if (isRemoteMode) {
3819
- console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`);
4374
+ console.log(
4375
+ `${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`
4376
+ );
3820
4377
  } else {
3821
4378
  const approvalEngine = getSmartApprovalEngine(debugMode);
3822
4379
 
@@ -3824,16 +4381,22 @@ export class ToolRegistry {
3824
4381
  const result = await approvalEngine.evaluate({
3825
4382
  toolName: `MCP[${serverName}]::${actualToolName}`,
3826
4383
  params,
3827
- timestamp: Date.now()
4384
+ timestamp: Date.now(),
3828
4385
  });
3829
4386
 
3830
4387
  if (result.decision === 'approved') {
3831
- console.log(`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`);
3832
- console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
4388
+ console.log(
4389
+ `${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`
4390
+ );
4391
+ console.log(
4392
+ `${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`
4393
+ );
3833
4394
  } else if (result.decision === 'requires_confirmation') {
3834
4395
  const confirmed = await approvalEngine.requestConfirmation(result);
3835
4396
  if (!confirmed) {
3836
- console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`);
4397
+ console.log(
4398
+ `${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`
4399
+ );
3837
4400
  throw new Error(`Tool execution cancelled by user: ${toolName}`);
3838
4401
  }
3839
4402
  } else {