@xagent-ai/cli 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (537) hide show
  1. package/.eslintrc.js +25 -0
  2. package/.gitmodules +3 -0
  3. package/.prettierrc.json +8 -0
  4. package/CONTRIBUTING.md +167 -0
  5. package/LICENSE +21 -0
  6. package/README.md +280 -0
  7. package/README_CN.md +280 -0
  8. package/dist/agents.d.ts +21 -0
  9. package/dist/agents.d.ts.map +1 -0
  10. package/dist/agents.js +463 -0
  11. package/dist/agents.js.map +1 -0
  12. package/dist/ai-client.d.ts +83 -0
  13. package/dist/ai-client.d.ts.map +1 -0
  14. package/dist/ai-client.js +1280 -0
  15. package/dist/ai-client.js.map +1 -0
  16. package/dist/auth.d.ts +25 -0
  17. package/dist/auth.d.ts.map +1 -0
  18. package/dist/auth.js +573 -0
  19. package/dist/auth.js.map +1 -0
  20. package/dist/cancellation.d.ts +46 -0
  21. package/dist/cancellation.d.ts.map +1 -0
  22. package/dist/cancellation.js +154 -0
  23. package/dist/cancellation.js.map +1 -0
  24. package/dist/checkpoint.d.ts +28 -0
  25. package/dist/checkpoint.d.ts.map +1 -0
  26. package/dist/checkpoint.js +186 -0
  27. package/dist/checkpoint.js.map +1 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +364 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/config.d.ts +49 -0
  33. package/dist/config.d.ts.map +1 -0
  34. package/dist/config.js +205 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/context-compressor.d.ts +51 -0
  37. package/dist/context-compressor.d.ts.map +1 -0
  38. package/dist/context-compressor.js +231 -0
  39. package/dist/context-compressor.js.map +1 -0
  40. package/dist/conversation.d.ts +34 -0
  41. package/dist/conversation.d.ts.map +1 -0
  42. package/dist/conversation.js +221 -0
  43. package/dist/conversation.js.map +1 -0
  44. package/dist/gui-subagent/action-parser/actionParser.d.ts +19 -0
  45. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -0
  46. package/dist/gui-subagent/action-parser/actionParser.js +203 -0
  47. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -0
  48. package/dist/gui-subagent/action-parser/constants.d.ts +8 -0
  49. package/dist/gui-subagent/action-parser/constants.d.ts.map +1 -0
  50. package/dist/gui-subagent/action-parser/constants.js +12 -0
  51. package/dist/gui-subagent/action-parser/constants.js.map +1 -0
  52. package/dist/gui-subagent/action-parser/index.d.ts +3 -0
  53. package/dist/gui-subagent/action-parser/index.d.ts.map +1 -0
  54. package/dist/gui-subagent/action-parser/index.js +6 -0
  55. package/dist/gui-subagent/action-parser/index.js.map +1 -0
  56. package/dist/gui-subagent/action-parser/types.d.ts +24 -0
  57. package/dist/gui-subagent/action-parser/types.d.ts.map +1 -0
  58. package/dist/gui-subagent/action-parser/types.js +12 -0
  59. package/dist/gui-subagent/action-parser/types.js.map +1 -0
  60. package/dist/gui-subagent/agent/gui-agent.d.ts +126 -0
  61. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -0
  62. package/dist/gui-subagent/agent/gui-agent.js +820 -0
  63. package/dist/gui-subagent/agent/gui-agent.js.map +1 -0
  64. package/dist/gui-subagent/agent/index.d.ts +5 -0
  65. package/dist/gui-subagent/agent/index.d.ts.map +1 -0
  66. package/dist/gui-subagent/agent/index.js +5 -0
  67. package/dist/gui-subagent/agent/index.js.map +1 -0
  68. package/dist/gui-subagent/index.d.ts +43 -0
  69. package/dist/gui-subagent/index.d.ts.map +1 -0
  70. package/dist/gui-subagent/index.js +96 -0
  71. package/dist/gui-subagent/index.js.map +1 -0
  72. package/dist/gui-subagent/operator/base-operator.d.ts +108 -0
  73. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -0
  74. package/dist/gui-subagent/operator/base-operator.js +172 -0
  75. package/dist/gui-subagent/operator/base-operator.js.map +1 -0
  76. package/dist/gui-subagent/operator/browser-operator.d.ts +36 -0
  77. package/dist/gui-subagent/operator/browser-operator.d.ts.map +1 -0
  78. package/dist/gui-subagent/operator/browser-operator.js +306 -0
  79. package/dist/gui-subagent/operator/browser-operator.js.map +1 -0
  80. package/dist/gui-subagent/operator/computer-operator.d.ts +31 -0
  81. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -0
  82. package/dist/gui-subagent/operator/computer-operator.js +441 -0
  83. package/dist/gui-subagent/operator/computer-operator.js.map +1 -0
  84. package/dist/gui-subagent/operator/desktop-operator.d.ts +55 -0
  85. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +1 -0
  86. package/dist/gui-subagent/operator/desktop-operator.js +527 -0
  87. package/dist/gui-subagent/operator/desktop-operator.js.map +1 -0
  88. package/dist/gui-subagent/operator/index.d.ts +7 -0
  89. package/dist/gui-subagent/operator/index.d.ts.map +1 -0
  90. package/dist/gui-subagent/operator/index.js +6 -0
  91. package/dist/gui-subagent/operator/index.js.map +1 -0
  92. package/dist/gui-subagent/types/actions.d.ts +108 -0
  93. package/dist/gui-subagent/types/actions.d.ts.map +1 -0
  94. package/dist/gui-subagent/types/actions.js +39 -0
  95. package/dist/gui-subagent/types/actions.js.map +1 -0
  96. package/dist/gui-subagent/types/index.d.ts +6 -0
  97. package/dist/gui-subagent/types/index.d.ts.map +1 -0
  98. package/dist/gui-subagent/types/index.js +6 -0
  99. package/dist/gui-subagent/types/index.js.map +1 -0
  100. package/dist/gui-subagent/types/operator.d.ts +95 -0
  101. package/dist/gui-subagent/types/operator.d.ts.map +1 -0
  102. package/dist/gui-subagent/types/operator.js +16 -0
  103. package/dist/gui-subagent/types/operator.js.map +1 -0
  104. package/dist/gui-subagent/utils.d.ts +19 -0
  105. package/dist/gui-subagent/utils.d.ts.map +1 -0
  106. package/dist/gui-subagent/utils.js +42 -0
  107. package/dist/gui-subagent/utils.js.map +1 -0
  108. package/dist/hook.d.ts +73 -0
  109. package/dist/hook.d.ts.map +1 -0
  110. package/dist/hook.js +156 -0
  111. package/dist/hook.js.map +1 -0
  112. package/dist/index.d.ts +19 -0
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.js +19 -0
  115. package/dist/index.js.map +1 -0
  116. package/dist/input-history.d.ts +24 -0
  117. package/dist/input-history.d.ts.map +1 -0
  118. package/dist/input-history.js +94 -0
  119. package/dist/input-history.js.map +1 -0
  120. package/dist/input-processor.d.ts +31 -0
  121. package/dist/input-processor.d.ts.map +1 -0
  122. package/dist/input-processor.js +233 -0
  123. package/dist/input-processor.js.map +1 -0
  124. package/dist/keyboard-manager.d.ts +151 -0
  125. package/dist/keyboard-manager.d.ts.map +1 -0
  126. package/dist/keyboard-manager.js +396 -0
  127. package/dist/keyboard-manager.js.map +1 -0
  128. package/dist/logger.d.ts +75 -0
  129. package/dist/logger.d.ts.map +1 -0
  130. package/dist/logger.js +339 -0
  131. package/dist/logger.js.map +1 -0
  132. package/dist/mcp.d.ts +57 -0
  133. package/dist/mcp.d.ts.map +1 -0
  134. package/dist/mcp.js +483 -0
  135. package/dist/mcp.js.map +1 -0
  136. package/dist/memory.d.ts +25 -0
  137. package/dist/memory.d.ts.map +1 -0
  138. package/dist/memory.js +250 -0
  139. package/dist/memory.js.map +1 -0
  140. package/dist/print-system-prompt.d.ts +2 -0
  141. package/dist/print-system-prompt.d.ts.map +1 -0
  142. package/dist/print-system-prompt.js +40 -0
  143. package/dist/print-system-prompt.js.map +1 -0
  144. package/dist/session-manager.d.ts +41 -0
  145. package/dist/session-manager.d.ts.map +1 -0
  146. package/dist/session-manager.js +234 -0
  147. package/dist/session-manager.js.map +1 -0
  148. package/dist/session.d.ts +77 -0
  149. package/dist/session.d.ts.map +1 -0
  150. package/dist/session.js +1081 -0
  151. package/dist/session.js.map +1 -0
  152. package/dist/skill-invoker.d.ts +177 -0
  153. package/dist/skill-invoker.d.ts.map +1 -0
  154. package/dist/skill-invoker.js +1643 -0
  155. package/dist/skill-invoker.js.map +1 -0
  156. package/dist/skill-loader.d.ts +76 -0
  157. package/dist/skill-loader.d.ts.map +1 -0
  158. package/dist/skill-loader.js +407 -0
  159. package/dist/skill-loader.js.map +1 -0
  160. package/dist/slash-commands.d.ts +60 -0
  161. package/dist/slash-commands.d.ts.map +1 -0
  162. package/dist/slash-commands.js +1021 -0
  163. package/dist/slash-commands.js.map +1 -0
  164. package/dist/smart-approval.d.ts +137 -0
  165. package/dist/smart-approval.d.ts.map +1 -0
  166. package/dist/smart-approval.js +512 -0
  167. package/dist/smart-approval.js.map +1 -0
  168. package/dist/system-prompt-generator.d.ts +35 -0
  169. package/dist/system-prompt-generator.d.ts.map +1 -0
  170. package/dist/system-prompt-generator.js +729 -0
  171. package/dist/system-prompt-generator.js.map +1 -0
  172. package/dist/test-boundary-conditions.d.ts.map +1 -0
  173. package/dist/test-boundary-conditions.js.map +1 -0
  174. package/dist/test-cancellation-fix.d.ts.map +1 -0
  175. package/dist/test-cancellation-fix.js.map +1 -0
  176. package/dist/test-input-history.d.ts.map +1 -0
  177. package/dist/test-input-history.js.map +1 -0
  178. package/dist/test-interaction-flow.d.ts.map +1 -0
  179. package/dist/test-interaction-flow.js.map +1 -0
  180. package/dist/test-quick.d.ts.map +1 -0
  181. package/dist/test-quick.js.map +1 -0
  182. package/dist/test-user-interaction.d.ts.map +1 -0
  183. package/dist/test-user-interaction.js.map +1 -0
  184. package/dist/theme.d.ts +353 -0
  185. package/dist/theme.d.ts.map +1 -0
  186. package/dist/theme.js +383 -0
  187. package/dist/theme.js.map +1 -0
  188. package/dist/tools.d.ts +373 -0
  189. package/dist/tools.d.ts.map +1 -0
  190. package/dist/tools.js +2906 -0
  191. package/dist/tools.js.map +1 -0
  192. package/dist/types.d.ts +180 -0
  193. package/dist/types.d.ts.map +1 -0
  194. package/dist/types.js +23 -0
  195. package/dist/types.js.map +1 -0
  196. package/dist/unified-session.d.ts +42 -0
  197. package/dist/unified-session.d.ts.map +1 -0
  198. package/dist/unified-session.js +271 -0
  199. package/dist/unified-session.js.map +1 -0
  200. package/dist/update.d.ts +30 -0
  201. package/dist/update.d.ts.map +1 -0
  202. package/dist/update.js +211 -0
  203. package/dist/update.js.map +1 -0
  204. package/dist/workflow.d.ts +53 -0
  205. package/dist/workflow.d.ts.map +1 -0
  206. package/dist/workflow.js +405 -0
  207. package/dist/workflow.js.map +1 -0
  208. package/docs/architecture/mcp-integration-guide.md +131 -0
  209. package/docs/architecture/overview.md +93 -0
  210. package/docs/architecture/tool-system-design.md +89 -0
  211. package/docs/cli/commands.md +189 -0
  212. package/docs/smart-mode.md +257 -0
  213. package/docs/third-party-models.md +449 -0
  214. package/package.json +85 -0
  215. package/scripts/init-skills-path.js +58 -0
  216. package/skills/.claude-plugin/marketplace.json +45 -0
  217. package/skills/README.md +94 -0
  218. package/skills/THIRD_PARTY_NOTICES.md +405 -0
  219. package/skills/skills/algorithmic-art/LICENSE.txt +202 -0
  220. package/skills/skills/algorithmic-art/SKILL.md +405 -0
  221. package/skills/skills/algorithmic-art/templates/generator_template.js +223 -0
  222. package/skills/skills/algorithmic-art/templates/viewer.html +599 -0
  223. package/skills/skills/brand-guidelines/LICENSE.txt +202 -0
  224. package/skills/skills/brand-guidelines/SKILL.md +73 -0
  225. package/skills/skills/canvas-design/LICENSE.txt +202 -0
  226. package/skills/skills/canvas-design/SKILL.md +130 -0
  227. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  228. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  229. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  230. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
  231. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  232. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
  233. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  234. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  235. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  236. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  237. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  238. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  239. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  240. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  241. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
  242. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  243. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
  244. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  245. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  246. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
  247. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  248. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
  249. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  250. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  251. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  252. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  253. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  254. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  255. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  256. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  257. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  258. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  259. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  260. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  261. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  262. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  263. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  264. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
  265. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  266. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  267. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  268. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  269. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  270. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  271. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
  272. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  273. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  274. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  275. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  276. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  277. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  278. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  279. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  280. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
  281. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  282. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  283. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  284. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  285. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  286. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  287. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  288. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
  289. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
  290. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  291. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  292. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
  293. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  294. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
  295. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  296. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  297. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
  298. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  299. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  300. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  301. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  302. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  303. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  304. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
  305. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  306. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  307. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  308. package/skills/skills/doc-coauthoring/SKILL.md +375 -0
  309. package/skills/skills/docx/LICENSE.txt +30 -0
  310. package/skills/skills/docx/SKILL.md +197 -0
  311. package/skills/skills/docx/docx-js.md +350 -0
  312. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  313. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  314. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  315. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  316. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  317. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  318. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  319. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  320. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  321. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  322. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  323. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  324. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  325. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  326. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  327. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  328. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  329. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  330. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  331. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  332. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  333. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  334. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  335. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  336. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  337. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  338. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  339. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  340. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  341. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  342. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  343. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
  344. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  345. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  346. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  347. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  348. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  349. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  350. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  351. package/skills/skills/docx/ooxml/scripts/pack.py +159 -0
  352. package/skills/skills/docx/ooxml/scripts/unpack.py +29 -0
  353. package/skills/skills/docx/ooxml/scripts/validate.py +69 -0
  354. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
  355. package/skills/skills/docx/ooxml/scripts/validation/base.py +951 -0
  356. package/skills/skills/docx/ooxml/scripts/validation/docx.py +274 -0
  357. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
  358. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
  359. package/skills/skills/docx/ooxml.md +610 -0
  360. package/skills/skills/docx/scripts/__init__.py +1 -0
  361. package/skills/skills/docx/scripts/document.py +1276 -0
  362. package/skills/skills/docx/scripts/templates/comments.xml +3 -0
  363. package/skills/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  364. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  365. package/skills/skills/docx/scripts/templates/commentsIds.xml +3 -0
  366. package/skills/skills/docx/scripts/templates/people.xml +3 -0
  367. package/skills/skills/docx/scripts/utilities.py +374 -0
  368. package/skills/skills/frontend-design/LICENSE.txt +177 -0
  369. package/skills/skills/frontend-design/SKILL.md +42 -0
  370. package/skills/skills/internal-comms/LICENSE.txt +202 -0
  371. package/skills/skills/internal-comms/SKILL.md +32 -0
  372. package/skills/skills/internal-comms/examples/3p-updates.md +47 -0
  373. package/skills/skills/internal-comms/examples/company-newsletter.md +65 -0
  374. package/skills/skills/internal-comms/examples/faq-answers.md +30 -0
  375. package/skills/skills/internal-comms/examples/general-comms.md +16 -0
  376. package/skills/skills/mcp-builder/LICENSE.txt +202 -0
  377. package/skills/skills/mcp-builder/SKILL.md +236 -0
  378. package/skills/skills/mcp-builder/reference/evaluation.md +602 -0
  379. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  380. package/skills/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  381. package/skills/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  382. package/skills/skills/mcp-builder/scripts/connections.py +151 -0
  383. package/skills/skills/mcp-builder/scripts/evaluation.py +373 -0
  384. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  385. package/skills/skills/mcp-builder/scripts/requirements.txt +2 -0
  386. package/skills/skills/pdf/LICENSE.txt +30 -0
  387. package/skills/skills/pdf/SKILL.md +294 -0
  388. package/skills/skills/pdf/forms.md +205 -0
  389. package/skills/skills/pdf/reference.md +612 -0
  390. package/skills/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  391. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  392. package/skills/skills/pdf/scripts/check_fillable_fields.py +12 -0
  393. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  394. package/skills/skills/pdf/scripts/create_validation_image.py +41 -0
  395. package/skills/skills/pdf/scripts/extract_form_field_info.py +152 -0
  396. package/skills/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  397. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  398. package/skills/skills/pptx/LICENSE.txt +30 -0
  399. package/skills/skills/pptx/SKILL.md +484 -0
  400. package/skills/skills/pptx/html2pptx.md +625 -0
  401. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  402. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  403. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  404. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  405. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  406. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  407. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  408. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  409. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  410. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  411. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  412. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  413. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  414. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  415. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  416. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  417. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  418. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  419. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  420. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  421. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  422. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  423. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  424. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  425. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  426. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  427. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  428. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  429. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  430. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  431. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  432. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  433. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  434. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  435. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  436. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  437. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  438. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  439. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  440. package/skills/skills/pptx/ooxml/scripts/pack.py +159 -0
  441. package/skills/skills/pptx/ooxml/scripts/unpack.py +29 -0
  442. package/skills/skills/pptx/ooxml/scripts/validate.py +69 -0
  443. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
  444. package/skills/skills/pptx/ooxml/scripts/validation/base.py +951 -0
  445. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
  446. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
  447. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
  448. package/skills/skills/pptx/ooxml.md +427 -0
  449. package/skills/skills/pptx/scripts/html2pptx.js +979 -0
  450. package/skills/skills/pptx/scripts/inventory.py +1020 -0
  451. package/skills/skills/pptx/scripts/rearrange.py +231 -0
  452. package/skills/skills/pptx/scripts/replace.py +385 -0
  453. package/skills/skills/pptx/scripts/thumbnail.py +450 -0
  454. package/skills/skills/skill-creator/LICENSE.txt +202 -0
  455. package/skills/skills/skill-creator/SKILL.md +356 -0
  456. package/skills/skills/skill-creator/references/output-patterns.md +82 -0
  457. package/skills/skills/skill-creator/references/workflows.md +28 -0
  458. package/skills/skills/skill-creator/scripts/init_skill.py +303 -0
  459. package/skills/skills/skill-creator/scripts/package_skill.py +110 -0
  460. package/skills/skills/skill-creator/scripts/quick_validate.py +95 -0
  461. package/skills/skills/slack-gif-creator/LICENSE.txt +202 -0
  462. package/skills/skills/slack-gif-creator/SKILL.md +254 -0
  463. package/skills/skills/slack-gif-creator/core/easing.py +234 -0
  464. package/skills/skills/slack-gif-creator/core/frame_composer.py +176 -0
  465. package/skills/skills/slack-gif-creator/core/gif_builder.py +269 -0
  466. package/skills/skills/slack-gif-creator/core/validators.py +136 -0
  467. package/skills/skills/slack-gif-creator/requirements.txt +4 -0
  468. package/skills/skills/theme-factory/LICENSE.txt +202 -0
  469. package/skills/skills/theme-factory/SKILL.md +59 -0
  470. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  471. package/skills/skills/theme-factory/themes/arctic-frost.md +19 -0
  472. package/skills/skills/theme-factory/themes/botanical-garden.md +19 -0
  473. package/skills/skills/theme-factory/themes/desert-rose.md +19 -0
  474. package/skills/skills/theme-factory/themes/forest-canopy.md +19 -0
  475. package/skills/skills/theme-factory/themes/golden-hour.md +19 -0
  476. package/skills/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  477. package/skills/skills/theme-factory/themes/modern-minimalist.md +19 -0
  478. package/skills/skills/theme-factory/themes/ocean-depths.md +19 -0
  479. package/skills/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  480. package/skills/skills/theme-factory/themes/tech-innovation.md +19 -0
  481. package/skills/skills/web-artifacts-builder/LICENSE.txt +202 -0
  482. package/skills/skills/web-artifacts-builder/SKILL.md +74 -0
  483. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  484. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  485. package/skills/skills/webapp-testing/LICENSE.txt +202 -0
  486. package/skills/skills/webapp-testing/SKILL.md +96 -0
  487. package/skills/skills/webapp-testing/examples/console_logging.py +35 -0
  488. package/skills/skills/webapp-testing/examples/element_discovery.py +40 -0
  489. package/skills/skills/webapp-testing/examples/static_html_automation.py +33 -0
  490. package/skills/skills/webapp-testing/scripts/with_server.py +106 -0
  491. package/skills/skills/xlsx/LICENSE.txt +30 -0
  492. package/skills/skills/xlsx/SKILL.md +289 -0
  493. package/skills/skills/xlsx/recalc.py +178 -0
  494. package/skills/spec/agent-skills-spec.md +3 -0
  495. package/skills/template/SKILL.md +6 -0
  496. package/src/agents.ts +504 -0
  497. package/src/ai-client.ts +1456 -0
  498. package/src/auth.ts +648 -0
  499. package/src/cancellation.ts +176 -0
  500. package/src/checkpoint.ts +219 -0
  501. package/src/cli.ts +384 -0
  502. package/src/config.ts +248 -0
  503. package/src/context-compressor.ts +290 -0
  504. package/src/conversation.ts +288 -0
  505. package/src/gui-subagent/action-parser/actionParser.ts +312 -0
  506. package/src/gui-subagent/action-parser/constants.ts +12 -0
  507. package/src/gui-subagent/action-parser/index.ts +6 -0
  508. package/src/gui-subagent/action-parser/types.ts +31 -0
  509. package/src/gui-subagent/agent/gui-agent.ts +982 -0
  510. package/src/gui-subagent/agent/index.ts +5 -0
  511. package/src/gui-subagent/index.ts +139 -0
  512. package/src/gui-subagent/operator/base-operator.ts +246 -0
  513. package/src/gui-subagent/operator/computer-operator.ts +520 -0
  514. package/src/gui-subagent/operator/index.ts +7 -0
  515. package/src/gui-subagent/types/actions.ts +263 -0
  516. package/src/gui-subagent/types/index.ts +6 -0
  517. package/src/gui-subagent/types/operator.ts +106 -0
  518. package/src/gui-subagent/utils.ts +51 -0
  519. package/src/index.ts +18 -0
  520. package/src/input-processor.ts +282 -0
  521. package/src/logger.ts +438 -0
  522. package/src/mcp.ts +563 -0
  523. package/src/memory.ts +303 -0
  524. package/src/session-manager.ts +308 -0
  525. package/src/session.ts +1280 -0
  526. package/src/skill-invoker.ts +1888 -0
  527. package/src/skill-loader.ts +476 -0
  528. package/src/slash-commands.ts +1150 -0
  529. package/src/smart-approval.ts +595 -0
  530. package/src/system-prompt-generator.ts +786 -0
  531. package/src/theme.ts +455 -0
  532. package/src/tools.ts +3398 -0
  533. package/src/types.ts +198 -0
  534. package/src/update.ts +270 -0
  535. package/src/workflow.ts +508 -0
  536. package/tsconfig.json +22 -0
  537. package/vitest.config.ts +19 -0
