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