package/dist/tools.js ADDED
@@ -0,0 +1,2906 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import readline from 'readline';
4
+ import { exec, spawn } from 'child_process';
5
+ import { promisify } from 'util';
6
+ import { glob } from 'glob';
7
+ import axios from 'axios';
8
+ import inquirer from 'inquirer';
9
+ import { ExecutionMode, AuthType } from './types.js';
10
+ import { colors, icons } from './theme.js';
11
+ import { getCancellationManager } from './cancellation.js';
12
+ import { getLogger } from './logger.js';
13
+ import { SystemPromptGenerator } from './system-prompt-generator.js';
14
+ const execAsync = promisify(exec);
15
+ export class ReadTool {
16
+ name = 'Read';
17
+ description = `Read the contents of a file. This is your PRIMARY tool for understanding existing code, configuration, and documentation.
18
+
19
+ # When to Use
20
+ - When you need to understand existing code before making changes
21
+ - When user asks you to "read", "show", "view", or "check" a file
22
+ - When debugging and need to inspect source files
23
+ - When analyzing project structure by reading key files
24
+ - When examining configuration files (package.json, tsconfig.json, etc.)
25
+ - When checking documentation or README files
26
+
27
+ # When NOT to Use
28
+ - For files you've already read in the same conversation (use memory instead)
29
+ - When you only need file metadata (use ListDirectory or Bash with ls instead)
30
+ - For binary files that cannot be read as text
31
+
32
+ # Parameters
33
+ - \`filePath\`: Absolute path or path relative to project root
34
+ - \`offset\`: (Optional) Line number to start reading from (0-based)
35
+ - \`limit\`: (Optional) Maximum number of lines to read
36
+
37
+ # Examples
38
+ - Read specific file: Read(filePath="/path/to/file.ts")
39
+ - Read with pagination: Read(filePath="src/app.ts", offset=0, limit=100)
40
+
41
+ # Best Practices
42
+ - Use absolute paths or paths relative to the project root
43
+ - Use offset and limit for large files to avoid loading entire content
44
+ - Combine with ListDirectory to explore project structure first
45
+ - Don't re-read files unnecessarily`;
46
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
47
+ async execute(params) {
48
+ const { filePath, offset = 0, limit } = params;
49
+ try {
50
+ // Handle ~ (user home directory) in file paths
51
+ let resolvedPath = filePath;
52
+ if (filePath.startsWith('~')) {
53
+ // On Windows, prefer USERPROFILE over HOME to avoid POSIX path issues
54
+ // Some tools like Git Bash may set HOME to a POSIX path on Windows
55
+ let homeDir = process.env.USERPROFILE || '';
56
+ if (!homeDir || homeDir.startsWith('/')) {
57
+ homeDir = process.env.HOME || process.env.USERPROFILE || '';
58
+ }
59
+ resolvedPath = path.join(homeDir, filePath.slice(1));
60
+ }
61
+ const absolutePath = path.resolve(resolvedPath);
62
+ const content = await fs.readFile(absolutePath, 'utf-8');
63
+ const lines = content.split('\n');
64
+ const startLine = Math.max(0, offset);
65
+ const endLine = limit !== undefined ? Math.min(lines.length, startLine + limit) : lines.length;
66
+ const selectedLines = lines.slice(startLine, endLine);
67
+ return selectedLines.join('\n');
68
+ }
69
+ catch (error) {
70
+ // Show user-friendly path in error message
71
+ let displayPath = filePath;
72
+ if (filePath.startsWith('~')) {
73
+ // On Windows, prefer USERPROFILE over HOME to avoid POSIX path issues
74
+ let homeDir = process.env.USERPROFILE || '';
75
+ if (!homeDir || homeDir.startsWith('/')) {
76
+ homeDir = process.env.HOME || process.env.USERPROFILE || '';
77
+ }
78
+ displayPath = path.join(homeDir, filePath.slice(1));
79
+ }
80
+ throw new Error(`Failed to read file ${displayPath}: ${error.message}`);
81
+ }
82
+ }
83
+ }
84
+ export class WriteTool {
85
+ name = 'Write';
86
+ description = `Create a new file or completely overwrite an existing file with new content.
87
+
88
+ # When to Use
89
+ - Creating new files (source code, configuration, documentation)
90
+ - Completely replacing file content (not partial edits)
91
+ - Generating files from templates or scratch
92
+ - When user explicitly asks to "create", "write", or "generate" a file
93
+
94
+ # When NOT to Use
95
+ - For making small edits to existing files (use Replace instead)
96
+ - When you only need to append content (read file first, then write)
97
+ - For creating directories (use CreateDirectory instead)
98
+
99
+ # Parameters
100
+ - \`filePath\`: Absolute path or path relative to project root
101
+ - \`content\`: The complete content to write to the file
102
+
103
+ # Examples
104
+ - Create new file: Write(filePath="src/utils.ts", content="...")
105
+ - Create config file: Write(filePath=".env.example", content="API_KEY=...")
106
+
107
+ # Best Practices
108
+ - Parent directories are created automatically
109
+ - Use appropriate file extensions
110
+ - Ensure content is complete and syntactically correct
111
+ - For partial edits, use Replace tool instead`;
112
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
113
+ async execute(params) {
114
+ const { filePath, content } = params;
115
+ try {
116
+ const absolutePath = path.resolve(filePath);
117
+ const dir = path.dirname(absolutePath);
118
+ await fs.mkdir(dir, { recursive: true });
119
+ await fs.writeFile(absolutePath, content, 'utf-8');
120
+ return {
121
+ success: true,
122
+ message: `Successfully wrote to ${filePath}`
123
+ };
124
+ }
125
+ catch (error) {
126
+ throw new Error(`Failed to write file ${filePath}: ${error.message}`);
127
+ }
128
+ }
129
+ }
130
+ export class GrepTool {
131
+ name = 'Grep';
132
+ 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.
133
+
134
+ # When to Use
135
+ - Finding specific function definitions or calls
136
+ - Searching for variable usages or imports
137
+ - Locating error messages or log statements
138
+ - Finding all occurrences of a pattern across the codebase
139
+ - When you need line-by-line results with context
140
+
141
+ # When NOT to Use
142
+ - When you only need to find files containing text (use SearchCodebase instead)
143
+ - When searching by file pattern rather than content (use SearchCodebase)
144
+ - For very large codebases where you only need file names (SearchCodebase is faster)
145
+
146
+ # Parameters
147
+ - \`pattern\`: Regex or literal string to search for
148
+ - \`path\`: (Optional) Directory to search in, default: "."
149
+ - \`include\`: (Optional) File glob pattern to include
150
+ - \`exclude\`: (Optional) File glob pattern to exclude
151
+ - \`case_sensitive\`: (Optional) Case-sensitive search, default: false
152
+ - \`fixed_strings\`: (Optional) Treat pattern as literal string, default: false
153
+ - \`context\`: (Optional) Lines of context before/after matches
154
+ - \`no_ignore\`: (Optional) Don't ignore node_modules/.git, default: false
155
+
156
+ # Examples
157
+ - Find function: Grep(pattern="function myFunction")
158
+ - Find with context: Grep(pattern="TODO", context=3)
159
+ - TypeScript only: Grep(pattern="interface", include="*.ts")
160
+
161
+ # Best Practices
162
+ - Use case_sensitive=true for short patterns to reduce false positives
163
+ - Use fixed_strings=true if your pattern has special regex characters
164
+ - Use context to see the surrounding code for each match
165
+ - Combine with include/exclude to narrow down file types`;
166
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
167
+ async execute(params) {
168
+ const { pattern, path: searchPath = '.', include, exclude, case_sensitive = false, fixed_strings = false, context, after, before, no_ignore = false } = params;
169
+ try {
170
+ const ignorePatterns = no_ignore ? [] : ['node_modules/**', '.git/**', 'dist/**', 'build/**'];
171
+ if (exclude) {
172
+ ignorePatterns.push(exclude);
173
+ }
174
+ const absolutePath = path.resolve(searchPath);
175
+ const files = await glob('**/*', {
176
+ cwd: absolutePath,
177
+ nodir: true,
178
+ ignore: ignorePatterns
179
+ });
180
+ const results = [];
181
+ for (const file of files) {
182
+ const fullPath = path.join(absolutePath, file);
183
+ if (include && !file.match(include)) {
184
+ continue;
185
+ }
186
+ try {
187
+ const content = await fs.readFile(fullPath, 'utf-8');
188
+ const lines = content.split('\n');
189
+ lines.forEach((line, index) => {
190
+ let matches = false;
191
+ if (fixed_strings) {
192
+ matches = case_sensitive
193
+ ? line.includes(pattern)
194
+ : line.toLowerCase().includes(pattern.toLowerCase());
195
+ }
196
+ else {
197
+ try {
198
+ const flags = case_sensitive ? 'g' : 'gi';
199
+ const regex = new RegExp(pattern, flags);
200
+ matches = regex.test(line);
201
+ }
202
+ catch (e) {
203
+ matches = case_sensitive
204
+ ? line.includes(pattern)
205
+ : line.toLowerCase().includes(pattern.toLowerCase());
206
+ }
207
+ }
208
+ if (matches) {
209
+ const contextLines = [];
210
+ if (before || context) {
211
+ const beforeCount = before || context || 0;
212
+ for (let i = Math.max(0, index - beforeCount); i < index; i++) {
213
+ contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
214
+ }
215
+ }
216
+ contextLines.push(`${fullPath}:${index + 1}:${line.trim()}`);
217
+ if (after || context) {
218
+ const afterCount = after || context || 0;
219
+ for (let i = index + 1; i < Math.min(lines.length, index + 1 + afterCount); i++) {
220
+ contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
221
+ }
222
+ }
223
+ results.push(...contextLines);
224
+ }
225
+ });
226
+ }
227
+ catch (error) {
228
+ continue;
229
+ }
230
+ }
231
+ return results;
232
+ }
233
+ catch (error) {
234
+ throw new Error(`Grep failed: ${error.message}`);
235
+ }
236
+ }
237
+ }
238
+ export class BashTool {
239
+ name = 'Bash';
240
+ description = `Execute shell commands in the terminal. This is your PRIMARY tool for running commands, scripts, and system operations.
241
+
242
+ # When to Use
243
+ - Running build commands (npm run build, tsc, etc.)
244
+ - Installing dependencies (npm install, pip install, etc.)
245
+ - Running tests (npm test, pytest, etc.)
246
+ - Git operations (git commit, git push, etc.)
247
+ - Running linters or formatters
248
+ - Any command-line operations
249
+
250
+ # When NOT to Use
251
+ - For file operations (use Read/Write/Replace/CreateDirectory instead)
252
+ - For searching file content (use Grep instead)
253
+ - For finding files (use SearchCodebase or ListDirectory instead)
254
+ - For commands that require user interaction (non-interactive only)
255
+ - For dangerous commands without understanding the impact
256
+
257
+ # Parameters
258
+ - \`command\`: The shell command to execute
259
+ - \`cwd\`: (Optional) Working directory for the command
260
+ - \`description\`: (Optional) Description of what the command does
261
+ - \`timeout\`: (Optional) Timeout in seconds, default: 120
262
+ - \`run_in_bg\`: (Optional) Run in background, default: false
263
+
264
+ # Examples
265
+ - Install dependencies: Bash(command="npm install", description="Install npm dependencies")
266
+ - Run tests: Bash(command="npm test", description="Run unit tests")
267
+ - Build project: Bash(command="npm run build", description="Build the project")
268
+
269
+ # Best Practices
270
+ - Always provide a description for context
271
+ - Set appropriate timeout for long-running commands
272
+ - Use run_in_bg=true for commands that take a long time
273
+ - Check the command is safe before executing
274
+ - Use absolute paths or paths relative to project root`;
275
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
276
+ async execute(params) {
277
+ const { command, cwd, description, timeout = 120, run_in_bg = false } = params;
278
+ // Determine effective working directory
279
+ // Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
280
+ let effectiveCwd;
281
+ const hasCdCommand = /cd\s+["']?[^"&|;]+["']?/.test(command);
282
+ if (cwd && !hasCdCommand) {
283
+ // Command doesn't control its own directory, use provided cwd
284
+ effectiveCwd = cwd;
285
+ }
286
+ else if (cwd && hasCdCommand) {
287
+ // Command uses cd, ignore cwd to let cd take effect
288
+ effectiveCwd = undefined;
289
+ }
290
+ else {
291
+ // No cwd provided, use default
292
+ effectiveCwd = undefined;
293
+ }
294
+ try {
295
+ if (run_in_bg) {
296
+ const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
297
+ const childProcess = spawn(command, {
298
+ cwd: effectiveCwd || process.cwd(),
299
+ shell: true,
300
+ detached: true
301
+ });
302
+ const output = [];
303
+ childProcess.stdout?.on('data', (data) => {
304
+ const text = data.toString();
305
+ output.push(text);
306
+ });
307
+ childProcess.stderr?.on('data', (data) => {
308
+ const text = data.toString();
309
+ output.push(text);
310
+ });
311
+ childProcess.on('close', (code) => {
312
+ console.log(`Background task ${taskId} exited with code ${code}`);
313
+ });
314
+ const toolRegistry = getToolRegistry();
315
+ toolRegistry.addBackgroundTask(taskId, {
316
+ process: childProcess,
317
+ startTime: Date.now(),
318
+ output
319
+ });
320
+ return {
321
+ stdout: '',
322
+ stderr: '',
323
+ exitCode: 0,
324
+ taskId
325
+ };
326
+ }
327
+ else {
328
+ const { stdout, stderr } = await execAsync(command, {
329
+ cwd: effectiveCwd || process.cwd(),
330
+ maxBuffer: 1024 * 1024 * 10,
331
+ timeout: timeout * 1000
332
+ });
333
+ return {
334
+ stdout,
335
+ stderr,
336
+ exitCode: 0
337
+ };
338
+ }
339
+ }
340
+ catch (error) {
341
+ return {
342
+ stdout: error.stdout || '',
343
+ stderr: error.stderr || error.message,
344
+ exitCode: error.code || 1
345
+ };
346
+ }
347
+ }
348
+ }
349
+ export class ListDirectoryTool {
350
+ name = 'ListDirectory';
351
+ description = `List files and directories in a path. This is your PRIMARY tool for exploring project structure.
352
+
353
+ # When to Use
354
+ - Exploring project structure and organization
355
+ - Finding what files exist in a directory
356
+ - Getting an overview of the codebase layout
357
+ - When user asks to "list files" or "show directory contents"
358
+ - Navigating through project directories
359
+
360
+ # When NOT to Use
361
+ - When you need to read file contents (use Read instead)
362
+ - For recursive exploration of entire codebase (use recursive=true)
363
+ - When you need to search for specific files (use SearchCodebase instead)
364
+
365
+ # Parameters
366
+ - \`path\`: (Optional) Directory path, default: "."
367
+ - \`recursive\`: (Optional) List recursively, default: false
368
+
369
+ # Examples
370
+ - List current directory: ListDirectory(path=".")
371
+ - List src directory: ListDirectory(path="src")
372
+ - List all files recursively: ListDirectory(path=".", recursive=true)
373
+
374
+ # Best Practices
375
+ - Use recursive=true to see entire subtree
376
+ - Results are absolute paths
377
+ - Ignores node_modules and .git by default
378
+ - Combine with Read to examine file contents`;
379
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
380
+ async execute(params) {
381
+ const { path: dirPath = '.', recursive = false } = params;
382
+ try {
383
+ const absolutePath = path.resolve(dirPath);
384
+ const stats = await fs.stat(absolutePath).catch(() => null);
385
+ if (!stats || !stats.isDirectory()) {
386
+ throw new Error(`Directory does not exist: ${dirPath}`);
387
+ }
388
+ const pattern = recursive ? '**/*' : '*';
389
+ const files = await glob(pattern, {
390
+ cwd: absolutePath,
391
+ nodir: false,
392
+ ignore: ['node_modules/**', '.git/**']
393
+ });
394
+ return files.map(file => path.join(absolutePath, file));
395
+ }
396
+ catch (error) {
397
+ throw new Error(`Failed to list directory: ${error.message}`);
398
+ }
399
+ }
400
+ }
401
+ export class SearchCodebaseTool {
402
+ name = 'SearchCodebase';
403
+ description = `Search for files matching a glob pattern. This is your PRIMARY tool for finding files by name or extension.
404
+
405
+ # When to Use
406
+ - Finding all files of a certain type (*.ts, *.json, *.md)
407
+ - Locating files in specific directories or subdirectories
408
+ - Finding configuration files, test files, or source files
409
+ - When you need a list of file paths, not content
410
+
411
+ # When NOT to Use
412
+ - When you need to search file contents (use Grep instead)
413
+ - When you need to find specific text within files (use Grep instead)
414
+ - For searching non-file patterns (use Grep or Bash)
415
+
416
+ # Parameters
417
+ - \`pattern\`: Glob pattern (e.g., "**/*.ts", "src/**/*.test.ts")
418
+ - \`path\`: (Optional) Directory to search in, default: "."
419
+
420
+ # Examples
421
+ - Find all TypeScript files: SearchCodebase(pattern="**/*.ts")
422
+ - Find test files: SearchCodebase(pattern="**/*.test.ts")
423
+ - Find config files: SearchCodebase(pattern="**/config.*")
424
+
425
+ # Glob Patterns
426
+ - \`*\` matches any characters except /
427
+ - \`**\` matches any characters including /
428
+ - \`?\` matches single character
429
+ - Use brackets for character classes: [abc]
430
+
431
+ # Best Practices
432
+ - Use **/*.ts for recursive search in all directories
433
+ - Combine with path parameter to search specific directories
434
+ - Results are file paths, not content (use Grep on results if needed)`;
435
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
436
+ async execute(params) {
437
+ const { pattern, path: searchPath = '.' } = params;
438
+ try {
439
+ const files = await glob(pattern, {
440
+ cwd: searchPath,
441
+ ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
442
+ });
443
+ return files;
444
+ }
445
+ catch (error) {
446
+ throw new Error(`Search failed: ${error.message}`);
447
+ }
448
+ }
449
+ }
450
+ export class DeleteFileTool {
451
+ name = 'DeleteFile';
452
+ description = `Delete a file from the filesystem.
453
+
454
+ # When to Use
455
+ - Removing temporary or debug files
456
+ - Cleaning up generated files
457
+ - Removing files as part of a refactoring task
458
+ - When user explicitly requests file deletion
459
+
460
+ # When NOT to Use
461
+ - For removing directories (use Bash with rm -rf instead)
462
+ - When uncertain if a file should be deleted (confirm with user first)
463
+ - For removing important source files without explicit user request
464
+
465
+ # Parameters
466
+ - \`filePath\`: Absolute path to the file to delete
467
+
468
+ # Examples
469
+ - Delete temporary file: DeleteFile(filePath="debug.log")
470
+ - Remove unused file: DeleteFile(filePath="src/old-component.tsx")
471
+
472
+ # Best Practices
473
+ - Ensure you have the correct file path
474
+ - Consider if the file might be needed later
475
+ - This action is irreversible - be certain before executing`;
476
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
477
+ async execute(params) {
478
+ const { filePath } = params;
479
+ try {
480
+ const absolutePath = path.resolve(filePath);
481
+ await fs.unlink(absolutePath);
482
+ return {
483
+ success: true,
484
+ message: `Successfully deleted ${filePath}`
485
+ };
486
+ }
487
+ catch (error) {
488
+ throw new Error(`Failed to delete file ${filePath}: ${error.message}`);
489
+ }
490
+ }
491
+ }
492
+ export class CreateDirectoryTool {
493
+ name = 'CreateDirectory';
494
+ description = `Create a new directory (folder) in the filesystem.
495
+
496
+ # When to Use
497
+ - Creating project structure (src/components, tests/unit, etc.)
498
+ - Setting up directories for new features or modules
499
+ - Organizing files into appropriate folders
500
+ - When user requests to create a folder structure
501
+
502
+ # When NOT to Use
503
+ - For creating parent directories while writing files (Write tool does this automatically)
504
+ - For creating multiple nested directories at once (create step by step or use Bash)
505
+
506
+ # Parameters
507
+ - \`dirPath\`: Path of the directory to create
508
+ - \`recursive\`: (Optional, default: true) Create parent directories if they don't exist
509
+
510
+ # Examples
511
+ - Create single directory: CreateDirectory(dirPath="src/utils")
512
+ - Create nested structure: CreateDirectory(dirPath="src/components/buttons", recursive=true)
513
+
514
+ # Best Practices
515
+ - recursive=true (default) creates all intermediate parent directories
516
+ - Use appropriate naming conventions (kebab-case for directories)
517
+ - Consider the overall project structure before creating`;
518
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
519
+ async execute(params) {
520
+ const { dirPath, recursive = true } = params;
521
+ try {
522
+ const absolutePath = path.resolve(dirPath);
523
+ await fs.mkdir(absolutePath, { recursive });
524
+ return {
525
+ success: true,
526
+ message: `Successfully created directory ${dirPath}`
527
+ };
528
+ }
529
+ catch (error) {
530
+ throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
531
+ }
532
+ }
533
+ }
534
+ export class ReplaceTool {
535
+ name = 'replace';
536
+ description = `Replace specific text within an existing file. This is your PRIMARY tool for making targeted edits to code.
537
+
538
+ # When to Use
539
+ - Modifying specific code sections without rewriting entire files
540
+ - Changing function implementations, variable values, or configurations
541
+ - Fixing bugs by editing specific lines
542
+ - Updating imports, exports, or references
543
+
544
+ # When NOT to Use
545
+ - When you need to create a completely new file (use Write instead)
546
+ - When you want to append content to a file (read first, then Write)
547
+ - When making changes across multiple files (use Grep to find, then Replace individually)
548
+
549
+ # Parameters
550
+ - \`file_path\`: Path to the file to edit
551
+ - \`instruction\`: Description of what to change (for your own tracking)
552
+ - \`old_string\`: The exact text to find and replace (must match exactly)
553
+ - \`new_string\`: The new text to replace with
554
+
555
+ # Critical Requirements
556
+ - \`old_string\` MUST be an EXACT match, including whitespace and indentation
557
+ - Include at least 3 lines of context before and after the target text
558
+ - Ensure unique matching to avoid unintended replacements
559
+
560
+ # Examples
561
+ replace(
562
+ file_path="src/app.ts",
563
+ instruction="Update API endpoint",
564
+ old_string="const API_URL = 'https://api.old.com';",
565
+ new_string="const API_URL = 'https://api.new.com';"
566
+ )
567
+
568
+ # Best Practices
569
+ - Read the file first to understand the exact content
570
+ - Include sufficient context in old_string to ensure unique match
571
+ - Be careful with special regex characters in old_string (they're escaped automatically)
572
+ - If multiple occurrences exist, all will be replaced`;
573
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
574
+ async execute(params) {
575
+ const { file_path, instruction, old_string, new_string } = params;
576
+ try {
577
+ const absolutePath = path.resolve(file_path);
578
+ const content = await fs.readFile(absolutePath, 'utf-8');
579
+ const occurrences = (content.match(new RegExp(this.escapeRegExp(old_string), 'g')) || []).length;
580
+ if (occurrences === 0) {
581
+ return {
582
+ success: false,
583
+ message: `No occurrences found to replace in ${file_path}`,
584
+ changes: 0
585
+ };
586
+ }
587
+ const newContent = content.replace(new RegExp(this.escapeRegExp(old_string), 'g'), new_string);
588
+ await fs.writeFile(absolutePath, newContent, 'utf-8');
589
+ return {
590
+ success: true,
591
+ message: `Successfully replaced ${occurrences} occurrence(s) in ${file_path}`,
592
+ changes: occurrences
593
+ };
594
+ }
595
+ catch (error) {
596
+ throw new Error(`Failed to replace in file ${file_path}: ${error.message}`);
597
+ }
598
+ }
599
+ escapeRegExp(string) {
600
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
601
+ }
602
+ }
603
+ export class WebSearchTool {
604
+ name = 'web_search';
605
+ description = `Search the web for information. This tool queries a search API to find relevant results.
606
+
607
+ # When to Use
608
+ - When you need current information not in your training data
609
+ - Finding documentation, tutorials, or guides
610
+ - Researching APIs, libraries, or tools
611
+ - Getting up-to-date information on technical topics
612
+ - When user asks for "latest", "recent", or "current" information
613
+
614
+ # When NOT to Use
615
+ - When information is likely in the codebase or project files
616
+ - For information that doesn't change frequently (check docs first)
617
+ - When you can use web_fetch with a known URL instead
618
+ - For purely conversational queries
619
+
620
+ # Parameters
621
+ - \`query\`: Search query string
622
+
623
+ # Examples
624
+ - Find React documentation: web_search(query="React useEffect documentation")
625
+ - Get latest Node.js version: web_search(query="Node.js latest LTS version 2024")
626
+
627
+ # Best Practices
628
+ - Be specific in your query for better results
629
+ - Combine with web_fetch to get full content from relevant URLs
630
+ - Use quotes for exact phrase matching
631
+ - Consider adding context like year or version in query`;
632
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
633
+ async execute(params) {
634
+ const { query } = params;
635
+ try {
636
+ const configManager = await import('./config.js');
637
+ const { getConfigManager } = configManager;
638
+ const config = getConfigManager();
639
+ const searchApiKey = config.get('searchApiKey');
640
+ const baseUrl = config.get('baseUrl') || 'https://apis.xagent.cn/v1';
641
+ if (!searchApiKey) {
642
+ throw new Error('Search API key not configured. Please set searchApiKey in settings.');
643
+ }
644
+ const response = await axios.post(`${baseUrl}/search`, { query }, {
645
+ headers: {
646
+ 'Authorization': `Bearer ${searchApiKey}`,
647
+ 'Content-Type': 'application/json'
648
+ },
649
+ timeout: 30000
650
+ });
651
+ return {
652
+ results: response.data.results || [],
653
+ message: `Found ${response.data.results?.length || 0} results for "${query}"`
654
+ };
655
+ }
656
+ catch (error) {
657
+ throw new Error(`Web search failed: ${error.message}`);
658
+ }
659
+ }
660
+ }
661
+ export class TodoWriteTool {
662
+ name = 'todo_write';
663
+ description = `Create and manage structured task todo lists. Use this tool VERY frequently to track your progress and give users visibility into what needs to be done.
664
+
665
+ # When to Use
666
+ - Complex, multi-step tasks (3+ steps)
667
+ - User explicitly requests a todo list
668
+ - User provides multiple tasks to accomplish
669
+ - Immediately when starting work on a new feature
670
+ - After completing a task (update status immediately)
671
+ - Breaking down large features into smaller steps
672
+ - Tracking independent subtasks that can be worked on
673
+
674
+ # When NOT to Use
675
+ - Single, straightforward task
676
+ - Trivial operations in less than 3 steps
677
+ - Purely conversational or informational responses
678
+ - When you already have an up-to-date todo list
679
+
680
+ # Task States
681
+ - **pending** - Not started, waiting to be worked on
682
+ - **in_progress** - Currently working on (limit ONE at a time)
683
+ - **completed** - Finished successfully
684
+ - **failed** - Could not complete due to errors
685
+
686
+ # Task Descriptions
687
+ Each task needs:
688
+ - \`id\`: Unique identifier
689
+ - \`task\`: Clear, actionable description in imperative form (e.g., "Run tests")
690
+ - \`status\`: Current state
691
+ - \`priority\`: high/medium/low
692
+
693
+ # Examples
694
+ \`\`\`json
695
+ {
696
+ "todos": [
697
+ { "id": "1", "task": "Run the build and check for errors", "status": "in_progress", "priority": "high" },
698
+ { "id": "2", "task": "Fix any type errors found", "status": "pending", "priority": "high" },
699
+ { "id": "3", "task": "Write unit tests for new feature", "status": "pending", "priority": "medium" }
700
+ ]
701
+ }
702
+ \`\`\`
703
+
704
+ # Best Practices
705
+ - Mark tasks as completed IMMEDIATELY after finishing
706
+ - Don't batch multiple completions - update as you go
707
+ - Keep task descriptions clear and actionable
708
+ - Use appropriate priority levels to indicate urgency`;
709
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
710
+ todoList = [];
711
+ async execute(params) {
712
+ const { todos } = params;
713
+ try {
714
+ this.todoList = todos;
715
+ const summary = {
716
+ pending: todos.filter(t => t.status === 'pending').length,
717
+ in_progress: todos.filter(t => t.status === 'in_progress').length,
718
+ completed: todos.filter(t => t.status === 'completed').length,
719
+ failed: todos.filter(t => t.status === 'failed').length
720
+ };
721
+ return {
722
+ success: true,
723
+ message: `Updated todo list: ${summary.pending} pending, ${summary.in_progress} in progress, ${summary.completed} completed, ${summary.failed} failed`,
724
+ todos: this.todoList
725
+ };
726
+ }
727
+ catch (error) {
728
+ throw new Error(`Failed to update todo list: ${error.message}`);
729
+ }
730
+ }
731
+ getTodos() {
732
+ return this.todoList;
733
+ }
734
+ }
735
+ export class TodoReadTool {
736
+ name = 'todo_read';
737
+ description = `Read the current session's todo list and get a summary of all tasks. Use this to check what tasks remain and their current status.
738
+
739
+ # When to Use
740
+ - Before starting work to understand what needs to be done
741
+ - After completing a task to verify the todo list is updated
742
+ - When user asks about progress or remaining tasks
743
+ - To get an overview of task distribution (pending, in_progress, completed)
744
+
745
+ # What It Returns
746
+ - Full list of all todos with their IDs, tasks, statuses, and priorities
747
+ - Summary counts: total, pending, in_progress, completed, failed
748
+
749
+ # Examples
750
+ - User asks: "What are we working on right now?" → Use todo_read to show current state
751
+ - After a task completes → Check todo_read to confirm the list is accurate
752
+
753
+ # Best Practices
754
+ - Use todo_write to modify the list, not todo_read
755
+ - Check todo_read after todo_write to verify updates`;
756
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
757
+ todoWriteTool;
758
+ constructor(todoWriteTool) {
759
+ this.todoWriteTool = todoWriteTool;
760
+ }
761
+ async execute() {
762
+ try {
763
+ const todos = this.todoWriteTool.getTodos();
764
+ const summary = {
765
+ total: todos.length,
766
+ pending: todos.filter(t => t.status === 'pending').length,
767
+ in_progress: todos.filter(t => t.status === 'in_progress').length,
768
+ completed: todos.filter(t => t.status === 'completed').length,
769
+ failed: todos.filter(t => t.status === 'failed').length
770
+ };
771
+ return {
772
+ todos,
773
+ summary
774
+ };
775
+ }
776
+ catch (error) {
777
+ throw new Error(`Failed to read todo list: ${error.message}`);
778
+ }
779
+ }
780
+ }
781
+ export class TaskTool {
782
+ name = 'task';
783
+ description = `Launch specialized AI subagents to handle complex, multi-step tasks. Subagents are expert agents designed for specific domains like planning, code exploration, frontend testing, and more.
784
+
785
+ # When to Use
786
+ - Complex tasks requiring specialized expertise (planning, analysis, testing)
787
+ - Multi-step workflows that benefit from dedicated focus
788
+ - When you need to delegate work to avoid context overload
789
+ - Parallel execution of independent tasks across different domains
790
+ - User explicitly requests a specific type of agent (e.g., "use the frontend tester")
791
+
792
+ # Available SubAgents
793
+ 1. **plan-agent** - Task planning and breakdown, risk analysis, implementation roadmaps
794
+ 2. **explore-agent** - Codebase exploration, architecture analysis, finding specific code
795
+ 3. **frontend-tester** - Writing and running frontend tests, UI validation
796
+ 4. **code-reviewer** - Code review, security checks, bug detection
797
+ 5. **frontend-developer** - Frontend development (React, TypeScript, modern web)
798
+ 6. **backend-developer** - Backend development (Node.js, APIs, databases)
799
+ 7. **gui-subagent** - Browser automation, visual web interactions, desktop application automation
800
+
801
+ # When NOT to Use
802
+ - Simple, straightforward tasks you can handle directly
803
+ - Tasks that don't require specialized expertise
804
+ - Single-step operations (use other tools instead)
805
+
806
+ # Examples
807
+ - "Analyze the authentication module and create a security report" → explore-agent
808
+ - "Create a detailed implementation plan for feature X" → plan-agent
809
+ - "Write unit tests for this React component" → frontend-tester
810
+ - "Review my changes for potential bugs" → code-reviewer
811
+ - "Automatically fill out this form and navigate the website" → gui-subagent
812
+ - "Test the login process on the desktop application" → gui-subagent
813
+ - "send a message to the my mom on the desktop application wechat" → gui-subagent
814
+
815
+ # Best Practices
816
+ - Provide clear, specific prompts to subagents
817
+ - Include relevant context (file paths, requirements, constraints)
818
+ - Set appropriate executionMode if needed
819
+ - For parallel execution, ensure tasks are truly independent`;
820
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
821
+ async execute(params, _executionMode) {
822
+ const mode = params.executionMode || _executionMode || ExecutionMode.YOLO;
823
+ try {
824
+ const { getAgentManager } = await import('./agents.js');
825
+ const agentManager = getAgentManager(process.cwd());
826
+ const { getConfigManager } = await import('./config.js');
827
+ const config = getConfigManager();
828
+ const { AIClient } = await import('./ai-client.js');
829
+ const aiClient = new AIClient({
830
+ type: AuthType.API_KEY,
831
+ apiKey: config.get('apiKey'),
832
+ baseUrl: config.get('baseUrl'),
833
+ modelName: config.get('modelName') || 'Qwen3-Coder'
834
+ });
835
+ const toolRegistry = getToolRegistry();
836
+ if (params.agents && params.agents.length > 0) {
837
+ return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry, aiClient);
838
+ }
839
+ if (!params.subagent_type || !params.prompt) {
840
+ throw new Error('Either subagent_type and prompt, or agents array must be provided');
841
+ }
842
+ const result = await this.executeSingleAgent(params.subagent_type, params.prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry, aiClient, config);
843
+ return result;
844
+ }
845
+ catch (error) {
846
+ throw new Error(`Task execution failed: ${error.message}`);
847
+ }
848
+ }
849
+ /**
850
+ * Execute GUI subagent by directly calling GUIAgent.run()
851
+ * This bypasses the normal subagent message loop for better GUI control
852
+ */
853
+ async executeGUIAgent(prompt, description, agent, mode, config, indentLevel = 1) {
854
+ const indent = ' '.repeat(indentLevel);
855
+ const cancellationManager = getCancellationManager();
856
+ const logger = getLogger();
857
+ console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
858
+ console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
859
+ console.log('');
860
+ // Get model config for GUI agent
861
+ // Priority: guiSubagentBaseUrl (test first) -> baseUrl (fallback)
862
+ // When falling back to baseUrl, also use the corresponding modelName and apiKey
863
+ const primaryBaseUrl = config.get('guiSubagentBaseUrl') || '';
864
+ const fallbackBaseUrl = config.get('baseUrl') || '';
865
+ const primaryApiKey = config.get('guiSubagentApiKey') || '';
866
+ const fallbackApiKey = config.get('apiKey') || '';
867
+ const primaryModelName = config.get('guiSubagentModel') || '';
868
+ const fallbackModelName = config.get('modelName') || '';
869
+ let baseUrl = primaryBaseUrl;
870
+ let modelName = primaryModelName;
871
+ let apiKey = primaryApiKey;
872
+ // Test API availability (like curl) and choose the right baseUrl
873
+ if (primaryBaseUrl) {
874
+ try {
875
+ const controller = new AbortController();
876
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
877
+ const response = await fetch(`${primaryBaseUrl.replace(/\/v1\/?$/, '')}/models`, {
878
+ method: 'GET',
879
+ headers: primaryApiKey ? { 'Authorization': `Bearer ${primaryApiKey}` } : {},
880
+ signal: controller.signal
881
+ });
882
+ clearTimeout(timeoutId);
883
+ if (!response.ok) {
884
+ // Fallback to baseUrl with its corresponding model and API key
885
+ baseUrl = fallbackBaseUrl;
886
+ modelName = fallbackModelName;
887
+ apiKey = fallbackApiKey;
888
+ }
889
+ }
890
+ catch {
891
+ // Fallback to baseUrl with its corresponding model and API key
892
+ baseUrl = fallbackBaseUrl;
893
+ modelName = fallbackModelName;
894
+ apiKey = fallbackApiKey;
895
+ }
896
+ }
897
+ else {
898
+ baseUrl = fallbackBaseUrl;
899
+ modelName = fallbackModelName;
900
+ apiKey = fallbackApiKey;
901
+ }
902
+ if (!baseUrl) {
903
+ return {
904
+ success: false,
905
+ message: `GUI task "${description}" failed: No valid API URL configured`
906
+ };
907
+ }
908
+ // Set up stdin polling for ESC cancellation
909
+ let rawModeEnabled = false;
910
+ let stdinPollingInterval = null;
911
+ const setupStdinPolling = () => {
912
+ if (process.stdin.isTTY) {
913
+ try {
914
+ process.stdin.setRawMode(true);
915
+ rawModeEnabled = true;
916
+ process.stdin.resume();
917
+ readline.emitKeypressEvents(process.stdin);
918
+ }
919
+ catch (e) {
920
+ logger.debug(`[GUIAgent] Could not set raw mode: ${e}`);
921
+ }
922
+ stdinPollingInterval = setInterval(() => {
923
+ try {
924
+ if (rawModeEnabled) {
925
+ const chunk = process.stdin.read(1);
926
+ if (chunk && chunk.length > 0) {
927
+ const code = chunk[0];
928
+ if (code === 0x1B) { // ESC
929
+ logger.debug('[GUIAgent] ESC detected!');
930
+ cancellationManager.cancel();
931
+ }
932
+ }
933
+ }
934
+ }
935
+ catch (e) {
936
+ // Ignore polling errors
937
+ }
938
+ }, 10);
939
+ }
940
+ };
941
+ const cleanupStdinPolling = () => {
942
+ if (stdinPollingInterval) {
943
+ clearInterval(stdinPollingInterval);
944
+ stdinPollingInterval = null;
945
+ }
946
+ };
947
+ // Set up cancellation
948
+ let cancelled = false;
949
+ const cancelHandler = () => {
950
+ cancelled = true;
951
+ };
952
+ cancellationManager.on('cancelled', cancelHandler);
953
+ // Start polling for ESC
954
+ setupStdinPolling();
955
+ try {
956
+ // Import and create GUIAgent
957
+ const { createGUISubAgent } = await import('./gui-subagent/index.js');
958
+ const guiAgent = await createGUISubAgent({
959
+ model: modelName,
960
+ modelBaseUrl: baseUrl || undefined,
961
+ modelApiKey: apiKey || undefined,
962
+ maxLoopCount: 30,
963
+ loopIntervalInMs: 500,
964
+ showAIDebugInfo: config.get('showAIDebugInfo') || false,
965
+ });
966
+ // Add constraints to prompt if any
967
+ const fullPrompt = prompt;
968
+ // Execute GUI task - this will run autonomously until completion
969
+ const result = await guiAgent.run(fullPrompt);
970
+ // Cleanup
971
+ await guiAgent.cleanup();
972
+ // Check cancellation
973
+ if (cancelled || cancellationManager.isOperationCancelled()) {
974
+ cleanupStdinPolling();
975
+ cancellationManager.off('cancelled', cancelHandler);
976
+ // Flush stdout to prevent residual output after prompt
977
+ process.stdout.write('\n');
978
+ return {
979
+ success: true,
980
+ cancelled: true, // Mark as cancelled so main agent won't continue
981
+ message: `GUI task "${description}" cancelled by user`,
982
+ result: 'Task cancelled'
983
+ };
984
+ }
985
+ cleanupStdinPolling();
986
+ cancellationManager.off('cancelled', cancelHandler);
987
+ // Flush stdout to ensure all output is displayed before returning
988
+ process.stdout.write('\n');
989
+ // Return result based on GUIAgent status
990
+ if (result.status === 'end') {
991
+ const iterations = result.conversations.filter(c => c.from === 'human' && c.screenshotBase64).length;
992
+ console.log(`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`);
993
+ return {
994
+ success: true,
995
+ message: `GUI task "${description}" completed`,
996
+ result: `Completed in ${iterations} iterations`
997
+ };
998
+ }
999
+ else if (result.status === 'user_stopped') {
1000
+ return {
1001
+ success: true,
1002
+ message: `GUI task "${description}" stopped by user`,
1003
+ result: 'User stopped'
1004
+ };
1005
+ }
1006
+ else {
1007
+ // status is 'error' or other non-success status
1008
+ const errorMsg = result.error || 'Unknown error';
1009
+ return {
1010
+ success: false,
1011
+ message: `GUI task "${description}" failed: ${errorMsg}`
1012
+ };
1013
+ }
1014
+ }
1015
+ catch (error) {
1016
+ cleanupStdinPolling();
1017
+ cancellationManager.off('cancelled', cancelHandler);
1018
+ // Flush stdout to prevent residual output
1019
+ process.stdout.write('\n');
1020
+ // If the user cancelled the task, ignore any API errors (like 429)
1021
+ // and return cancelled status instead
1022
+ if (cancelled || cancellationManager.isOperationCancelled()) {
1023
+ return {
1024
+ success: true,
1025
+ cancelled: true, // Mark as cancelled so main agent won't continue
1026
+ message: `GUI task "${description}" cancelled by user`,
1027
+ result: 'Task cancelled'
1028
+ };
1029
+ }
1030
+ if (error.message === 'Operation cancelled by user') {
1031
+ return {
1032
+ success: true,
1033
+ message: `GUI task "${description}" cancelled by user`,
1034
+ result: 'Task cancelled'
1035
+ };
1036
+ }
1037
+ // Return failure without throwing - let the main agent handle it
1038
+ return {
1039
+ success: false,
1040
+ message: `GUI task "${description}" failed: ${error.message}`
1041
+ };
1042
+ }
1043
+ }
1044
+ async executeSingleAgent(subagent_type, prompt, description, useContext, constraints, mode, agentManager, toolRegistry, aiClient, config, indentLevel = 1) {
1045
+ const agent = agentManager.getAgent(subagent_type);
1046
+ if (!agent) {
1047
+ throw new Error(`Agent ${subagent_type} not found`);
1048
+ }
1049
+ // Special handling for gui-subagent: directly call GUIAgent.run() instead of subagent message loop
1050
+ if (subagent_type === 'gui-subagent') {
1051
+ return this.executeGUIAgent(prompt, description, agent, mode, config, indentLevel);
1052
+ }
1053
+ // Determine the model to use for this subagent
1054
+ let modelName = config.get('modelName') || 'Qwen3-Coder';
1055
+ let baseUrl = config.get('baseUrl') || 'https://apis.xagent.cn/v1';
1056
+ let apiKey = config.get('apiKey') || '';
1057
+ if (agent.model) {
1058
+ // If agent has a model field, it can be a model name or a config reference like 'guiSubagentModel'
1059
+ if (typeof agent.model === 'string' && agent.model.endsWith('Model')) {
1060
+ // It's a config reference, use corresponding config values
1061
+ modelName = config.get(agent.model) || modelName;
1062
+ const baseUrlKey = agent.model.replace('Model', 'BaseUrl');
1063
+ const apiKeyKey = agent.model.replace('Model', 'ApiKey');
1064
+ if (config.get(baseUrlKey)) {
1065
+ baseUrl = config.get(baseUrlKey);
1066
+ }
1067
+ if (config.get(apiKeyKey)) {
1068
+ apiKey = config.get(apiKeyKey);
1069
+ }
1070
+ }
1071
+ else if (typeof agent.model === 'string') {
1072
+ // It's an explicit model name
1073
+ modelName = agent.model;
1074
+ }
1075
+ }
1076
+ // Create a new AIClient for this subagent with its specific model
1077
+ const { AIClient: SubAgentAIClient } = await import('./ai-client.js');
1078
+ const subAgentClient = new SubAgentAIClient({
1079
+ type: AuthType.API_KEY,
1080
+ apiKey: apiKey,
1081
+ baseUrl: baseUrl,
1082
+ modelName: modelName,
1083
+ showAIDebugInfo: config.get('showAIDebugInfo') || false
1084
+ });
1085
+ const indent = ' '.repeat(indentLevel);
1086
+ const indentNext = ' '.repeat(indentLevel + 1);
1087
+ const agentName = agent.name || subagent_type;
1088
+ const cancellationManager = getCancellationManager();
1089
+ const logger = getLogger();
1090
+ let cancelled = false;
1091
+ // Set up raw mode and stdin polling for ESC detection
1092
+ let rawModeEnabled = false;
1093
+ let stdinPollingInterval = null;
1094
+ const setupStdinPolling = () => {
1095
+ if (process.stdin.isTTY) {
1096
+ try {
1097
+ process.stdin.setRawMode(true);
1098
+ rawModeEnabled = true;
1099
+ process.stdin.resume();
1100
+ readline.emitKeypressEvents(process.stdin);
1101
+ }
1102
+ catch (e) {
1103
+ logger.debug(`[TaskTool] Could not set raw mode: ${e}`);
1104
+ }
1105
+ // Start polling for ESC key (10ms interval for faster response)
1106
+ stdinPollingInterval = setInterval(() => {
1107
+ try {
1108
+ if (rawModeEnabled) {
1109
+ const chunk = process.stdin.read(1);
1110
+ if (chunk && chunk.length > 0) {
1111
+ const code = chunk[0];
1112
+ if (code === 0x1B) { // ESC
1113
+ logger.debug('[TaskTool] ESC detected via polling!');
1114
+ cancellationManager.cancel();
1115
+ }
1116
+ }
1117
+ }
1118
+ }
1119
+ catch (e) {
1120
+ // Ignore polling errors
1121
+ }
1122
+ }, 10);
1123
+ }
1124
+ };
1125
+ const cleanupStdinPolling = () => {
1126
+ if (stdinPollingInterval) {
1127
+ clearInterval(stdinPollingInterval);
1128
+ stdinPollingInterval = null;
1129
+ }
1130
+ };
1131
+ // Start polling for ESC
1132
+ setupStdinPolling();
1133
+ // Listen for cancellation
1134
+ const cancelHandler = () => {
1135
+ cancelled = true;
1136
+ };
1137
+ cancellationManager.on('cancelled', cancelHandler);
1138
+ // Helper function to indent multi-line content
1139
+ const indentMultiline = (content, baseIndent) => {
1140
+ return content.split('\n').map(line => `${baseIndent} ${line}`).join('\n');
1141
+ };
1142
+ // Check if operation is cancelled
1143
+ const checkCancellation = () => {
1144
+ if (cancelled || cancellationManager.isOperationCancelled()) {
1145
+ cancellationManager.off('cancelled', cancelHandler);
1146
+ cleanupStdinPolling();
1147
+ throw new Error('Operation cancelled by user');
1148
+ }
1149
+ };
1150
+ const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, mode, agent);
1151
+ const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(agent.systemPrompt);
1152
+ const fullPrompt = constraints.length > 0
1153
+ ? `${prompt}\n\nConstraints:\n${constraints.map(c => `- ${c}`).join('\n')}`
1154
+ : prompt;
1155
+ let messages = [
1156
+ { role: 'system', content: enhancedSystemPrompt },
1157
+ { role: 'user', content: fullPrompt }
1158
+ ];
1159
+ const availableTools = agentManager.getAvailableToolsForAgent(agent, mode);
1160
+ const allToolDefinitions = toolRegistry.getToolDefinitions();
1161
+ const toolDefinitions = availableTools.map((toolName) => {
1162
+ const fullDef = allToolDefinitions.find((def) => def.function.name === toolName);
1163
+ if (fullDef) {
1164
+ return fullDef;
1165
+ }
1166
+ return {
1167
+ type: 'function',
1168
+ function: {
1169
+ name: toolName,
1170
+ description: `Tool: ${toolName}`,
1171
+ parameters: { type: 'object', properties: {}, required: [] }
1172
+ }
1173
+ };
1174
+ });
1175
+ let iteration = 0;
1176
+ const maxIterations = 10;
1177
+ while (iteration < maxIterations) {
1178
+ iteration++;
1179
+ // Check for cancellation before each iteration
1180
+ checkCancellation();
1181
+ // Use withCancellation to make API call cancellable
1182
+ const result = await cancellationManager.withCancellation(subAgentClient.chatCompletion(messages, {
1183
+ tools: toolDefinitions,
1184
+ temperature: 0.7
1185
+ }), `api-${subagent_type}-${iteration}`);
1186
+ // Check for cancellation after API call
1187
+ checkCancellation();
1188
+ if (!result || !result.choices || result.choices.length === 0) {
1189
+ throw new Error(`Sub-agent ${subagent_type} returned empty response`);
1190
+ }
1191
+ const choice = result.choices[0];
1192
+ const messageContent = choice.message?.content;
1193
+ const toolCalls = choice.message.tool_calls;
1194
+ let contentStr;
1195
+ let hasValidContent = false;
1196
+ if (typeof messageContent === 'string') {
1197
+ contentStr = messageContent;
1198
+ hasValidContent = messageContent.trim() !== '';
1199
+ }
1200
+ else if (Array.isArray(messageContent)) {
1201
+ const textParts = messageContent
1202
+ .filter(item => typeof item?.text === 'string' && item.text.trim() !== '')
1203
+ .map(item => item.text);
1204
+ contentStr = textParts.join('');
1205
+ hasValidContent = textParts.length > 0;
1206
+ }
1207
+ else {
1208
+ contentStr = '';
1209
+ hasValidContent = false;
1210
+ }
1211
+ // Only throw empty content error if there's no text content AND no tool calls
1212
+ // When AI model returns tool_calls, message.content can be null/empty, which is valid
1213
+ if (!hasValidContent && (!toolCalls || toolCalls.length === 0)) {
1214
+ throw new Error(`Sub-agent ${subagent_type} returned empty content`);
1215
+ }
1216
+ if (choice.finish_reason === 'length') {
1217
+ throw new Error(`Sub-agent ${subagent_type} response truncated due to length limits`);
1218
+ }
1219
+ // Add assistant message to conversation
1220
+ messages.push({ role: 'assistant', content: contentStr });
1221
+ // Display assistant response (if there's any text content) with proper indentation
1222
+ if (contentStr) {
1223
+ console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
1224
+ const truncatedContent = contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
1225
+ const indentedContent = indentMultiline(truncatedContent, indent);
1226
+ console.log(`${indentedContent}\n`);
1227
+ }
1228
+ // Process tool calls with proper indentation
1229
+ if (toolCalls && toolCalls.length > 0) {
1230
+ for (const toolCall of toolCalls) {
1231
+ const { name, arguments: params } = toolCall.function;
1232
+ let parsedParams;
1233
+ try {
1234
+ parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
1235
+ }
1236
+ catch (e) {
1237
+ parsedParams = params;
1238
+ }
1239
+ console.log(`${indent}${colors.textMuted(`${icons.loading} Tool: ${name}`)}`);
1240
+ try {
1241
+ // Check cancellation before tool execution
1242
+ checkCancellation();
1243
+ const toolResult = await cancellationManager.withCancellation(toolRegistry.execute(name, parsedParams, mode, indent), `subagent-${subagent_type}-${name}-${iteration}`);
1244
+ // Display tool result with proper indentation for multi-line content
1245
+ const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
1246
+ const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
1247
+ const indentedPreview = indentMultiline(truncatedPreview, indent);
1248
+ console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
1249
+ messages.push({
1250
+ role: 'tool',
1251
+ content: JSON.stringify(toolResult),
1252
+ tool_call_id: toolCall.id
1253
+ });
1254
+ }
1255
+ catch (error) {
1256
+ if (error.message === 'Operation cancelled by user') {
1257
+ console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
1258
+ cancellationManager.off('cancelled', cancelHandler);
1259
+ cleanupStdinPolling();
1260
+ return {
1261
+ success: false,
1262
+ message: `Task "${description}" cancelled by user`,
1263
+ result: contentStr
1264
+ };
1265
+ }
1266
+ console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error.message}\n`);
1267
+ messages.push({
1268
+ role: 'tool',
1269
+ content: JSON.stringify({ error: error.message }),
1270
+ tool_call_id: toolCall.id
1271
+ });
1272
+ }
1273
+ }
1274
+ console.log('');
1275
+ continue; // Continue to next iteration to get final response
1276
+ }
1277
+ // No more tool calls, return the result
1278
+ cancellationManager.off('cancelled', cancelHandler);
1279
+ cleanupStdinPolling();
1280
+ return {
1281
+ success: true,
1282
+ message: `Task "${description}" completed by ${subagent_type}`,
1283
+ result: contentStr
1284
+ };
1285
+ }
1286
+ // Max iterations reached - return accumulated results instead of throwing error
1287
+ cancellationManager.off('cancelled', cancelHandler);
1288
+ cleanupStdinPolling();
1289
+ // Get the last assistant message content
1290
+ const lastAssistantMsg = messages.filter(m => m.role === 'assistant').pop();
1291
+ const lastContent = lastAssistantMsg?.content || '';
1292
+ return {
1293
+ success: true,
1294
+ message: `Task "${description}" completed (max iterations reached) by ${subagent_type}`,
1295
+ result: lastContent
1296
+ };
1297
+ }
1298
+ async executeParallelAgents(agents, description, mode, agentManager, toolRegistry, aiClient, indentLevel = 1) {
1299
+ const indent = ' '.repeat(indentLevel);
1300
+ const indentNext = ' '.repeat(indentLevel + 1);
1301
+ const cancellationManager = getCancellationManager();
1302
+ const logger = getLogger();
1303
+ // Set up raw mode and stdin polling for ESC detection
1304
+ let rawModeEnabled = false;
1305
+ let stdinPollingInterval = null;
1306
+ const setupStdinPolling = () => {
1307
+ if (process.stdin.isTTY) {
1308
+ try {
1309
+ process.stdin.setRawMode(true);
1310
+ rawModeEnabled = true;
1311
+ process.stdin.resume();
1312
+ readline.emitKeypressEvents(process.stdin);
1313
+ }
1314
+ catch (e) {
1315
+ logger.debug(`[ParallelAgents] Could not set raw mode: ${e}`);
1316
+ }
1317
+ stdinPollingInterval = setInterval(() => {
1318
+ try {
1319
+ if (rawModeEnabled) {
1320
+ const chunk = process.stdin.read(1);
1321
+ if (chunk && chunk.length > 0) {
1322
+ const code = chunk[0];
1323
+ if (code === 0x1B) { // ESC
1324
+ logger.debug('[ParallelAgents] ESC detected via polling!');
1325
+ cancellationManager.cancel();
1326
+ }
1327
+ }
1328
+ }
1329
+ }
1330
+ catch (e) {
1331
+ // Ignore polling errors
1332
+ }
1333
+ }, 10);
1334
+ }
1335
+ };
1336
+ const cleanupStdinPolling = () => {
1337
+ if (stdinPollingInterval) {
1338
+ clearInterval(stdinPollingInterval);
1339
+ stdinPollingInterval = null;
1340
+ }
1341
+ };
1342
+ // Start polling for ESC
1343
+ setupStdinPolling();
1344
+ // Listen for cancellation to stop parallel execution
1345
+ let cancelled = false;
1346
+ const cancelHandler = () => {
1347
+ cancelled = true;
1348
+ };
1349
+ cancellationManager.on('cancelled', cancelHandler);
1350
+ console.log(`\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`);
1351
+ const startTime = Date.now();
1352
+ const agentPromises = agents.map(async (agentTask, index) => {
1353
+ // Check if cancelled
1354
+ if (cancelled || cancellationManager.isOperationCancelled()) {
1355
+ return {
1356
+ success: false,
1357
+ agent: agentTask.subagent_type,
1358
+ description: agentTask.description,
1359
+ error: 'Operation cancelled by user'
1360
+ };
1361
+ }
1362
+ try {
1363
+ const result = await this.executeSingleAgent(agentTask.subagent_type, agentTask.prompt, agentTask.description, agentTask.useContext ?? true, agentTask.constraints || [], mode, agentManager, toolRegistry, aiClient, indentLevel + 1);
1364
+ return {
1365
+ success: true,
1366
+ agent: agentTask.subagent_type,
1367
+ description: agentTask.description,
1368
+ result: result.result
1369
+ };
1370
+ }
1371
+ catch (error) {
1372
+ return {
1373
+ success: false,
1374
+ agent: agentTask.subagent_type,
1375
+ description: agentTask.description,
1376
+ error: error.message
1377
+ };
1378
+ }
1379
+ });
1380
+ const results = await Promise.all(agentPromises);
1381
+ const duration = Date.now() - startTime;
1382
+ const successfulAgents = results.filter(r => r.success);
1383
+ const failedAgents = results.filter(r => !r.success);
1384
+ console.log(`${indent}${colors.success('✔')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`);
1385
+ console.log(`${indent}${colors.info('ℹ')} Success: ${successfulAgents.length}/${agents.length} agents\n`);
1386
+ if (failedAgents.length > 0) {
1387
+ console.log(`${indent}${colors.error('✖')} Failed agents:`);
1388
+ for (const failed of failedAgents) {
1389
+ console.log(`${indentNext} ${colors.error('•')} ${failed.agent}: ${failed.error}`);
1390
+ }
1391
+ console.log('');
1392
+ }
1393
+ // Cleanup
1394
+ cancellationManager.off('cancelled', cancelHandler);
1395
+ cleanupStdinPolling();
1396
+ return {
1397
+ success: failedAgents.length === 0,
1398
+ message: `Parallel task "${description}" completed: ${successfulAgents.length}/${agents.length} successful`,
1399
+ results: successfulAgents.map(r => ({
1400
+ agent: r.agent,
1401
+ description: r.description,
1402
+ result: r.result
1403
+ })),
1404
+ errors: failedAgents.map(r => ({
1405
+ agent: r.agent,
1406
+ description: r.description,
1407
+ error: r.error
1408
+ }))
1409
+ };
1410
+ }
1411
+ }
1412
+ export class ReadBashOutputTool {
1413
+ name = 'ReadBashOutput';
1414
+ description = `Retrieve output from a background task that was started with Bash(run_in_bg=true).
1415
+
1416
+ # When to Use
1417
+ - Checking the output of a long-running background process
1418
+ - Monitoring progress of builds, tests, or servers
1419
+ - Retrieving logs from background tasks
1420
+ - When you started a task with run_in_bg=true and need results
1421
+
1422
+ # When NOT to Use
1423
+ - For synchronous commands (they return output directly)
1424
+ - When the background task hasn't been started yet
1425
+ - For tasks that have already completed (use Bash directly)
1426
+
1427
+ # Parameters
1428
+ - \`task_id\`: The ID returned from the background Bash command
1429
+ - \`poll_interval\`: (Optional) Seconds to wait before checking, default: 10
1430
+
1431
+ # Examples
1432
+ - Check build output: ReadBashOutput(task_id="task_1234567890")
1433
+ - Wait and check: ReadBashOutput(task_id="task_123", poll_interval=5)
1434
+
1435
+ # Best Practices
1436
+ - Save the task_id from Bash response for later use
1437
+ - Use appropriate poll_interval based on expected task duration
1438
+ - Check status to see if task is still running or completed
1439
+ - Combine with todo_write to track background task progress`;
1440
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1441
+ async execute(params) {
1442
+ const { task_id, poll_interval = 10 } = params;
1443
+ try {
1444
+ const toolRegistry = getToolRegistry();
1445
+ const task = toolRegistry.getBackgroundTask(task_id);
1446
+ if (!task) {
1447
+ throw new Error(`Task ${task_id} not found`);
1448
+ }
1449
+ const interval = Math.min(Math.max(poll_interval, 1), 120);
1450
+ await new Promise(resolve => setTimeout(resolve, interval * 1000));
1451
+ const duration = Date.now() - task.startTime;
1452
+ const output = task.output.join('');
1453
+ const status = task.process.exitCode === null ? 'running' : 'completed';
1454
+ return {
1455
+ taskId: task_id,
1456
+ output,
1457
+ status,
1458
+ duration: Math.floor(duration / 1000)
1459
+ };
1460
+ }
1461
+ catch (error) {
1462
+ throw new Error(`Failed to read bash output: ${error.message}`);
1463
+ }
1464
+ }
1465
+ }
1466
+ export class WebFetchTool {
1467
+ name = 'web_fetch';
1468
+ description = `Fetch and extract content from a specific URL. This tool retrieves the full content of a webpage.
1469
+
1470
+ # When to Use
1471
+ - When you have a specific URL and need its content
1472
+ - Extracting documentation from web pages
1473
+ - Fetching API documentation or guides
1474
+ - Getting content from known URLs (not for searching)
1475
+
1476
+ # When NOT to Use
1477
+ - When you need to search but don't have a specific URL (use web_search first)
1478
+ - For pages requiring authentication or login
1479
+ - For very large files or pages (may timeout)
1480
+ - When the URL format is unknown (use web_search first)
1481
+
1482
+ # Parameters
1483
+ - \`prompt\`: A prompt containing the URL to fetch (e.g., "Summarize https://example.com/docs")
1484
+
1485
+ # Examples
1486
+ - Fetch documentation: web_fetch(prompt="Extract key points from https://react.dev/docs")
1487
+ - Get API spec: web_fetch(prompt="Fetch the OpenAPI spec from https://api.example.com/openapi.json")
1488
+
1489
+ # Best Practices
1490
+ - Ensure the URL is accessible and doesn't require authentication
1491
+ - Use specific prompts to extract relevant information
1492
+ - Check if the page is accessible if you get errors
1493
+ - Large pages may be truncated due to size limits`;
1494
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1495
+ async execute(params) {
1496
+ const { prompt } = params;
1497
+ try {
1498
+ const urlMatch = prompt.match(/https?:\/\/[^\s]+/i);
1499
+ if (!urlMatch) {
1500
+ throw new Error('No URL found in prompt');
1501
+ }
1502
+ const url = urlMatch[0];
1503
+ const response = await axios.get(url, {
1504
+ timeout: 30000,
1505
+ maxContentLength: 10 * 1024 * 1024,
1506
+ validateStatus: () => true
1507
+ });
1508
+ let content = response.data;
1509
+ if (typeof content === 'object') {
1510
+ content = JSON.stringify(content, null, 2);
1511
+ }
1512
+ return {
1513
+ content,
1514
+ url,
1515
+ status: response.status
1516
+ };
1517
+ }
1518
+ catch (error) {
1519
+ throw new Error(`Failed to fetch URL: ${error.message}`);
1520
+ }
1521
+ }
1522
+ }
1523
+ export class AskUserQuestionTool {
1524
+ name = 'ask_user_question';
1525
+ description = `Ask the user questions during execution to gather input, preferences, or clarifications.
1526
+
1527
+ # When to Use
1528
+ - When you need user input or preferences to proceed
1529
+ - When a task has multiple options and user should choose
1530
+ - When clarification is needed for ambiguous requests
1531
+ - When user explicitly asks to be prompted
1532
+
1533
+ # When NOT to Use
1534
+ - When you can make reasonable assumptions
1535
+ - For simple confirmations (just proceed with reasonable default)
1536
+ - When the information is already available in context
1537
+ - For information you should know or can infer
1538
+
1539
+ # Parameters
1540
+ - \`questions\`: Array of questions with:
1541
+ - \`question\`: The question text
1542
+ - \`header\`: (Optional) Short label for the question
1543
+ - \`options\`: (Optional) Multiple choice options
1544
+ - \`multiSelect\`: (Optional) Allow multiple selections
1545
+
1546
+ # Examples
1547
+ - Simple input: Ask user their preferred name
1548
+ - Multiple choice: Ask which framework to use (React, Vue, Angular)
1549
+ - Multi-select: Ask which features to include (with checkboxes)
1550
+
1551
+ # Best Practices
1552
+ - Limit to 1-4 questions at a time
1553
+ - Provide options when possible for faster response
1554
+ - Use multiSelect=true when multiple answers are valid
1555
+ - Be clear and concise in question wording`;
1556
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1557
+ async execute(params) {
1558
+ const { questions } = params;
1559
+ try {
1560
+ if (questions.length === 0 || questions.length > 4) {
1561
+ throw new Error('Must provide 1-4 questions');
1562
+ }
1563
+ const answers = [];
1564
+ for (const q of questions) {
1565
+ if (q.options && q.options.length > 0) {
1566
+ const result = await inquirer.prompt([
1567
+ {
1568
+ type: q.multiSelect ? 'checkbox' : 'list',
1569
+ name: 'answer',
1570
+ message: q.question,
1571
+ choices: q.options,
1572
+ default: q.multiSelect ? [] : q.options[0]
1573
+ }
1574
+ ]);
1575
+ answers.push(Array.isArray(result.answer) ? result.answer.join(', ') : result.answer);
1576
+ }
1577
+ else {
1578
+ const result = await inquirer.prompt([
1579
+ {
1580
+ type: 'input',
1581
+ name: 'answer',
1582
+ message: q.question
1583
+ }
1584
+ ]);
1585
+ answers.push(result.answer);
1586
+ }
1587
+ }
1588
+ return { answers };
1589
+ }
1590
+ catch (error) {
1591
+ throw new Error(`Failed to ask user questions: ${error.message}`);
1592
+ }
1593
+ }
1594
+ }
1595
+ export class SaveMemoryTool {
1596
+ name = 'save_memory';
1597
+ description = `Save specific information to long-term memory for future sessions. Useful for remembering user preferences, project conventions, or important facts.
1598
+
1599
+ # When to Use
1600
+ - User explicitly asks to "remember" something
1601
+ - User provides preferences or configuration details
1602
+ - Important project conventions or patterns to remember
1603
+ - Information that should persist across sessions
1604
+
1605
+ # When NOT to Use
1606
+ - For temporary information only needed in current session
1607
+ - For information already in project files or configuration
1608
+ - For obvious or trivial facts
1609
+ - When user doesn't explicitly want information saved
1610
+
1611
+ # Parameters
1612
+ - \`fact\`: The specific fact or information to remember
1613
+
1614
+ # Examples
1615
+ - Remember user preference: save_memory(fact="User prefers TypeScript over JavaScript")
1616
+ - Remember project convention: save_memory(fact="Project uses kebab-case for component files")
1617
+ - Remember important context: save_memory(fact="API endpoint is https://api.example.com/v2")
1618
+
1619
+ # Best Practices
1620
+ - Save only when user explicitly requests or provides clear preference
1621
+ - Keep facts concise and specific
1622
+ - Remember project-specific conventions for consistency
1623
+ - This persists across sessions (global memory)`;
1624
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1625
+ async execute(params) {
1626
+ const { fact } = params;
1627
+ try {
1628
+ const { getMemoryManager } = await import('./memory.js');
1629
+ const memoryManager = getMemoryManager(process.cwd());
1630
+ await memoryManager.saveMemory(fact, 'global');
1631
+ return {
1632
+ success: true,
1633
+ message: `Successfully saved fact to memory`
1634
+ };
1635
+ }
1636
+ catch (error) {
1637
+ throw new Error(`Failed to save memory: ${error.message}`);
1638
+ }
1639
+ }
1640
+ }
1641
+ export class ExitPlanModeTool {
1642
+ name = 'exit_plan_mode';
1643
+ description = `Complete plan presentation in plan mode and transition to execution. This tool is used when you have finished planning and are ready to implement.
1644
+
1645
+ # When to Use
1646
+ - When you have completed creating a plan or design document
1647
+ - When the plan is ready for review and execution
1648
+ - After presenting the full implementation plan to the user
1649
+ - When ready to transition from planning to coding
1650
+
1651
+ # When NOT to Use
1652
+ - When still in the middle of planning (continue planning first)
1653
+ - When the plan needs revision based on feedback
1654
+ - When user hasn't reviewed the plan yet
1655
+ - In non-plan execution modes
1656
+
1657
+ # Parameters
1658
+ - \`plan\`: The complete plan text to be saved and executed
1659
+
1660
+ # Examples
1661
+ - Exit after creating implementation plan
1662
+ - Present final design and exit to implementation
1663
+
1664
+ # Best Practices
1665
+ - Ensure the plan is complete and comprehensive
1666
+ - Include all necessary steps and considerations
1667
+ - The plan will be saved for reference during execution
1668
+ - Use this only when truly ready to start coding`;
1669
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1670
+ async execute(params) {
1671
+ const { plan } = params;
1672
+ try {
1673
+ return {
1674
+ success: true,
1675
+ message: 'Plan completed and ready for execution',
1676
+ plan
1677
+ };
1678
+ }
1679
+ catch (error) {
1680
+ throw new Error(`Failed to exit plan mode: ${error.message}`);
1681
+ }
1682
+ }
1683
+ }
1684
+ export class XmlEscapeTool {
1685
+ name = 'xml_escape';
1686
+ description = `Automatically escape special characters in XML/HTML files to make them valid.
1687
+
1688
+ # When to Use
1689
+ - When content contains special XML characters (<, >, &, ", ')
1690
+ - When generating XML/HTML from raw content
1691
+ - When fixing encoding issues in markup files
1692
+
1693
+ # When NOT to Use
1694
+ - For files that should contain raw XML/HTML
1695
+ - For JavaScript, CSS, or other non-XML files
1696
+ - When escaping should be done manually
1697
+
1698
+ # Parameters
1699
+ - \`file_path\`: Path to the file to escape
1700
+ - \`escape_all\`: (Optional) Also escape additional entities (©, ®, €)
1701
+
1702
+ # Examples
1703
+ - Escape XML content in HTML file
1704
+ - Fix special characters in generated markup
1705
+
1706
+ # Best Practices
1707
+ - Backup files before escaping if unsure
1708
+ - escape_all=true adds common HTML entities`;
1709
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
1710
+ async execute(params) {
1711
+ const { file_path, escape_all = false } = params;
1712
+ try {
1713
+ const absolutePath = path.resolve(file_path);
1714
+ let content = await fs.readFile(absolutePath, 'utf-8');
1715
+ const specialChars = [
1716
+ { char: '&', replacement: '&amp;' },
1717
+ { char: '<', replacement: '&lt;' },
1718
+ { char: '>', replacement: '&gt;' },
1719
+ { char: '"', replacement: '&quot;' },
1720
+ { char: "'", replacement: '&apos;' }
1721
+ ];
1722
+ let changes = 0;
1723
+ for (const { char, replacement } of specialChars) {
1724
+ const regex = new RegExp(this.escapeRegExp(char), 'g');
1725
+ const matches = content.match(regex);
1726
+ if (matches) {
1727
+ changes += matches.length;
1728
+ content = content.replace(regex, replacement);
1729
+ }
1730
+ }
1731
+ if (escape_all) {
1732
+ const additionalChars = [
1733
+ { char: '©', replacement: '&copy;' },
1734
+ { char: '®', replacement: '&reg;' },
1735
+ { char: '€', replacement: '&euro;' }
1736
+ ];
1737
+ for (const { char, replacement } of additionalChars) {
1738
+ const regex = new RegExp(this.escapeRegExp(char), 'g');
1739
+ const matches = content.match(regex);
1740
+ if (matches) {
1741
+ changes += matches.length;
1742
+ content = content.replace(regex, replacement);
1743
+ }
1744
+ }
1745
+ }
1746
+ await fs.writeFile(absolutePath, content, 'utf-8');
1747
+ return {
1748
+ success: true,
1749
+ message: `Successfully escaped ${changes} character(s) in ${file_path}`,
1750
+ changes
1751
+ };
1752
+ }
1753
+ catch (error) {
1754
+ throw new Error(`Failed to escape XML/HTML in file ${file_path}: ${error.message}`);
1755
+ }
1756
+ }
1757
+ escapeRegExp(string) {
1758
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1759
+ }
1760
+ }
1761
+ export class ImageReadTool {
1762
+ name = 'image_read';
1763
+ description = `Read image files and generate detailed analysis using a vision-language model.
1764
+
1765
+ # When to Use
1766
+ - Analyzing UI designs or mockups
1767
+ - Examining screenshots or diagrams
1768
+ - Extracting information from images
1769
+ - Validating visual content or assets
1770
+
1771
+ # When NOT to Use
1772
+ - For text-based file analysis (use Read instead)
1773
+ - When the image is not relevant to the task
1774
+ - For very large images (may have size limits)
1775
+
1776
+ # Parameters
1777
+ - \`image_input\`: Path to image or base64 data
1778
+ - \`prompt\`: Instructions for what to analyze
1779
+ - \`input_type\`: (Optional) 'file_path' or 'base64'
1780
+ - \`task_brief\`: (Optional) Brief task description
1781
+
1782
+ # Examples
1783
+ - Analyze UI mockup: image_read(image_input="design.png", prompt="Describe the UI components")
1784
+ - Validate screenshot: image_read(image_input="screenshot.jpg", prompt="Check if login form is visible")
1785
+
1786
+ # Best Practices
1787
+ - Provide clear prompts for what to look for
1788
+ - Use task_brief for context
1789
+ - Supports PNG, JPG, GIF, WEBP, SVG, BMP`;
1790
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1791
+ async execute(params) {
1792
+ const { image_input, prompt, task_brief, input_type = 'file_path', mime_type } = params;
1793
+ try {
1794
+ let imageData;
1795
+ if (input_type === 'file_path') {
1796
+ const absolutePath = path.resolve(image_input);
1797
+ const imageBuffer = await fs.readFile(absolutePath);
1798
+ imageData = imageBuffer.toString('base64');
1799
+ }
1800
+ else {
1801
+ imageData = image_input;
1802
+ }
1803
+ const { AIClient } = await import('./ai-client.js');
1804
+ const configManager = await import('./config.js');
1805
+ const { getConfigManager } = configManager;
1806
+ const config = getConfigManager();
1807
+ const aiClient = new AIClient({
1808
+ type: AuthType.API_KEY,
1809
+ apiKey: config.get('apiKey'),
1810
+ baseUrl: config.get('baseUrl'),
1811
+ modelName: config.get('modelName') || 'Qwen3-Coder'
1812
+ });
1813
+ const textContent = task_brief ? `${task_brief}\n\n${prompt}` : prompt;
1814
+ const messages = [
1815
+ {
1816
+ role: 'user',
1817
+ content: [
1818
+ {
1819
+ type: 'text',
1820
+ text: textContent
1821
+ },
1822
+ {
1823
+ type: 'image_url',
1824
+ image_url: {
1825
+ url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`
1826
+ }
1827
+ }
1828
+ ]
1829
+ }
1830
+ ];
1831
+ const result = await aiClient.chatCompletion(messages, {
1832
+ temperature: 0.7
1833
+ });
1834
+ const messageContent = result.choices[0]?.message?.content;
1835
+ const analysis = typeof messageContent === 'string' ? messageContent : '';
1836
+ return {
1837
+ analysis,
1838
+ image_info: {
1839
+ input_type,
1840
+ prompt,
1841
+ task_brief
1842
+ }
1843
+ };
1844
+ }
1845
+ catch (error) {
1846
+ throw new Error(`Failed to read image: ${error.message}`);
1847
+ }
1848
+ }
1849
+ }
1850
+ // export class SkillTool implements Tool {
1851
+ // name = 'Skill';
1852
+ // description = `Execute pre-defined workflows (skills) from the xAgent marketplace. Skills are reusable workflows that automate common tasks.
1853
+ // # When to Use
1854
+ // - When a skill exists for the requested task
1855
+ // - When you need to run a multi-step workflow
1856
+ // - When the task matches a marketplace workflow
1857
+ // # When NOT to Use
1858
+ // - When a simple tool can accomplish the task
1859
+ // - When creating new functionality from scratch
1860
+ // - When skill doesn't exist for the specific task
1861
+ // # Parameters
1862
+ // - \`skill\`: The skill/workflow name to execute
1863
+ // # Examples
1864
+ // - Execute a PDF processing skill
1865
+ // - Run a data analysis workflow
1866
+ // # Best Practices
1867
+ // - Skills are pre-configured workflows from the marketplace
1868
+ // - Check if a relevant skill exists first`;
1869
+ // allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
1870
+ // async execute(params: { skill: string }): Promise<{ success: boolean; message: string; result?: any }> {
1871
+ // const { skill } = params;
1872
+ // try {
1873
+ // const { getWorkflowManager } = await import('./workflow.js');
1874
+ // const workflowManager = getWorkflowManager(process.cwd());
1875
+ // const workflow = workflowManager.getWorkflow(skill);
1876
+ // if (!workflow) {
1877
+ // throw new Error(`Skill ${skill} not found`);
1878
+ // }
1879
+ // await workflowManager.executeWorkflow(skill, 'Execute skill');
1880
+ // return {
1881
+ // success: true,
1882
+ // message: `Successfully executed skill: ${skill}`,
1883
+ // result: workflow
1884
+ // };
1885
+ // } catch (error: any) {
1886
+ // throw new Error(`Failed to execute skill: ${error.message}`);
1887
+ // }
1888
+ // }
1889
+ // }
1890
+ export class InvokeSkillTool {
1891
+ name = 'InvokeSkill';
1892
+ description = `Invoke a specialized skill to handle domain-specific tasks. Skills are AI-powered capabilities that understand complex requirements and generate high-quality outputs.
1893
+
1894
+ # When to Use
1895
+ - When user requests involve document processing (Word, PDF, PowerPoint)
1896
+ - When user wants to create frontend interfaces or web applications
1897
+ - When user needs visual design, posters, or generative art
1898
+ - When user asks for documentation or internal communications
1899
+ - When the task matches a specific skill domain
1900
+
1901
+ # When NOT to Use
1902
+ - For simple file operations (use Read/Write instead)
1903
+ - For basic code changes (use Replace/Write instead)
1904
+ - When a regular tool can accomplish the task
1905
+
1906
+ # Parameters
1907
+ - \`skillId\`: The skill identifier (e.g., "docx", "frontend-design", "canvas-design")
1908
+ - \`taskDescription\`: Detailed description of what to accomplish
1909
+ - \`inputFile\`: (Optional) Path to input file if applicable
1910
+ - \`outputFile\`: (Optional) Desired output file path
1911
+ - \`options\`: (Optional) Additional options for the skill
1912
+
1913
+ # Examples
1914
+ - "Create a Word document with contract terms" → InvokeSkill(skillId="docx", taskDescription="Create a professional Word document with contract terms, including numbered sections, signature blocks, and professional formatting")
1915
+ - "Build a landing page for a product" → InvokeSkill(skillId="frontend-design", taskDescription="Create a visually striking landing page with hero section, features, pricing, and footer. Use bold typography and animations.")
1916
+ - "Create a poster for a music festival" → InvokeSkill(skillId="canvas-design", taskDescription="Create a poster for an electronic music festival. The topic is subtle reference to techno culture and underground rave scene.")
1917
+ - "Write API documentation" → InvokeSkill(skillId="doc-coauthoring", taskDescription="Write comprehensive API documentation for a REST API including endpoints, request/response examples, and error handling")
1918
+
1919
+ # Best Practices
1920
+ - Provide detailed task descriptions for better results
1921
+ - Include relevant file paths when working with existing files
1922
+ - Match the skill to the domain (e.g., don't use frontend-design for Word docs)
1923
+ - Skills will guide you through their specific workflows`;
1924
+ allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
1925
+ async execute(params, _executionMode) {
1926
+ const { skillId, taskDescription, inputFile, outputFile, options } = params;
1927
+ try {
1928
+ const { getSkillInvoker } = await import('./skill-invoker.js');
1929
+ const { SkillExecutionParams } = await import('./skill-invoker.js');
1930
+ const skillInvoker = getSkillInvoker();
1931
+ await skillInvoker.initialize();
1932
+ // Verify skill exists
1933
+ const skillDetails = await skillInvoker.getSkillDetails(skillId);
1934
+ if (!skillDetails) {
1935
+ // Try to auto-match the skill
1936
+ const match = await skillInvoker.matchSkill(taskDescription);
1937
+ if (match) {
1938
+ return {
1939
+ success: true,
1940
+ message: `Auto-matched skill: ${match.skill.name} (${match.category})`,
1941
+ skill: match.skill.id,
1942
+ task: taskDescription,
1943
+ result: {
1944
+ category: match.category,
1945
+ confidence: match.confidence,
1946
+ matchedKeywords: match.matchedKeywords
1947
+ },
1948
+ guidance: '请按照匹配到的技能继续执行任务。'
1949
+ };
1950
+ }
1951
+ throw new Error(`Skill not found: ${skillId}`);
1952
+ }
1953
+ const result = await skillInvoker.executeSkill({
1954
+ skillId,
1955
+ taskDescription,
1956
+ inputFile,
1957
+ outputFile,
1958
+ options
1959
+ });
1960
+ if (result.success) {
1961
+ // 生成指导信息,告诉 Agent 接下来要做什么
1962
+ let guidance = '';
1963
+ if (result.nextSteps && result.nextSteps.length > 0) {
1964
+ guidance = `\n## 🎯 下一步操作\n\n请按照以下步骤继续执行任务:\n\n`;
1965
+ for (const step of result.nextSteps) {
1966
+ guidance += `### 步骤 ${step.step}: ${step.action}\n`;
1967
+ guidance += `- **描述**: ${step.description}\n`;
1968
+ guidance += `- **原因**: ${step.reason}\n`;
1969
+ if (step.command) {
1970
+ guidance += `- **命令**: \`${step.command}\`\n`;
1971
+ }
1972
+ if (step.file) {
1973
+ guidance += `- **文件**: ${step.file}\n`;
1974
+ }
1975
+ guidance += '\n';
1976
+ }
1977
+ guidance += `---\n**重要**: 上述步骤是根据 SKILL.md 自动生成的执行指南。请按照这些步骤继续完成任务,而不是结束对话。\n`;
1978
+ }
1979
+ return {
1980
+ success: true,
1981
+ message: `技能已激活: ${skillDetails.name}`,
1982
+ skill: skillId,
1983
+ task: taskDescription,
1984
+ result: result.output + (guidance ? guidance : ''),
1985
+ files: result.files,
1986
+ nextSteps: result.nextSteps,
1987
+ guidance: guidance
1988
+ };
1989
+ }
1990
+ else {
1991
+ throw new Error(result.error);
1992
+ }
1993
+ }
1994
+ catch (error) {
1995
+ throw new Error(`Failed to invoke skill: ${error.message}`);
1996
+ }
1997
+ }
1998
+ }
1999
+ // export class ListSkillsTool implements Tool {
2000
+ // name = 'ListSkills';
2001
+ // description = `List all available skills from the xAgent skills library. Use this tool when you need to:
2002
+ // - See what skills are available
2003
+ // - Find skills that match a user's request
2004
+ // - Get an overview of capabilities
2005
+ // This returns a list of all skills with their names, descriptions, and categories.`;
2006
+ // allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
2007
+ // async execute(): Promise<{ success: boolean; skills: any[] }> {
2008
+ // try {
2009
+ // const { getWorkflowManager } = await import('./workflow.js');
2010
+ // const workflowManager = getWorkflowManager(process.cwd());
2011
+ // const skills = await workflowManager.listSkills();
2012
+ // return {
2013
+ // success: true,
2014
+ // skills: skills.map(s => ({
2015
+ // id: s.id,
2016
+ // name: s.name,
2017
+ // description: s.description,
2018
+ // category: s.category
2019
+ // }))
2020
+ // };
2021
+ // } catch (error: any) {
2022
+ // throw new Error(`Failed to list skills: ${error.message}`);
2023
+ // }
2024
+ // }
2025
+ // }
2026
+ // export class GetSkillDetailsTool implements Tool {
2027
+ // name = 'GetSkillDetails';
2028
+ // description = `Get detailed information about a specific skill. Use this tool when:
2029
+ // - You want to understand what a skill does before executing it
2030
+ // - You need the full skill documentation to help the user
2031
+ // - You need to verify a skill exists before using it
2032
+ // # Parameters
2033
+ // - \`skill\`: The skill name/id to get details for
2034
+ // # Returns
2035
+ // The full skill documentation including instructions, examples, and guidelines.`;
2036
+ // allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2037
+ // async execute(params: { skill: string }): Promise<{ success: boolean; details: any }> {
2038
+ // const { skill } = params;
2039
+ // if (!skill) {
2040
+ // throw new Error('Skill parameter is required');
2041
+ // }
2042
+ // try {
2043
+ // const { getWorkflowManager } = await import('./workflow.js');
2044
+ // const workflowManager = getWorkflowManager(process.cwd());
2045
+ // const details = await workflowManager.getSkillDetails(skill);
2046
+ // if (!details) {
2047
+ // throw new Error(`Skill '${skill}' not found`);
2048
+ // }
2049
+ // return {
2050
+ // success: true,
2051
+ // details: {
2052
+ // id: details.id,
2053
+ // name: details.name,
2054
+ // description: details.description,
2055
+ // category: details.category,
2056
+ // content: details.content
2057
+ // }
2058
+ // };
2059
+ // } catch (error: any) {
2060
+ // throw new Error(`Failed to get skill details: ${error.message}`);
2061
+ // }
2062
+ // }
2063
+ // }
2064
+ export class ToolRegistry {
2065
+ tools = new Map();
2066
+ todoWriteTool;
2067
+ backgroundTasks = new Map();
2068
+ constructor() {
2069
+ this.todoWriteTool = new TodoWriteTool();
2070
+ this.registerDefaultTools();
2071
+ }
2072
+ registerDefaultTools() {
2073
+ this.register(new ReadTool());
2074
+ this.register(new WriteTool());
2075
+ this.register(new GrepTool());
2076
+ this.register(new BashTool());
2077
+ this.register(new ListDirectoryTool());
2078
+ this.register(new SearchCodebaseTool());
2079
+ this.register(new DeleteFileTool());
2080
+ this.register(new CreateDirectoryTool());
2081
+ this.register(new ReplaceTool());
2082
+ this.register(new WebSearchTool());
2083
+ this.register(this.todoWriteTool);
2084
+ this.register(new TodoReadTool(this.todoWriteTool));
2085
+ this.register(new TaskTool());
2086
+ this.register(new ReadBashOutputTool());
2087
+ this.register(new WebFetchTool());
2088
+ this.register(new AskUserQuestionTool());
2089
+ this.register(new SaveMemoryTool());
2090
+ this.register(new ExitPlanModeTool());
2091
+ this.register(new XmlEscapeTool());
2092
+ this.register(new ImageReadTool());
2093
+ // Deprecated: Use InvokeSkillTool instead (2026-01-17)
2094
+ // this.register(new SkillTool());
2095
+ // this.register(new ListSkillsTool());
2096
+ // this.register(new GetSkillDetailsTool());
2097
+ this.register(new InvokeSkillTool());
2098
+ // GUI Subagent Tools
2099
+ // this.register(new GUIOperateTool());
2100
+ // this.register(new GUIScreenshotTool());
2101
+ // this.register(new GUICleanupTool());
2102
+ }
2103
+ register(tool) {
2104
+ this.tools.set(tool.name, tool);
2105
+ }
2106
+ /**
2107
+ * Register MCP tools with their simple names (without server prefix)
2108
+ * This allows the LLM to call MCP tools using simple names like "create_issue"
2109
+ * instead of "github__create_issue"
2110
+ */
2111
+ registerMCPTools(mcpTools) {
2112
+ let registeredCount = 0;
2113
+ for (const [fullName, tool] of mcpTools) {
2114
+ const [serverName, originalName] = fullName.split('__');
2115
+ if (!originalName) {
2116
+ continue;
2117
+ }
2118
+ // Auto-rename if conflict, ensure unique name
2119
+ let toolName = originalName;
2120
+ let suffix = 1;
2121
+ while (this.tools.has(toolName)) {
2122
+ const existingTool = this.tools.get(toolName);
2123
+ const existingIsMcp = existingTool._isMcpTool;
2124
+ if (existingIsMcp && existingTool._mcpFullName === fullName) {
2125
+ // Same MCP tool already registered, skip silently
2126
+ break;
2127
+ }
2128
+ // Conflict - auto-rename with suffix
2129
+ toolName = `${originalName}_mcp${suffix}`;
2130
+ suffix++;
2131
+ }
2132
+ if (!this.tools.has(toolName)) {
2133
+ // Create a wrapper tool for the MCP tool - hide MCP origin from LLM
2134
+ const mcpTool = {
2135
+ name: toolName,
2136
+ description: tool.description || 'MCP tool',
2137
+ allowedModes: [ExecutionMode.YOLO, ExecutionMode.SMART, ExecutionMode.ACCEPT_EDITS],
2138
+ inputSchema: tool.inputSchema,
2139
+ _isMcpTool: true,
2140
+ _mcpServerName: serverName,
2141
+ _mcpFullName: fullName,
2142
+ execute: async (params) => {
2143
+ const { getMCPManager } = await import('./mcp.js');
2144
+ const mcpManager = getMCPManager();
2145
+ return await mcpManager.callTool(fullName, params);
2146
+ }
2147
+ };
2148
+ this.tools.set(toolName, mcpTool);
2149
+ registeredCount++;
2150
+ if (toolName !== originalName) {
2151
+ console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
2152
+ }
2153
+ }
2154
+ }
2155
+ if (registeredCount > 0) {
2156
+ console.log(`[MCP] Registered ${registeredCount} tool(s)`);
2157
+ }
2158
+ }
2159
+ /**
2160
+ * Remove all MCP tool wrappers (useful when MCP servers are removed)
2161
+ */
2162
+ unregisterMCPTools(serverName) {
2163
+ for (const [name, tool] of this.tools) {
2164
+ // Remove MCP tool wrappers by checking marker
2165
+ const mcpTool = tool;
2166
+ if (mcpTool._isMcpTool && (!serverName || mcpTool._mcpServerName === serverName)) {
2167
+ this.tools.delete(name);
2168
+ }
2169
+ }
2170
+ }
2171
+ unregister(toolName) {
2172
+ this.tools.delete(toolName);
2173
+ }
2174
+ get(toolName) {
2175
+ return this.tools.get(toolName);
2176
+ }
2177
+ getAll() {
2178
+ return Array.from(this.tools.values());
2179
+ }
2180
+ addBackgroundTask(taskId, task) {
2181
+ this.backgroundTasks.set(taskId, task);
2182
+ }
2183
+ getBackgroundTask(taskId) {
2184
+ return this.backgroundTasks.get(taskId);
2185
+ }
2186
+ removeBackgroundTask(taskId) {
2187
+ this.backgroundTasks.delete(taskId);
2188
+ }
2189
+ getToolDefinitions() {
2190
+ return Array.from(this.tools.values()).map(tool => {
2191
+ let parameters = {
2192
+ type: 'object',
2193
+ properties: {},
2194
+ required: []
2195
+ };
2196
+ // Define specific parameters for each tool
2197
+ switch (tool.name) {
2198
+ case 'Read':
2199
+ parameters = {
2200
+ type: 'object',
2201
+ properties: {
2202
+ filePath: {
2203
+ type: 'string',
2204
+ description: 'The absolute path to the file to read'
2205
+ },
2206
+ offset: {
2207
+ type: 'number',
2208
+ description: 'Optional: Line number to start reading from (0-based)'
2209
+ },
2210
+ limit: {
2211
+ type: 'number',
2212
+ description: 'Optional: Maximum number of lines to read'
2213
+ }
2214
+ },
2215
+ required: ['filePath']
2216
+ };
2217
+ break;
2218
+ case 'Write':
2219
+ parameters = {
2220
+ type: 'object',
2221
+ properties: {
2222
+ filePath: {
2223
+ type: 'string',
2224
+ description: 'The absolute path to the file to write'
2225
+ },
2226
+ content: {
2227
+ type: 'string',
2228
+ description: 'The content to write to the file'
2229
+ }
2230
+ },
2231
+ required: ['filePath', 'content']
2232
+ };
2233
+ break;
2234
+ case 'Grep':
2235
+ parameters = {
2236
+ type: 'object',
2237
+ properties: {
2238
+ pattern: {
2239
+ type: 'string',
2240
+ description: 'The regex pattern to search for'
2241
+ },
2242
+ path: {
2243
+ type: 'string',
2244
+ description: 'Optional: The path to search in (default: current directory)'
2245
+ },
2246
+ include: {
2247
+ type: 'string',
2248
+ description: 'Optional: Glob pattern to filter files'
2249
+ },
2250
+ case_sensitive: {
2251
+ type: 'boolean',
2252
+ description: 'Optional: Case-sensitive search (default: false)'
2253
+ },
2254
+ context: {
2255
+ type: 'number',
2256
+ description: 'Optional: Number of context lines to show'
2257
+ }
2258
+ },
2259
+ required: ['pattern']
2260
+ };
2261
+ break;
2262
+ case 'Bash':
2263
+ parameters = {
2264
+ type: 'object',
2265
+ properties: {
2266
+ command: {
2267
+ type: 'string',
2268
+ description: 'The shell command to execute'
2269
+ },
2270
+ cwd: {
2271
+ type: 'string',
2272
+ description: 'Optional: Working directory'
2273
+ },
2274
+ description: {
2275
+ type: 'string',
2276
+ description: 'Optional: Brief description of the command'
2277
+ },
2278
+ timeout: {
2279
+ type: 'number',
2280
+ description: 'Optional: Timeout in seconds (default: 120)'
2281
+ },
2282
+ run_in_bg: {
2283
+ type: 'boolean',
2284
+ description: 'Optional: Run in background (default: false)'
2285
+ }
2286
+ },
2287
+ required: ['command']
2288
+ };
2289
+ break;
2290
+ case 'ListDirectory':
2291
+ parameters = {
2292
+ type: 'object',
2293
+ properties: {
2294
+ path: {
2295
+ type: 'string',
2296
+ description: 'Optional: The directory path to list (default: current directory)'
2297
+ },
2298
+ recursive: {
2299
+ type: 'boolean',
2300
+ description: 'Optional: List recursively (default: false)'
2301
+ }
2302
+ },
2303
+ required: []
2304
+ };
2305
+ break;
2306
+ case 'SearchCodebase':
2307
+ parameters = {
2308
+ type: 'object',
2309
+ properties: {
2310
+ pattern: {
2311
+ type: 'string',
2312
+ description: 'The glob pattern to match files'
2313
+ },
2314
+ path: {
2315
+ type: 'string',
2316
+ description: 'Optional: The path to search in (default: current directory)'
2317
+ }
2318
+ },
2319
+ required: ['pattern']
2320
+ };
2321
+ break;
2322
+ case 'DeleteFile':
2323
+ parameters = {
2324
+ type: 'object',
2325
+ properties: {
2326
+ filePath: {
2327
+ type: 'string',
2328
+ description: 'The path to the file to delete'
2329
+ }
2330
+ },
2331
+ required: ['filePath']
2332
+ };
2333
+ break;
2334
+ case 'CreateDirectory':
2335
+ parameters = {
2336
+ type: 'object',
2337
+ properties: {
2338
+ dirPath: {
2339
+ type: 'string',
2340
+ description: 'The directory path to create'
2341
+ },
2342
+ recursive: {
2343
+ type: 'boolean',
2344
+ description: 'Optional: Create parent directories (default: true)'
2345
+ }
2346
+ },
2347
+ required: ['dirPath']
2348
+ };
2349
+ break;
2350
+ case 'replace':
2351
+ parameters = {
2352
+ type: 'object',
2353
+ properties: {
2354
+ file_path: {
2355
+ type: 'string',
2356
+ description: 'The absolute path to the file'
2357
+ },
2358
+ instruction: {
2359
+ type: 'string',
2360
+ description: 'Description of what needs to be changed'
2361
+ },
2362
+ old_string: {
2363
+ type: 'string',
2364
+ description: 'The exact text to replace'
2365
+ },
2366
+ new_string: {
2367
+ type: 'string',
2368
+ description: 'The exact text to replace with'
2369
+ }
2370
+ },
2371
+ required: ['file_path', 'instruction', 'old_string', 'new_string']
2372
+ };
2373
+ break;
2374
+ case 'web_search':
2375
+ parameters = {
2376
+ type: 'object',
2377
+ properties: {
2378
+ query: {
2379
+ type: 'string',
2380
+ description: 'The search query'
2381
+ }
2382
+ },
2383
+ required: ['query']
2384
+ };
2385
+ break;
2386
+ case 'todo_write':
2387
+ parameters = {
2388
+ type: 'object',
2389
+ properties: {
2390
+ todos: {
2391
+ type: 'array',
2392
+ description: 'Array of todo items',
2393
+ items: {
2394
+ type: 'object',
2395
+ properties: {
2396
+ id: { type: 'string' },
2397
+ task: { type: 'string' },
2398
+ status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed'] },
2399
+ priority: { type: 'string', enum: ['high', 'medium', 'low'] }
2400
+ },
2401
+ required: ['id', 'task', 'status']
2402
+ }
2403
+ }
2404
+ },
2405
+ required: ['todos']
2406
+ };
2407
+ break;
2408
+ case 'todo_read':
2409
+ parameters = {
2410
+ type: 'object',
2411
+ properties: {},
2412
+ required: []
2413
+ };
2414
+ break;
2415
+ case 'task':
2416
+ parameters = {
2417
+ type: 'object',
2418
+ properties: {
2419
+ description: {
2420
+ type: 'string',
2421
+ description: 'Brief description of the task (3-5 words)'
2422
+ },
2423
+ agents: {
2424
+ type: 'array',
2425
+ description: 'Optional: Array of agents to run in parallel for comprehensive analysis',
2426
+ items: {
2427
+ type: 'object',
2428
+ properties: {
2429
+ description: {
2430
+ type: 'string',
2431
+ description: 'Brief description of the sub-agent task'
2432
+ },
2433
+ prompt: {
2434
+ type: 'string',
2435
+ description: 'The task for the sub-agent to perform'
2436
+ },
2437
+ subagent_type: {
2438
+ type: 'string',
2439
+ enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
2440
+ description: 'The type of specialized agent'
2441
+ },
2442
+ constraints: {
2443
+ type: 'array',
2444
+ items: { type: 'string' },
2445
+ description: 'Optional: Constraints or limitations'
2446
+ }
2447
+ },
2448
+ required: ['description', 'prompt', 'subagent_type']
2449
+ }
2450
+ },
2451
+ prompt: {
2452
+ type: 'string',
2453
+ description: 'Optional: The task for the agent to perform (use agents for parallel execution)'
2454
+ },
2455
+ subagent_type: {
2456
+ type: 'string',
2457
+ enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
2458
+ description: 'Optional: The type of specialized agent (use agents for parallel execution)'
2459
+ },
2460
+ useContext: {
2461
+ type: 'boolean',
2462
+ description: 'Optional: Include main agent context'
2463
+ },
2464
+ outputFormat: {
2465
+ type: 'string',
2466
+ description: 'Optional: Output format template'
2467
+ },
2468
+ constraints: {
2469
+ type: 'array',
2470
+ items: { type: 'string' },
2471
+ description: 'Optional: Constraints or limitations'
2472
+ }
2473
+ },
2474
+ required: ['description']
2475
+ };
2476
+ break;
2477
+ case 'ReadBashOutput':
2478
+ parameters = {
2479
+ type: 'object',
2480
+ properties: {
2481
+ task_id: {
2482
+ type: 'string',
2483
+ description: 'The ID of the task'
2484
+ },
2485
+ poll_interval: {
2486
+ type: 'number',
2487
+ description: 'Optional: Polling interval in seconds (default: 10)'
2488
+ }
2489
+ },
2490
+ required: ['task_id']
2491
+ };
2492
+ break;
2493
+ case 'web_fetch':
2494
+ parameters = {
2495
+ type: 'object',
2496
+ properties: {
2497
+ prompt: {
2498
+ type: 'string',
2499
+ description: 'Prompt containing URL(s) and processing instructions'
2500
+ }
2501
+ },
2502
+ required: ['prompt']
2503
+ };
2504
+ break;
2505
+ case 'ask_user_question':
2506
+ parameters = {
2507
+ type: 'object',
2508
+ properties: {
2509
+ questions: {
2510
+ type: 'array',
2511
+ description: 'Array of questions to ask',
2512
+ items: {
2513
+ type: 'object',
2514
+ properties: {
2515
+ question: { type: 'string' },
2516
+ header: { type: 'string', description: 'Short label (max 12 chars)' },
2517
+ options: {
2518
+ type: 'array',
2519
+ items: { type: 'string' },
2520
+ description: 'Available choices (2-4 options)'
2521
+ },
2522
+ multiSelect: { type: 'boolean' }
2523
+ },
2524
+ required: ['question', 'header', 'options', 'multiSelect']
2525
+ }
2526
+ }
2527
+ },
2528
+ required: ['questions']
2529
+ };
2530
+ break;
2531
+ case 'save_memory':
2532
+ parameters = {
2533
+ type: 'object',
2534
+ properties: {
2535
+ fact: {
2536
+ type: 'string',
2537
+ description: 'The specific fact to remember'
2538
+ }
2539
+ },
2540
+ required: ['fact']
2541
+ };
2542
+ break;
2543
+ case 'exit_plan_mode':
2544
+ parameters = {
2545
+ type: 'object',
2546
+ properties: {
2547
+ plan: {
2548
+ type: 'string',
2549
+ description: 'The plan to present'
2550
+ }
2551
+ },
2552
+ required: ['plan']
2553
+ };
2554
+ break;
2555
+ case 'xml_escape':
2556
+ parameters = {
2557
+ type: 'object',
2558
+ properties: {
2559
+ file_path: {
2560
+ type: 'string',
2561
+ description: 'The absolute path to the XML/HTML file'
2562
+ },
2563
+ escape_all: {
2564
+ type: 'boolean',
2565
+ description: 'Optional: Escape all special characters (default: false)'
2566
+ }
2567
+ },
2568
+ required: ['file_path']
2569
+ };
2570
+ break;
2571
+ case 'image_read':
2572
+ parameters = {
2573
+ type: 'object',
2574
+ properties: {
2575
+ image_input: {
2576
+ type: 'string',
2577
+ description: 'Image file path or base64 data'
2578
+ },
2579
+ prompt: {
2580
+ type: 'string',
2581
+ description: 'Comprehensive VLM instruction'
2582
+ },
2583
+ task_brief: {
2584
+ type: 'string',
2585
+ description: 'Brief task description (max 15 words)'
2586
+ },
2587
+ input_type: {
2588
+ type: 'string',
2589
+ enum: ['file_path', 'base64'],
2590
+ description: 'Input type (default: file_path)'
2591
+ },
2592
+ mime_type: {
2593
+ type: 'string',
2594
+ description: 'Optional: MIME type for base64 input'
2595
+ }
2596
+ },
2597
+ required: ['image_input', 'prompt']
2598
+ };
2599
+ break;
2600
+ case 'Skill':
2601
+ parameters = {
2602
+ type: 'object',
2603
+ properties: {
2604
+ skill: {
2605
+ type: 'string',
2606
+ description: 'The skill name to execute'
2607
+ }
2608
+ },
2609
+ required: ['skill']
2610
+ };
2611
+ break;
2612
+ case 'ListSkills':
2613
+ parameters = {
2614
+ type: 'object',
2615
+ properties: {},
2616
+ required: []
2617
+ };
2618
+ break;
2619
+ case 'GetSkillDetails':
2620
+ parameters = {
2621
+ type: 'object',
2622
+ properties: {
2623
+ skill: {
2624
+ type: 'string',
2625
+ description: 'The skill name/id to get details for'
2626
+ }
2627
+ },
2628
+ required: ['skill']
2629
+ };
2630
+ break;
2631
+ default:
2632
+ // For MCP tools, use their inputSchema; for other unknown tools, keep empty schema
2633
+ const mcpTool = tool;
2634
+ if (mcpTool._isMcpTool && mcpTool.inputSchema) {
2635
+ // Use MCP tool's inputSchema directly
2636
+ parameters = {
2637
+ type: 'object',
2638
+ properties: {},
2639
+ required: []
2640
+ };
2641
+ if (mcpTool.inputSchema.properties) {
2642
+ for (const [paramName, paramDef] of Object.entries(mcpTool.inputSchema.properties)) {
2643
+ parameters.properties[paramName] = {
2644
+ type: paramDef.type || 'string',
2645
+ description: paramDef.description || ''
2646
+ };
2647
+ }
2648
+ }
2649
+ if (mcpTool.inputSchema.required) {
2650
+ parameters.required = mcpTool.inputSchema.required;
2651
+ }
2652
+ }
2653
+ else {
2654
+ parameters = {
2655
+ type: 'object',
2656
+ properties: {},
2657
+ required: []
2658
+ };
2659
+ }
2660
+ }
2661
+ return {
2662
+ type: 'function',
2663
+ function: {
2664
+ name: tool.name,
2665
+ description: tool.description,
2666
+ parameters
2667
+ }
2668
+ };
2669
+ });
2670
+ }
2671
+ async execute(toolName, params, executionMode, indent = '') {
2672
+ // First try to execute as local tool
2673
+ const localTool = this.tools.get(toolName);
2674
+ if (localTool) {
2675
+ return await this.executeLocalTool(toolName, params, executionMode, indent);
2676
+ }
2677
+ // Fall back to MCP tool if local tool doesn't exist
2678
+ const { getMCPManager } = await import('./mcp.js');
2679
+ const mcpManager = getMCPManager();
2680
+ const allMcpTools = mcpManager.getAllTools();
2681
+ // Check if this is an MCP tool (format: serverName__toolName)
2682
+ if (toolName.includes('__') && allMcpTools.has(toolName)) {
2683
+ return await this.executeMCPTool(toolName, params, executionMode, indent);
2684
+ }
2685
+ // Try to find MCP tool with just the tool name (try each server)
2686
+ for (const [fullName, tool] of allMcpTools) {
2687
+ const [serverName, actualToolName] = fullName.split('__');
2688
+ if (actualToolName === toolName) {
2689
+ return await this.executeMCPTool(fullName, params, executionMode, indent);
2690
+ }
2691
+ }
2692
+ // Tool not found anywhere
2693
+ throw new Error(`Tool not found: ${toolName}`);
2694
+ }
2695
+ /**
2696
+ * Generate guidance for LLM to choose between local and MCP tools
2697
+ * This returns a message that the LLM can analyze to decide which tool to use
2698
+ */
2699
+ generateLlmToolChoiceGuidance(toolName, params, executionMode, indent) {
2700
+ const modeLabel = executionMode === ExecutionMode.YOLO ? 'YOLO' :
2701
+ executionMode === ExecutionMode.SMART ? 'SMART' :
2702
+ executionMode === ExecutionMode.ACCEPT_EDITS ? 'ACCEPT_EDITS' : 'DEFAULT';
2703
+ return Promise.resolve({
2704
+ success: false,
2705
+ needsLlmDecision: true,
2706
+ toolName,
2707
+ mode: modeLabel,
2708
+ message: `
2709
+ ## Tool Choice Decision Required (${modeLabel} Mode)
2710
+
2711
+ Both **local** and **MCP** versions of "${toolName}" are available. Please analyze the requirements and decide which tool to use.
2712
+
2713
+ ### Local Tool
2714
+ - **Pros**: No network latency, always available, no external dependencies
2715
+ - **Best for**: Filesystem operations, project-specific tasks, command execution
2716
+
2717
+ ### MCP Tool
2718
+ - **Pros**: Extended capabilities (e.g., GitHub API, database access, cloud services)
2719
+ - **Best for**: External API calls, database operations, cloud service integration
2720
+
2721
+ ### How to Specify Your Choice
2722
+ Retry the tool call with the \`_useTool\` parameter:
2723
+
2724
+ **To use MCP tool:**
2725
+ \`\`\`json
2726
+ {"_useTool": "mcp", ...otherParams}
2727
+ \`\`\`
2728
+
2729
+ **To use local tool:**
2730
+ \`\`\`json
2731
+ {"_useTool": "local", ...otherParams}
2732
+ \`\`\`
2733
+
2734
+ ### Decision Criteria
2735
+ Consider:
2736
+ 1. Does the task require external API access?
2737
+ 2. Is network latency a concern?
2738
+ 3. Does the MCP tool have features not available locally?
2739
+ 4. Is the local tool sufficient for the task?
2740
+
2741
+ Make your decision based on the user's request and the above criteria.`
2742
+ });
2743
+ }
2744
+ /**
2745
+ * Execute local tool (extracted for reuse)
2746
+ */
2747
+ async executeLocalTool(toolName, params, executionMode, indent) {
2748
+ const tool = this.get(toolName);
2749
+ if (!tool) {
2750
+ throw new Error(`Tool not found: ${toolName}`);
2751
+ }
2752
+ if (!tool.allowedModes.includes(executionMode)) {
2753
+ throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
2754
+ }
2755
+ // Smart approval mode
2756
+ if (executionMode === ExecutionMode.SMART) {
2757
+ const debugMode = process.env.DEBUG === 'smart-approval';
2758
+ const cancellationManager = getCancellationManager();
2759
+ // task tool bypasses smart approval entirely
2760
+ if (toolName === 'task') {
2761
+ if (debugMode) {
2762
+ const { getLogger } = await import('./logger.js');
2763
+ const logger = getLogger();
2764
+ logger.debug(`[SmartApprovalEngine] Tool '${toolName}' bypassed smart approval completely`);
2765
+ }
2766
+ return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
2767
+ }
2768
+ const { getSmartApprovalEngine } = await import('./smart-approval.js');
2769
+ const { getConfigManager } = await import('./config.js');
2770
+ const configManager = getConfigManager();
2771
+ const approvalEngine = getSmartApprovalEngine(debugMode);
2772
+ // Evaluate tool call
2773
+ const result = await approvalEngine.evaluate({
2774
+ toolName,
2775
+ params,
2776
+ timestamp: Date.now()
2777
+ });
2778
+ // Decide whether to execute based on approval result
2779
+ if (result.decision === 'approved') {
2780
+ // Whitelist or AI approval passed, execute directly
2781
+ console.log('');
2782
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`);
2783
+ console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
2784
+ console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
2785
+ console.log('');
2786
+ return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
2787
+ }
2788
+ else if (result.decision === 'requires_confirmation') {
2789
+ // Requires user confirmation
2790
+ const confirmed = await approvalEngine.requestConfirmation(result);
2791
+ if (confirmed) {
2792
+ console.log('');
2793
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`);
2794
+ console.log('');
2795
+ return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
2796
+ }
2797
+ else {
2798
+ console.log('');
2799
+ console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`);
2800
+ console.log('');
2801
+ throw new Error(`Tool execution cancelled by user: ${toolName}`);
2802
+ }
2803
+ }
2804
+ else {
2805
+ // Rejected execution
2806
+ console.log('');
2807
+ console.log(`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`);
2808
+ console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
2809
+ console.log('');
2810
+ throw new Error(`Tool execution rejected: ${toolName}`);
2811
+ }
2812
+ }
2813
+ // Other modes execute directly
2814
+ return await tool.execute(params, executionMode);
2815
+ }
2816
+ /**
2817
+ * Execute an MCP tool call
2818
+ */
2819
+ async executeMCPTool(toolName, params, executionMode, indent = '') {
2820
+ const { getMCPManager } = await import('./mcp.js');
2821
+ const cancellationManager = getCancellationManager();
2822
+ const mcpManager = getMCPManager();
2823
+ // Parse the tool name (format: serverName__toolName)
2824
+ const [serverName, actualToolName] = toolName.split('__');
2825
+ // Get server info for display
2826
+ const server = mcpManager.getServer(serverName);
2827
+ const serverTools = server?.getToolNames() || [];
2828
+ // Display tool call info
2829
+ console.log('');
2830
+ console.log(`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`);
2831
+ // Smart approval mode for MCP tools
2832
+ if (executionMode === ExecutionMode.SMART) {
2833
+ const debugMode = process.env.DEBUG === 'smart-approval';
2834
+ const { getSmartApprovalEngine } = await import('./smart-approval.js');
2835
+ const approvalEngine = getSmartApprovalEngine(debugMode);
2836
+ // Evaluate MCP tool call
2837
+ const result = await approvalEngine.evaluate({
2838
+ toolName: `MCP[${serverName}]::${actualToolName}`,
2839
+ params,
2840
+ timestamp: Date.now()
2841
+ });
2842
+ if (result.decision === 'approved') {
2843
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`);
2844
+ console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
2845
+ }
2846
+ else if (result.decision === 'requires_confirmation') {
2847
+ const confirmed = await approvalEngine.requestConfirmation(result);
2848
+ if (!confirmed) {
2849
+ console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`);
2850
+ throw new Error(`Tool execution cancelled by user: ${toolName}`);
2851
+ }
2852
+ }
2853
+ else {
2854
+ console.log(`${indent}${colors.error(`❌ [Smart Mode] MCP tool execution rejected`)}`);
2855
+ console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
2856
+ throw new Error(`Tool execution rejected: ${toolName}`);
2857
+ }
2858
+ }
2859
+ // Execute the MCP tool call with cancellation support
2860
+ const operationId = `mcp-${serverName}-${actualToolName}-${Date.now()}`;
2861
+ return await cancellationManager.withCancellation(mcpManager.callTool(toolName, params), operationId);
2862
+ }
2863
+ async executeAll(toolCalls, executionMode) {
2864
+ const results = [];
2865
+ const cancellationManager = getCancellationManager();
2866
+ let cancelled = false;
2867
+ // Listen for cancellation
2868
+ const cancelHandler = () => {
2869
+ cancelled = true;
2870
+ };
2871
+ cancellationManager.on('cancelled', cancelHandler);
2872
+ const executePromises = toolCalls.map(async (toolCall, index) => {
2873
+ const { name, params } = toolCall;
2874
+ const operationId = `tool-${name}-${index}-${Date.now()}`;
2875
+ try {
2876
+ const result = await cancellationManager.withCancellation(this.execute(name, params, executionMode), operationId);
2877
+ return { tool: name, result, error: undefined };
2878
+ }
2879
+ catch (error) {
2880
+ if (error.message === 'Operation cancelled by user') {
2881
+ return { tool: name, result: undefined, error: 'Cancelled' };
2882
+ }
2883
+ return { tool: name, result: undefined, error: error.message };
2884
+ }
2885
+ });
2886
+ const settledResults = await Promise.all(executePromises);
2887
+ cancellationManager.off('cancelled', cancelHandler);
2888
+ // Filter out cancelled tools and mark them appropriately
2889
+ for (const result of settledResults) {
2890
+ if (result.error === 'Cancelled' && cancelled) {
2891
+ // Don't add cancelled results to the final output
2892
+ continue;
2893
+ }
2894
+ results.push(result);
2895
+ }
2896
+ return results;
2897
+ }
2898
+ }
2899
+ let toolRegistryInstance = null;
2900
+ export function getToolRegistry() {
2901
+ if (!toolRegistryInstance) {
2902
+ toolRegistryInstance = new ToolRegistry();
2903
+ }
2904
+ return toolRegistryInstance;
2905
+ }
2906
+ //# sourceMappingURL=tools.js.map