atena-cli 0.1.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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/agent.d.ts +44 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +731 -0
- package/dist/agent.js.map +1 -0
- package/dist/agents.d.ts +33 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +162 -0
- package/dist/agents.js.map +1 -0
- package/dist/autocomplete.d.ts +4 -0
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +96 -0
- package/dist/autocomplete.js.map +1 -0
- package/dist/cli/commands/connect.d.ts +3 -0
- package/dist/cli/commands/connect.d.ts.map +1 -0
- package/dist/cli/commands/connect.js +31 -0
- package/dist/cli/commands/connect.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +3 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +26 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/login.d.ts +3 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +46 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/vault.d.ts +3 -0
- package/dist/cli/commands/vault.d.ts.map +1 -0
- package/dist/cli/commands/vault.js +73 -0
- package/dist/cli/commands/vault.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +309 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init/setup.d.ts +3 -0
- package/dist/cli/init/setup.d.ts.map +1 -0
- package/dist/cli/init/setup.js +74 -0
- package/dist/cli/init/setup.js.map +1 -0
- package/dist/cli/menus.d.ts +2 -0
- package/dist/cli/menus.d.ts.map +1 -0
- package/dist/cli/menus.js +111 -0
- package/dist/cli/menus.js.map +1 -0
- package/dist/cli/postinstall.d.ts +9 -0
- package/dist/cli/postinstall.d.ts.map +1 -0
- package/dist/cli/postinstall.js +81 -0
- package/dist/cli/postinstall.js.map +1 -0
- package/dist/cli/realtime-input.d.ts +4 -0
- package/dist/cli/realtime-input.d.ts.map +1 -0
- package/dist/cli/realtime-input.js +268 -0
- package/dist/cli/realtime-input.js.map +1 -0
- package/dist/cli/setup.d.ts +26 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +202 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/themes.d.ts +30 -0
- package/dist/cli/themes.d.ts.map +1 -0
- package/dist/cli/themes.js +225 -0
- package/dist/cli/themes.js.map +1 -0
- package/dist/cli/ui.d.ts +70 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +455 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/commands.d.ts +16 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +230 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +140 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +23 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +80 -0
- package/dist/context.js.map +1 -0
- package/dist/core/agent.d.ts +94 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +1280 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/agents/enterprise/backend.d.ts +3 -0
- package/dist/core/agents/enterprise/backend.d.ts.map +1 -0
- package/dist/core/agents/enterprise/backend.js +35 -0
- package/dist/core/agents/enterprise/backend.js.map +1 -0
- package/dist/core/agents/enterprise/cloud.d.ts +3 -0
- package/dist/core/agents/enterprise/cloud.d.ts.map +1 -0
- package/dist/core/agents/enterprise/cloud.js +35 -0
- package/dist/core/agents/enterprise/cloud.js.map +1 -0
- package/dist/core/agents/enterprise/datascience.d.ts +3 -0
- package/dist/core/agents/enterprise/datascience.d.ts.map +1 -0
- package/dist/core/agents/enterprise/datascience.js +35 -0
- package/dist/core/agents/enterprise/datascience.js.map +1 -0
- package/dist/core/agents/enterprise/design.d.ts +3 -0
- package/dist/core/agents/enterprise/design.d.ts.map +1 -0
- package/dist/core/agents/enterprise/design.js +35 -0
- package/dist/core/agents/enterprise/design.js.map +1 -0
- package/dist/core/agents/enterprise/devops.d.ts +3 -0
- package/dist/core/agents/enterprise/devops.d.ts.map +1 -0
- package/dist/core/agents/enterprise/devops.js +35 -0
- package/dist/core/agents/enterprise/devops.js.map +1 -0
- package/dist/core/agents/enterprise/finance.d.ts +3 -0
- package/dist/core/agents/enterprise/finance.d.ts.map +1 -0
- package/dist/core/agents/enterprise/finance.js +35 -0
- package/dist/core/agents/enterprise/finance.js.map +1 -0
- package/dist/core/agents/enterprise/frontend.d.ts +3 -0
- package/dist/core/agents/enterprise/frontend.d.ts.map +1 -0
- package/dist/core/agents/enterprise/frontend.js +35 -0
- package/dist/core/agents/enterprise/frontend.js.map +1 -0
- package/dist/core/agents/enterprise/hr.d.ts +3 -0
- package/dist/core/agents/enterprise/hr.d.ts.map +1 -0
- package/dist/core/agents/enterprise/hr.js +35 -0
- package/dist/core/agents/enterprise/hr.js.map +1 -0
- package/dist/core/agents/enterprise/index.d.ts +4 -0
- package/dist/core/agents/enterprise/index.d.ts.map +1 -0
- package/dist/core/agents/enterprise/index.js +44 -0
- package/dist/core/agents/enterprise/index.js.map +1 -0
- package/dist/core/agents/enterprise/legal.d.ts +3 -0
- package/dist/core/agents/enterprise/legal.d.ts.map +1 -0
- package/dist/core/agents/enterprise/legal.js +35 -0
- package/dist/core/agents/enterprise/legal.js.map +1 -0
- package/dist/core/agents/enterprise/marketing.d.ts +3 -0
- package/dist/core/agents/enterprise/marketing.d.ts.map +1 -0
- package/dist/core/agents/enterprise/marketing.js +35 -0
- package/dist/core/agents/enterprise/marketing.js.map +1 -0
- package/dist/core/agents/enterprise/pm.d.ts +3 -0
- package/dist/core/agents/enterprise/pm.d.ts.map +1 -0
- package/dist/core/agents/enterprise/pm.js +35 -0
- package/dist/core/agents/enterprise/pm.js.map +1 -0
- package/dist/core/agents/enterprise/product.d.ts +3 -0
- package/dist/core/agents/enterprise/product.d.ts.map +1 -0
- package/dist/core/agents/enterprise/product.js +35 -0
- package/dist/core/agents/enterprise/product.js.map +1 -0
- package/dist/core/agents/enterprise/qa.d.ts +3 -0
- package/dist/core/agents/enterprise/qa.d.ts.map +1 -0
- package/dist/core/agents/enterprise/qa.js +35 -0
- package/dist/core/agents/enterprise/qa.js.map +1 -0
- package/dist/core/agents/enterprise/sales.d.ts +3 -0
- package/dist/core/agents/enterprise/sales.d.ts.map +1 -0
- package/dist/core/agents/enterprise/sales.js +35 -0
- package/dist/core/agents/enterprise/sales.js.map +1 -0
- package/dist/core/agents/enterprise/security.d.ts +3 -0
- package/dist/core/agents/enterprise/security.d.ts.map +1 -0
- package/dist/core/agents/enterprise/security.js +36 -0
- package/dist/core/agents/enterprise/security.js.map +1 -0
- package/dist/core/agents/enterprise/support.d.ts +3 -0
- package/dist/core/agents/enterprise/support.d.ts.map +1 -0
- package/dist/core/agents/enterprise/support.js +35 -0
- package/dist/core/agents/enterprise/support.js.map +1 -0
- package/dist/core/agents.d.ts +84 -0
- package/dist/core/agents.d.ts.map +1 -0
- package/dist/core/agents.js +266 -0
- package/dist/core/agents.js.map +1 -0
- package/dist/core/analytics.d.ts +21 -0
- package/dist/core/analytics.d.ts.map +1 -0
- package/dist/core/analytics.js +57 -0
- package/dist/core/analytics.js.map +1 -0
- package/dist/core/auth.d.ts +43 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +110 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/code_indexer.d.ts +14 -0
- package/dist/core/code_indexer.d.ts.map +1 -0
- package/dist/core/code_indexer.js +52 -0
- package/dist/core/code_indexer.js.map +1 -0
- package/dist/core/commands.d.ts +16 -0
- package/dist/core/commands.d.ts.map +1 -0
- package/dist/core/commands.js +285 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/consensus.d.ts +25 -0
- package/dist/core/consensus.d.ts.map +1 -0
- package/dist/core/consensus.js +49 -0
- package/dist/core/consensus.js.map +1 -0
- package/dist/core/context.d.ts +23 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +82 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/eden-ai-sdk.d.ts +11 -0
- package/dist/core/eden-ai-sdk.d.ts.map +1 -0
- package/dist/core/eden-ai-sdk.js +158 -0
- package/dist/core/eden-ai-sdk.js.map +1 -0
- package/dist/core/events.d.ts +64 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +18 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/groq-client.d.ts +7 -0
- package/dist/core/groq-client.d.ts.map +1 -0
- package/dist/core/groq-client.js +112 -0
- package/dist/core/groq-client.js.map +1 -0
- package/dist/core/mcp.d.ts +51 -0
- package/dist/core/mcp.d.ts.map +1 -0
- package/dist/core/mcp.js +201 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/memory.d.ts +20 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +61 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/model-profiles.d.ts +62 -0
- package/dist/core/model-profiles.d.ts.map +1 -0
- package/dist/core/model-profiles.js +841 -0
- package/dist/core/model-profiles.js.map +1 -0
- package/dist/core/moonshot-client.d.ts +7 -0
- package/dist/core/moonshot-client.d.ts.map +1 -0
- package/dist/core/moonshot-client.js +102 -0
- package/dist/core/moonshot-client.js.map +1 -0
- package/dist/core/nexus.d.ts +16 -0
- package/dist/core/nexus.d.ts.map +1 -0
- package/dist/core/nexus.js +31 -0
- package/dist/core/nexus.js.map +1 -0
- package/dist/core/notifications.d.ts +16 -0
- package/dist/core/notifications.d.ts.map +1 -0
- package/dist/core/notifications.js +26 -0
- package/dist/core/notifications.js.map +1 -0
- package/dist/core/ollama-client.d.ts +16 -0
- package/dist/core/ollama-client.d.ts.map +1 -0
- package/dist/core/ollama-client.js +138 -0
- package/dist/core/ollama-client.js.map +1 -0
- package/dist/core/orchestrator.d.ts +96 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +801 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/permissions.d.ts +34 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +224 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/providers.d.ts +22 -0
- package/dist/core/providers.d.ts.map +1 -0
- package/dist/core/providers.js +81 -0
- package/dist/core/providers.js.map +1 -0
- package/dist/core/researcher.d.ts +24 -0
- package/dist/core/researcher.d.ts.map +1 -0
- package/dist/core/researcher.js +97 -0
- package/dist/core/researcher.js.map +1 -0
- package/dist/core/sandbox.d.ts +22 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +79 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/scavenger.d.ts +5 -0
- package/dist/core/scavenger.d.ts.map +1 -0
- package/dist/core/scavenger.js +51 -0
- package/dist/core/scavenger.js.map +1 -0
- package/dist/core/server.d.ts +21 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/server.js +776 -0
- package/dist/core/server.js.map +1 -0
- package/dist/core/sessions.d.ts +54 -0
- package/dist/core/sessions.d.ts.map +1 -0
- package/dist/core/sessions.js +182 -0
- package/dist/core/sessions.js.map +1 -0
- package/dist/core/setup.d.ts +12 -0
- package/dist/core/setup.d.ts.map +1 -0
- package/dist/core/setup.js +70 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/skills.d.ts +17 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +123 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/stats.d.ts +54 -0
- package/dist/core/stats.d.ts.map +1 -0
- package/dist/core/stats.js +221 -0
- package/dist/core/stats.js.map +1 -0
- package/dist/core/swarm_manager.d.ts +20 -0
- package/dist/core/swarm_manager.d.ts.map +1 -0
- package/dist/core/swarm_manager.js +48 -0
- package/dist/core/swarm_manager.js.map +1 -0
- package/dist/core/system.d.ts +39 -0
- package/dist/core/system.d.ts.map +1 -0
- package/dist/core/system.js +129 -0
- package/dist/core/system.js.map +1 -0
- package/dist/core/tasks.d.ts +24 -0
- package/dist/core/tasks.d.ts.map +1 -0
- package/dist/core/tasks.js +66 -0
- package/dist/core/tasks.js.map +1 -0
- package/dist/core/telegram.d.ts +9 -0
- package/dist/core/telegram.d.ts.map +1 -0
- package/dist/core/telegram.js +49 -0
- package/dist/core/telegram.js.map +1 -0
- package/dist/core/tools/acquire_skill.d.ts +3 -0
- package/dist/core/tools/acquire_skill.d.ts.map +1 -0
- package/dist/core/tools/acquire_skill.js +30 -0
- package/dist/core/tools/acquire_skill.js.map +1 -0
- package/dist/core/tools/agent_tools.d.ts +4 -0
- package/dist/core/tools/agent_tools.d.ts.map +1 -0
- package/dist/core/tools/agent_tools.js +117 -0
- package/dist/core/tools/agent_tools.js.map +1 -0
- package/dist/core/tools/code_search.d.ts +3 -0
- package/dist/core/tools/code_search.d.ts.map +1 -0
- package/dist/core/tools/code_search.js +43 -0
- package/dist/core/tools/code_search.js.map +1 -0
- package/dist/core/tools/craft_tool.d.ts +3 -0
- package/dist/core/tools/craft_tool.d.ts.map +1 -0
- package/dist/core/tools/craft_tool.js +32 -0
- package/dist/core/tools/craft_tool.js.map +1 -0
- package/dist/core/tools/discord.d.ts +3 -0
- package/dist/core/tools/discord.d.ts.map +1 -0
- package/dist/core/tools/discord.js +206 -0
- package/dist/core/tools/discord.js.map +1 -0
- package/dist/core/tools/dynamic/registry.d.ts +15 -0
- package/dist/core/tools/dynamic/registry.d.ts.map +1 -0
- package/dist/core/tools/dynamic/registry.js +59 -0
- package/dist/core/tools/dynamic/registry.js.map +1 -0
- package/dist/core/tools/github.d.ts +3 -0
- package/dist/core/tools/github.d.ts.map +1 -0
- package/dist/core/tools/github.js +357 -0
- package/dist/core/tools/github.js.map +1 -0
- package/dist/core/tools/google.d.ts +3 -0
- package/dist/core/tools/google.d.ts.map +1 -0
- package/dist/core/tools/google.js +409 -0
- package/dist/core/tools/google.js.map +1 -0
- package/dist/core/tools/index.d.ts +4 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +2338 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/optimize_prompt.d.ts +3 -0
- package/dist/core/tools/optimize_prompt.d.ts.map +1 -0
- package/dist/core/tools/optimize_prompt.js +43 -0
- package/dist/core/tools/optimize_prompt.js.map +1 -0
- package/dist/core/tools/predict_healing.d.ts +3 -0
- package/dist/core/tools/predict_healing.d.ts.map +1 -0
- package/dist/core/tools/predict_healing.js +34 -0
- package/dist/core/tools/predict_healing.js.map +1 -0
- package/dist/core/tools/sage_memory.d.ts +3 -0
- package/dist/core/tools/sage_memory.d.ts.map +1 -0
- package/dist/core/tools/sage_memory.js +29 -0
- package/dist/core/tools/sage_memory.js.map +1 -0
- package/dist/core/tools/self_heal.d.ts +3 -0
- package/dist/core/tools/self_heal.d.ts.map +1 -0
- package/dist/core/tools/self_heal.js +46 -0
- package/dist/core/tools/self_heal.js.map +1 -0
- package/dist/core/tools/send_briefing.d.ts +3 -0
- package/dist/core/tools/send_briefing.d.ts.map +1 -0
- package/dist/core/tools/send_briefing.js +31 -0
- package/dist/core/tools/send_briefing.js.map +1 -0
- package/dist/core/tools/slack.d.ts +3 -0
- package/dist/core/tools/slack.d.ts.map +1 -0
- package/dist/core/tools/slack.js +256 -0
- package/dist/core/tools/slack.js.map +1 -0
- package/dist/core/tools/telegram.d.ts +3 -0
- package/dist/core/tools/telegram.d.ts.map +1 -0
- package/dist/core/tools/telegram.js +211 -0
- package/dist/core/tools/telegram.js.map +1 -0
- package/dist/core/tools/vox.d.ts +3 -0
- package/dist/core/tools/vox.d.ts.map +1 -0
- package/dist/core/tools/vox.js +29 -0
- package/dist/core/tools/vox.js.map +1 -0
- package/dist/core/types/index.d.ts +164 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +3 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/core/vault.d.ts +20 -0
- package/dist/core/vault.d.ts.map +1 -0
- package/dist/core/vault.js +91 -0
- package/dist/core/vault.js.map +1 -0
- package/dist/core/verify_v3.d.ts +2 -0
- package/dist/core/verify_v3.d.ts.map +1 -0
- package/dist/core/verify_v3.js +38 -0
- package/dist/core/verify_v3.js.map +1 -0
- package/dist/core/verify_v3_5.d.ts +2 -0
- package/dist/core/verify_v3_5.d.ts.map +1 -0
- package/dist/core/verify_v3_5.js +56 -0
- package/dist/core/verify_v3_5.js.map +1 -0
- package/dist/core/verify_v4.d.ts +2 -0
- package/dist/core/verify_v4.d.ts.map +1 -0
- package/dist/core/verify_v4.js +42 -0
- package/dist/core/verify_v4.js.map +1 -0
- package/dist/core/verify_v5.d.ts +2 -0
- package/dist/core/verify_v5.d.ts.map +1 -0
- package/dist/core/verify_v5.js +53 -0
- package/dist/core/verify_v5.js.map +1 -0
- package/dist/core/verify_v6.d.ts +2 -0
- package/dist/core/verify_v6.d.ts.map +1 -0
- package/dist/core/verify_v6.js +42 -0
- package/dist/core/verify_v6.js.map +1 -0
- package/dist/core/verify_v7.d.ts +2 -0
- package/dist/core/verify_v7.d.ts.map +1 -0
- package/dist/core/verify_v7.js +29 -0
- package/dist/core/verify_v7.js.map +1 -0
- package/dist/core/verify_v8.d.ts +2 -0
- package/dist/core/verify_v8.d.ts.map +1 -0
- package/dist/core/verify_v8.js +32 -0
- package/dist/core/verify_v8.js.map +1 -0
- package/dist/core/workflows.d.ts +24 -0
- package/dist/core/workflows.d.ts.map +1 -0
- package/dist/core/workflows.js +47 -0
- package/dist/core/workflows.js.map +1 -0
- package/dist/core/workspaces.d.ts +13 -0
- package/dist/core/workspaces.d.ts.map +1 -0
- package/dist/core/workspaces.js +45 -0
- package/dist/core/workspaces.js.map +1 -0
- package/dist/enquirer-input.d.ts +3 -0
- package/dist/enquirer-input.d.ts.map +1 -0
- package/dist/enquirer-input.js +112 -0
- package/dist/enquirer-input.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +241 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +51 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +201 -0
- package/dist/mcp.js.map +1 -0
- package/dist/ollama-client.d.ts +15 -0
- package/dist/ollama-client.d.ts.map +1 -0
- package/dist/ollama-client.js +95 -0
- package/dist/ollama-client.js.map +1 -0
- package/dist/permissions.d.ts +34 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +224 -0
- package/dist/permissions.js.map +1 -0
- package/dist/realtime-input.d.ts +2 -0
- package/dist/realtime-input.d.ts.map +1 -0
- package/dist/realtime-input.js +308 -0
- package/dist/realtime-input.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +203 -0
- package/dist/server.js.map +1 -0
- package/dist/sessions.d.ts +50 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +181 -0
- package/dist/sessions.js.map +1 -0
- package/dist/simple-input.d.ts +2 -0
- package/dist/simple-input.d.ts.map +1 -0
- package/dist/simple-input.js +83 -0
- package/dist/simple-input.js.map +1 -0
- package/dist/skills.d.ts +17 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +194 -0
- package/dist/skills.js.map +1 -0
- package/dist/stats.d.ts +54 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +220 -0
- package/dist/stats.js.map +1 -0
- package/dist/tools/discord.d.ts +3 -0
- package/dist/tools/discord.d.ts.map +1 -0
- package/dist/tools/discord.js +206 -0
- package/dist/tools/discord.js.map +1 -0
- package/dist/tools/github.d.ts +3 -0
- package/dist/tools/github.d.ts.map +1 -0
- package/dist/tools/github.js +357 -0
- package/dist/tools/github.js.map +1 -0
- package/dist/tools/google.d.ts +3 -0
- package/dist/tools/google.d.ts.map +1 -0
- package/dist/tools/google.js +409 -0
- package/dist/tools/google.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2139 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/slack.d.ts +3 -0
- package/dist/tools/slack.d.ts.map +1 -0
- package/dist/tools/slack.js +256 -0
- package/dist/tools/slack.js.map +1 -0
- package/dist/tools/telegram.d.ts +3 -0
- package/dist/tools/telegram.d.ts.map +1 -0
- package/dist/tools/telegram.js +211 -0
- package/dist/tools/telegram.js.map +1 -0
- package/dist/types/index.d.ts +152 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui.d.ts +101 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +317 -0
- package/dist/ui.js.map +1 -0
- package/dist/web/src/lib/utils.d.ts +3 -0
- package/dist/web/src/lib/utils.d.ts.map +1 -0
- package/dist/web/src/lib/utils.js +6 -0
- package/dist/web/src/lib/utils.js.map +1 -0
- package/dist/web/src/main.d.ts +2 -0
- package/dist/web/src/main.d.ts.map +1 -0
- package/dist/web/src/main.js +5 -0
- package/dist/web/src/main.js.map +1 -0
- package/dist/web/vite.config.d.ts +3 -0
- package/dist/web/vite.config.d.ts.map +1 -0
- package/dist/web/vite.config.js +12 -0
- package/dist/web/vite.config.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,2139 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
import { slackTools } from './slack.js';
|
|
6
|
+
import { googleTools } from './google.js';
|
|
7
|
+
import { githubTools } from './github.js';
|
|
8
|
+
import { discordTools } from './discord.js';
|
|
9
|
+
import { telegramTools } from './telegram.js';
|
|
10
|
+
import { colors } from '../ui.js';
|
|
11
|
+
// File operations tool
|
|
12
|
+
const readFileTool = {
|
|
13
|
+
definition: {
|
|
14
|
+
name: 'read_file',
|
|
15
|
+
description: 'Read the contents of a file at the specified path. Returns the file content as text.',
|
|
16
|
+
parameters: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
file_path: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'The absolute path to the file to read',
|
|
22
|
+
},
|
|
23
|
+
offset: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
description: 'Optional line offset to start reading from (1-indexed)',
|
|
26
|
+
},
|
|
27
|
+
limit: {
|
|
28
|
+
type: 'number',
|
|
29
|
+
description: 'Optional maximum number of lines to read',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ['file_path'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
handler: async (args) => {
|
|
36
|
+
try {
|
|
37
|
+
const filePath = String(args.file_path);
|
|
38
|
+
const offset = args.offset ? Number(args.offset) : undefined;
|
|
39
|
+
const limit = args.limit ? Number(args.limit) : undefined;
|
|
40
|
+
let content = await fs.readFile(filePath, 'utf8');
|
|
41
|
+
if (offset !== undefined || limit !== undefined) {
|
|
42
|
+
const lines = content.split('\n');
|
|
43
|
+
const start = offset ? Math.max(0, offset - 1) : 0;
|
|
44
|
+
const end = limit ? Math.min(lines.length, start + limit) : lines.length;
|
|
45
|
+
content = lines.slice(start, end).join('\n');
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
status: 'success',
|
|
49
|
+
output: content,
|
|
50
|
+
metadata: { file_path: filePath },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
return {
|
|
55
|
+
status: 'error',
|
|
56
|
+
error: err instanceof Error ? err.message : String(err),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
// Write file tool
|
|
62
|
+
const writeFileTool = {
|
|
63
|
+
definition: {
|
|
64
|
+
name: 'write_file',
|
|
65
|
+
description: 'Write content to a file at the specified path. Creates the file if it does not exist.',
|
|
66
|
+
parameters: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: {
|
|
69
|
+
file_path: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
description: 'The absolute path to the file to write',
|
|
72
|
+
},
|
|
73
|
+
content: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'The content to write to the file',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
required: ['file_path', 'content'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
handler: async (args) => {
|
|
82
|
+
try {
|
|
83
|
+
let filePath = String(args.file_path);
|
|
84
|
+
const content = String(args.content);
|
|
85
|
+
// Fix paths that reference non-existent home directories
|
|
86
|
+
if (filePath.startsWith('/home/user') || filePath.startsWith('/root')) {
|
|
87
|
+
const relativePath = filePath.replace(/^\/home\/user\//, '').replace(/^\/root\//, '');
|
|
88
|
+
filePath = path.join(process.cwd(), relativePath);
|
|
89
|
+
}
|
|
90
|
+
// Ensure directory exists
|
|
91
|
+
const dir = path.dirname(filePath);
|
|
92
|
+
try {
|
|
93
|
+
await fs.mkdir(dir, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
catch (mkdirErr) {
|
|
96
|
+
// If we can't create the directory, fallback to current directory
|
|
97
|
+
if (mkdirErr.code === 'EACCES' || mkdirErr.code === 'EPERM') {
|
|
98
|
+
const basename = path.basename(filePath);
|
|
99
|
+
filePath = path.join(process.cwd(), basename);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
103
|
+
return {
|
|
104
|
+
status: 'success',
|
|
105
|
+
output: `File written successfully: ${filePath}`,
|
|
106
|
+
metadata: { file_path: filePath, bytes: content.length },
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return {
|
|
111
|
+
status: 'error',
|
|
112
|
+
error: err instanceof Error ? err.message : String(err),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
// Edit file tool (search and replace)
|
|
118
|
+
const editFileTool = {
|
|
119
|
+
definition: {
|
|
120
|
+
name: 'edit_file',
|
|
121
|
+
description: 'Edit a file by replacing occurrences of old_string with new_string.',
|
|
122
|
+
parameters: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
file_path: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
description: 'The absolute path to the file to edit',
|
|
128
|
+
},
|
|
129
|
+
old_string: {
|
|
130
|
+
type: 'string',
|
|
131
|
+
description: 'The text to search for and replace',
|
|
132
|
+
},
|
|
133
|
+
new_string: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'The replacement text',
|
|
136
|
+
},
|
|
137
|
+
replace_all: {
|
|
138
|
+
type: 'boolean',
|
|
139
|
+
description: 'If true, replace all occurrences instead of just the first one',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
required: ['file_path', 'old_string', 'new_string'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
handler: async (args) => {
|
|
146
|
+
try {
|
|
147
|
+
const filePath = String(args.file_path);
|
|
148
|
+
const oldString = String(args.old_string);
|
|
149
|
+
const newString = String(args.new_string);
|
|
150
|
+
const replaceAll = Boolean(args.replace_all);
|
|
151
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
152
|
+
if (!content.includes(oldString)) {
|
|
153
|
+
return {
|
|
154
|
+
status: 'error',
|
|
155
|
+
error: `Could not find the string to replace in ${filePath}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const newContent = replaceAll
|
|
159
|
+
? content.split(oldString).join(newString)
|
|
160
|
+
: content.replace(oldString, newString);
|
|
161
|
+
await fs.writeFile(filePath, newContent, 'utf8');
|
|
162
|
+
return {
|
|
163
|
+
status: 'success',
|
|
164
|
+
output: `File edited successfully: ${filePath}`,
|
|
165
|
+
metadata: { file_path: filePath, replacements: replaceAll ? 'all' : 'first' },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
return {
|
|
170
|
+
status: 'error',
|
|
171
|
+
error: err instanceof Error ? err.message : String(err),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
// List directory tool
|
|
177
|
+
const listDirTool = {
|
|
178
|
+
definition: {
|
|
179
|
+
name: 'list_dir',
|
|
180
|
+
description: 'List files and directories at the specified path. Returns file names, sizes, and modification times.',
|
|
181
|
+
parameters: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
properties: {
|
|
184
|
+
directory_path: {
|
|
185
|
+
type: 'string',
|
|
186
|
+
description: 'The absolute path to the directory to list',
|
|
187
|
+
},
|
|
188
|
+
recursive: {
|
|
189
|
+
type: 'boolean',
|
|
190
|
+
description: 'If true, list recursively',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
required: ['directory_path'],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
handler: async (args) => {
|
|
197
|
+
try {
|
|
198
|
+
const dirPath = String(args.directory_path);
|
|
199
|
+
const recursive = Boolean(args.recursive);
|
|
200
|
+
const entries = [];
|
|
201
|
+
if (recursive) {
|
|
202
|
+
const walk = async (currentPath, prefix) => {
|
|
203
|
+
const items = await fs.readdir(currentPath, { withFileTypes: true });
|
|
204
|
+
for (const item of items) {
|
|
205
|
+
const fullPath = path.join(currentPath, item.name);
|
|
206
|
+
const stat = await fs.stat(fullPath);
|
|
207
|
+
const size = stat.isFile() ? ` (${stat.size} bytes)` : ' (dir)';
|
|
208
|
+
entries.push(`${prefix}${item.name}${size}`);
|
|
209
|
+
if (item.isDirectory()) {
|
|
210
|
+
await walk(fullPath, prefix + ' ');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
await walk(dirPath, '');
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const items = await fs.readdir(dirPath, { withFileTypes: true });
|
|
218
|
+
for (const item of items) {
|
|
219
|
+
const fullPath = path.join(dirPath, item.name);
|
|
220
|
+
const stat = await fs.stat(fullPath);
|
|
221
|
+
const size = stat.isFile() ? ` (${stat.size} bytes)` : ' (dir)';
|
|
222
|
+
entries.push(`${item.name}${size}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
status: 'success',
|
|
227
|
+
output: entries.join('\n'),
|
|
228
|
+
metadata: { directory: dirPath, count: entries.length },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
return {
|
|
233
|
+
status: 'error',
|
|
234
|
+
error: err instanceof Error ? err.message : String(err),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
// Grep search tool
|
|
240
|
+
const grepSearchTool = {
|
|
241
|
+
definition: {
|
|
242
|
+
name: 'grep_search',
|
|
243
|
+
description: 'Search for text patterns in files using ripgrep-style matching.',
|
|
244
|
+
parameters: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
properties: {
|
|
247
|
+
query: {
|
|
248
|
+
type: 'string',
|
|
249
|
+
description: 'The search pattern or text to find',
|
|
250
|
+
},
|
|
251
|
+
path: {
|
|
252
|
+
type: 'string',
|
|
253
|
+
description: 'The directory or file path to search in',
|
|
254
|
+
},
|
|
255
|
+
file_pattern: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: 'Optional glob pattern to filter files (e.g., "*.ts", "*.js")',
|
|
258
|
+
},
|
|
259
|
+
case_sensitive: {
|
|
260
|
+
type: 'boolean',
|
|
261
|
+
description: 'If false, perform case-insensitive search',
|
|
262
|
+
},
|
|
263
|
+
fixed_strings: {
|
|
264
|
+
type: 'boolean',
|
|
265
|
+
description: 'If true, treat query as literal string instead of regex',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
required: ['query', 'path'],
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
handler: async (args) => {
|
|
272
|
+
try {
|
|
273
|
+
const query = String(args.query);
|
|
274
|
+
const searchPath = String(args.path);
|
|
275
|
+
const filePattern = args.file_pattern ? String(args.file_pattern) : undefined;
|
|
276
|
+
const caseSensitive = args.case_sensitive !== false;
|
|
277
|
+
const fixedStrings = Boolean(args.fixed_strings);
|
|
278
|
+
// Build grep/ripgrep command
|
|
279
|
+
const args_list = [];
|
|
280
|
+
if (!caseSensitive)
|
|
281
|
+
args_list.push('-i');
|
|
282
|
+
if (fixedStrings)
|
|
283
|
+
args_list.push('-F');
|
|
284
|
+
if (filePattern) {
|
|
285
|
+
args_list.push('--include', filePattern);
|
|
286
|
+
}
|
|
287
|
+
args_list.push(query, searchPath);
|
|
288
|
+
const result = execSync(`rg ${args_list.join(' ')} 2>/dev/null || grep -r ${args_list.join(' ')} 2>/dev/null || echo "No matches found"`, {
|
|
289
|
+
encoding: 'utf8',
|
|
290
|
+
maxBuffer: 1024 * 1024,
|
|
291
|
+
});
|
|
292
|
+
const lines = result.trim().split('\n').filter(Boolean);
|
|
293
|
+
const truncated = lines.length > 50;
|
|
294
|
+
const output = truncated ? lines.slice(0, 50).join('\n') + '\n... (truncated)' : result;
|
|
295
|
+
return {
|
|
296
|
+
status: 'success',
|
|
297
|
+
output,
|
|
298
|
+
metadata: {
|
|
299
|
+
matches: lines.length,
|
|
300
|
+
truncated,
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
return {
|
|
306
|
+
status: 'error',
|
|
307
|
+
error: err instanceof Error ? err.message : String(err),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
// Run command tool - improved with better output handling and suggestions
|
|
313
|
+
const runCommandTool = {
|
|
314
|
+
definition: {
|
|
315
|
+
name: 'run_command',
|
|
316
|
+
description: 'Execute a shell command. Returns stdout and stderr output. Shows suggestions if command fails or has no output.',
|
|
317
|
+
parameters: {
|
|
318
|
+
type: 'object',
|
|
319
|
+
properties: {
|
|
320
|
+
command: {
|
|
321
|
+
type: 'string',
|
|
322
|
+
description: 'The command to execute',
|
|
323
|
+
},
|
|
324
|
+
cwd: {
|
|
325
|
+
type: 'string',
|
|
326
|
+
description: 'Optional working directory for the command',
|
|
327
|
+
},
|
|
328
|
+
timeout_sec: {
|
|
329
|
+
type: 'number',
|
|
330
|
+
description: 'Optional timeout in seconds (default: 60)',
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
required: ['command'],
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
handler: async (args) => {
|
|
337
|
+
try {
|
|
338
|
+
const command = String(args.command);
|
|
339
|
+
const cwd = args.cwd ? String(args.cwd) : process.cwd();
|
|
340
|
+
const timeoutSec = args.timeout_sec ? Number(args.timeout_sec) : 60;
|
|
341
|
+
return new Promise((resolve) => {
|
|
342
|
+
// Stop spinner so we don't clobber live output
|
|
343
|
+
console.log(); // Add a newline before output
|
|
344
|
+
const child = spawn('bash', ['-c', command], {
|
|
345
|
+
cwd,
|
|
346
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
347
|
+
});
|
|
348
|
+
let stdout = '';
|
|
349
|
+
let stderr = '';
|
|
350
|
+
let timeout = null;
|
|
351
|
+
if (timeoutSec > 0) {
|
|
352
|
+
timeout = setTimeout(() => {
|
|
353
|
+
child.kill('SIGTERM');
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
if (!child.killed)
|
|
356
|
+
child.kill('SIGKILL');
|
|
357
|
+
}, 5000);
|
|
358
|
+
}, timeoutSec * 1000);
|
|
359
|
+
}
|
|
360
|
+
child.stdout?.on('data', (data) => {
|
|
361
|
+
process.stdout.write(colors.muted(String(data)));
|
|
362
|
+
stdout += String(data);
|
|
363
|
+
});
|
|
364
|
+
child.stderr?.on('data', (data) => {
|
|
365
|
+
process.stderr.write(colors.error(String(data)));
|
|
366
|
+
stderr += String(data);
|
|
367
|
+
});
|
|
368
|
+
child.on('close', (code) => {
|
|
369
|
+
if (timeout)
|
|
370
|
+
clearTimeout(timeout);
|
|
371
|
+
console.log(); // End visual block of command stream
|
|
372
|
+
const isError = code !== 0;
|
|
373
|
+
const hasStdout = stdout.trim().length > 0;
|
|
374
|
+
const hasStderr = stderr.trim().length > 0;
|
|
375
|
+
// Build comprehensive output
|
|
376
|
+
let output = '';
|
|
377
|
+
let suggestions = [];
|
|
378
|
+
if (hasStdout) {
|
|
379
|
+
output += stdout;
|
|
380
|
+
}
|
|
381
|
+
if (hasStderr) {
|
|
382
|
+
if (output)
|
|
383
|
+
output += '\n\n';
|
|
384
|
+
output += `STDERR:\n${stderr}`;
|
|
385
|
+
}
|
|
386
|
+
// Handle silent success cases (mkdir, touch, etc.)
|
|
387
|
+
if (!isError && !hasStdout && !hasStderr) {
|
|
388
|
+
// Check what command was run and provide context
|
|
389
|
+
const cmdBase = command.trim().split(' ')[0];
|
|
390
|
+
const silentCommands = ['mkdir', 'touch', 'rm', 'cp', 'mv', 'chmod', 'chown'];
|
|
391
|
+
if (silentCommands.includes(cmdBase)) {
|
|
392
|
+
output = `✓ Comando ejecutado exitosamente (sin output)`;
|
|
393
|
+
// Show what was created/modified
|
|
394
|
+
if (cmdBase === 'mkdir') {
|
|
395
|
+
const match = command.match(/mkdir\s+-?p?\s+(.*)/);
|
|
396
|
+
if (match) {
|
|
397
|
+
output += `\nDirectorios creados: ${match[1]}`;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
output = '(comando completado sin output)';
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// Generate suggestions for common errors
|
|
406
|
+
if (isError) {
|
|
407
|
+
const errorText = stderr.toLowerCase();
|
|
408
|
+
if (errorText.includes('command not found') || errorText.includes('no such file')) {
|
|
409
|
+
suggestions.push('Verifica que el comando esté instalado: `which <comando>`');
|
|
410
|
+
suggestions.push('Alternativa: Instala con tu package manager (apt, npm, etc.)');
|
|
411
|
+
}
|
|
412
|
+
if (errorText.includes('permission denied') || errorText.includes('eacces')) {
|
|
413
|
+
suggestions.push('Intenta con sudo o verifica permisos: `ls -la`');
|
|
414
|
+
suggestions.push('Alternativa: Cambia permisos con `chmod` o cambia de directorio');
|
|
415
|
+
}
|
|
416
|
+
if (errorText.includes('file exists') || errorText.includes('eexist')) {
|
|
417
|
+
suggestions.push('El archivo/directorio ya existe');
|
|
418
|
+
suggestions.push('Alternativa: Usa `-f` (force) o elimina primero con `rm`');
|
|
419
|
+
}
|
|
420
|
+
if (errorText.includes('no such file or directory')) {
|
|
421
|
+
suggestions.push('Verifica que la ruta existe con `ls -la`');
|
|
422
|
+
suggestions.push('Alternativa: Crea directorios padre primero con `mkdir -p`');
|
|
423
|
+
}
|
|
424
|
+
if (errorText.includes('connection refused') || errorText.includes('timeout')) {
|
|
425
|
+
suggestions.push('Verifica conectividad de red');
|
|
426
|
+
suggestions.push('Alternativa: Revisa firewall o intenta más tarde');
|
|
427
|
+
}
|
|
428
|
+
if (suggestions.length === 0) {
|
|
429
|
+
suggestions.push('Verifica el comando con `--help` o man pages');
|
|
430
|
+
suggestions.push('Alternativa: Busca ejemplos en documentación oficial');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Build final response
|
|
434
|
+
let finalOutput = output;
|
|
435
|
+
if (suggestions.length > 0) {
|
|
436
|
+
finalOutput += '\n\n💡 Sugerencias:\n' + suggestions.map(s => ` • ${s}`).join('\n');
|
|
437
|
+
}
|
|
438
|
+
resolve({
|
|
439
|
+
status: isError ? 'error' : 'success',
|
|
440
|
+
output: finalOutput || '(no output)',
|
|
441
|
+
error: isError ? `Exit code: ${code}` : undefined,
|
|
442
|
+
metadata: {
|
|
443
|
+
exit_code: code,
|
|
444
|
+
stdout_length: stdout.length,
|
|
445
|
+
stderr_length: stderr.length,
|
|
446
|
+
command: command.slice(0, 100), // Truncate for safety
|
|
447
|
+
suggestions_count: suggestions.length,
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
child.on('error', (err) => {
|
|
452
|
+
if (timeout)
|
|
453
|
+
clearTimeout(timeout);
|
|
454
|
+
resolve({
|
|
455
|
+
status: 'error',
|
|
456
|
+
output: '',
|
|
457
|
+
error: `Failed to execute: ${err.message}\n\n💡 Sugerencias:\n • Verifica que el shell esté disponible\n • Alternativa: Intenta con el comando completo (ej: /bin/ls)`,
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
return {
|
|
464
|
+
status: 'error',
|
|
465
|
+
output: '',
|
|
466
|
+
error: err instanceof Error ? err.message : String(err),
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
// Find files by name tool
|
|
472
|
+
const findByNameTool = {
|
|
473
|
+
definition: {
|
|
474
|
+
name: 'find_by_name',
|
|
475
|
+
description: 'Find files and directories by name pattern.',
|
|
476
|
+
parameters: {
|
|
477
|
+
type: 'object',
|
|
478
|
+
properties: {
|
|
479
|
+
pattern: {
|
|
480
|
+
type: 'string',
|
|
481
|
+
description: 'The glob pattern to match (e.g., "*.ts", "package.json")',
|
|
482
|
+
},
|
|
483
|
+
directory: {
|
|
484
|
+
type: 'string',
|
|
485
|
+
description: 'The directory to search in',
|
|
486
|
+
},
|
|
487
|
+
max_depth: {
|
|
488
|
+
type: 'number',
|
|
489
|
+
description: 'Maximum directory depth to search',
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
required: ['pattern', 'directory'],
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
handler: async (args) => {
|
|
496
|
+
try {
|
|
497
|
+
const pattern = String(args.pattern);
|
|
498
|
+
const directory = String(args.directory);
|
|
499
|
+
const maxDepth = args.max_depth ? Number(args.max_depth) : undefined;
|
|
500
|
+
let command = `find "${directory}" -name "${pattern}" -type f`;
|
|
501
|
+
if (maxDepth) {
|
|
502
|
+
command += ` -maxdepth ${maxDepth}`;
|
|
503
|
+
}
|
|
504
|
+
const result = execSync(command, { encoding: 'utf8' });
|
|
505
|
+
const files = result.trim().split('\n').filter(Boolean);
|
|
506
|
+
return {
|
|
507
|
+
status: 'success',
|
|
508
|
+
output: files.join('\n') || '(no files found)',
|
|
509
|
+
metadata: { count: files.length },
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
return {
|
|
514
|
+
status: 'error',
|
|
515
|
+
error: err instanceof Error ? err.message : String(err),
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
// View URL content tool
|
|
521
|
+
const viewUrlTool = {
|
|
522
|
+
definition: {
|
|
523
|
+
name: 'view_url',
|
|
524
|
+
description: 'Fetch and view content from a URL.',
|
|
525
|
+
parameters: {
|
|
526
|
+
type: 'object',
|
|
527
|
+
properties: {
|
|
528
|
+
url: {
|
|
529
|
+
type: 'string',
|
|
530
|
+
description: 'The URL to fetch',
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
required: ['url'],
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
handler: async (args) => {
|
|
537
|
+
try {
|
|
538
|
+
const url = String(args.url);
|
|
539
|
+
const response = await fetch(url);
|
|
540
|
+
if (!response.ok) {
|
|
541
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
542
|
+
}
|
|
543
|
+
const content = await response.text();
|
|
544
|
+
const truncated = content.length > 10000;
|
|
545
|
+
const output = truncated ? content.slice(0, 10000) + '\n... (truncated)' : content;
|
|
546
|
+
return {
|
|
547
|
+
status: 'success',
|
|
548
|
+
output,
|
|
549
|
+
metadata: {
|
|
550
|
+
url,
|
|
551
|
+
content_type: response.headers.get('content-type'),
|
|
552
|
+
content_length: content.length,
|
|
553
|
+
truncated,
|
|
554
|
+
},
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
return {
|
|
559
|
+
status: 'error',
|
|
560
|
+
error: err instanceof Error ? err.message : String(err),
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
// Ask user question tool
|
|
566
|
+
const askUserTool = {
|
|
567
|
+
definition: {
|
|
568
|
+
name: 'ask_user',
|
|
569
|
+
description: 'Ask the user a question with predefined options.',
|
|
570
|
+
parameters: {
|
|
571
|
+
type: 'object',
|
|
572
|
+
properties: {
|
|
573
|
+
question: {
|
|
574
|
+
type: 'string',
|
|
575
|
+
description: 'The question to ask',
|
|
576
|
+
},
|
|
577
|
+
options: {
|
|
578
|
+
type: 'array',
|
|
579
|
+
description: 'Array of option objects with label and description',
|
|
580
|
+
items: {
|
|
581
|
+
type: 'object',
|
|
582
|
+
properties: {
|
|
583
|
+
label: { type: 'string' },
|
|
584
|
+
description: { type: 'string' },
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
allow_multiple: {
|
|
589
|
+
type: 'boolean',
|
|
590
|
+
description: 'Allow selecting multiple options',
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
required: ['question', 'options'],
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
handler: (args) => {
|
|
597
|
+
const question = String(args.question);
|
|
598
|
+
const options = Array.isArray(args.options) ? args.options : [];
|
|
599
|
+
const allowMultiple = Boolean(args.allow_multiple);
|
|
600
|
+
const formatted = options.map((opt, i) => {
|
|
601
|
+
const o = opt;
|
|
602
|
+
return `${i + 1}. ${o.label || '?'}: ${o.description || ''}`;
|
|
603
|
+
}).join('\n');
|
|
604
|
+
return {
|
|
605
|
+
status: 'success',
|
|
606
|
+
output: `USER QUESTION: ${question}\n\nOptions (${allowMultiple ? 'select multiple' : 'select one'}):\n${formatted}\n\nPlease respond with the number(s) of your choice.`,
|
|
607
|
+
metadata: { requires_user_input: true },
|
|
608
|
+
};
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
// Create memory tool
|
|
612
|
+
const createMemoryTool = {
|
|
613
|
+
definition: {
|
|
614
|
+
name: 'create_memory',
|
|
615
|
+
description: 'Save important context to the memory database.',
|
|
616
|
+
parameters: {
|
|
617
|
+
type: 'object',
|
|
618
|
+
properties: {
|
|
619
|
+
content: {
|
|
620
|
+
type: 'string',
|
|
621
|
+
description: 'The content to remember',
|
|
622
|
+
},
|
|
623
|
+
tags: {
|
|
624
|
+
type: 'array',
|
|
625
|
+
description: 'Tags to associate with the memory',
|
|
626
|
+
items: { type: 'string' },
|
|
627
|
+
},
|
|
628
|
+
title: {
|
|
629
|
+
type: 'string',
|
|
630
|
+
description: 'Title for the memory',
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
required: ['content', 'title'],
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
handler: (args) => {
|
|
637
|
+
const content = String(args.content);
|
|
638
|
+
const title = String(args.title);
|
|
639
|
+
const tags = Array.isArray(args.tags) ? args.tags : [];
|
|
640
|
+
// In a real implementation, this would write to a database
|
|
641
|
+
// For now, we just acknowledge the memory was created
|
|
642
|
+
return {
|
|
643
|
+
status: 'success',
|
|
644
|
+
output: `Memory saved: "${title}"\n\n${content}\n\nTags: ${tags.join(', ') || 'none'}`,
|
|
645
|
+
metadata: { title, tags },
|
|
646
|
+
};
|
|
647
|
+
},
|
|
648
|
+
};
|
|
649
|
+
// Vision/Image analysis tool - analyze images using Ollama vision models
|
|
650
|
+
const analyzeImageTool = {
|
|
651
|
+
definition: {
|
|
652
|
+
name: 'analyze_image',
|
|
653
|
+
description: 'Analyze an image file using vision AI. Returns a description of what the image contains.',
|
|
654
|
+
parameters: {
|
|
655
|
+
type: 'object',
|
|
656
|
+
properties: {
|
|
657
|
+
image_path: {
|
|
658
|
+
type: 'string',
|
|
659
|
+
description: 'The absolute path to the image file',
|
|
660
|
+
},
|
|
661
|
+
prompt: {
|
|
662
|
+
type: 'string',
|
|
663
|
+
description: 'Optional specific question about the image (default: "Describe this image in detail")',
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
required: ['image_path'],
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
handler: async (args) => {
|
|
670
|
+
try {
|
|
671
|
+
const imagePath = String(args.image_path);
|
|
672
|
+
const prompt = args.prompt ? String(args.prompt) : 'Describe this image in detail';
|
|
673
|
+
// Read image as base64
|
|
674
|
+
const imageBuffer = await fs.readFile(imagePath);
|
|
675
|
+
const base64Image = imageBuffer.toString('base64');
|
|
676
|
+
// Determine mime type from extension
|
|
677
|
+
const ext = path.extname(imagePath).toLowerCase();
|
|
678
|
+
const mimeType = ext === '.png' ? 'image/png' :
|
|
679
|
+
ext === '.gif' ? 'image/gif' :
|
|
680
|
+
ext === '.webp' ? 'image/webp' : 'image/jpeg';
|
|
681
|
+
// Call Ollama with vision capabilities
|
|
682
|
+
const ollamaUrl = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
683
|
+
const model = process.env.VISION_MODEL || 'llava';
|
|
684
|
+
const response = await fetch(`${ollamaUrl}/api/generate`, {
|
|
685
|
+
method: 'POST',
|
|
686
|
+
headers: { 'Content-Type': 'application/json' },
|
|
687
|
+
body: JSON.stringify({
|
|
688
|
+
model,
|
|
689
|
+
prompt,
|
|
690
|
+
images: [base64Image],
|
|
691
|
+
stream: false,
|
|
692
|
+
}),
|
|
693
|
+
});
|
|
694
|
+
if (!response.ok) {
|
|
695
|
+
throw new Error(`Ollama API error: ${response.status}`);
|
|
696
|
+
}
|
|
697
|
+
const result = await response.json();
|
|
698
|
+
return {
|
|
699
|
+
status: 'success',
|
|
700
|
+
output: result.response,
|
|
701
|
+
metadata: { image_path: imagePath, model, prompt },
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
catch (err) {
|
|
705
|
+
return {
|
|
706
|
+
status: 'error',
|
|
707
|
+
error: err instanceof Error ? err.message : String(err),
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
};
|
|
712
|
+
// Text embedding tool - generate embeddings for text similarity
|
|
713
|
+
const embeddingTool = {
|
|
714
|
+
definition: {
|
|
715
|
+
name: 'generate_embedding',
|
|
716
|
+
description: 'Generate vector embeddings for text using Ollama. Useful for semantic search and similarity comparisons.',
|
|
717
|
+
parameters: {
|
|
718
|
+
type: 'object',
|
|
719
|
+
properties: {
|
|
720
|
+
text: {
|
|
721
|
+
type: 'string',
|
|
722
|
+
description: 'The text to generate embeddings for',
|
|
723
|
+
},
|
|
724
|
+
model: {
|
|
725
|
+
type: 'string',
|
|
726
|
+
description: 'Embedding model to use (default: nomic-embed-text)',
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
required: ['text'],
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
handler: async (args) => {
|
|
733
|
+
try {
|
|
734
|
+
const text = String(args.text);
|
|
735
|
+
const model = args.model ? String(args.model) : 'nomic-embed-text';
|
|
736
|
+
const ollamaUrl = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
737
|
+
const response = await fetch(`${ollamaUrl}/api/embeddings`, {
|
|
738
|
+
method: 'POST',
|
|
739
|
+
headers: { 'Content-Type': 'application/json' },
|
|
740
|
+
body: JSON.stringify({
|
|
741
|
+
model,
|
|
742
|
+
prompt: text,
|
|
743
|
+
}),
|
|
744
|
+
});
|
|
745
|
+
if (!response.ok) {
|
|
746
|
+
throw new Error(`Ollama API error: ${response.status}`);
|
|
747
|
+
}
|
|
748
|
+
const result = await response.json();
|
|
749
|
+
return {
|
|
750
|
+
status: 'success',
|
|
751
|
+
output: `Generated ${result.embedding.length} dimensional embedding`,
|
|
752
|
+
metadata: {
|
|
753
|
+
embedding: result.embedding,
|
|
754
|
+
dimensions: result.embedding.length,
|
|
755
|
+
model,
|
|
756
|
+
text_preview: text.slice(0, 100) + (text.length > 100 ? '...' : ''),
|
|
757
|
+
},
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
catch (err) {
|
|
761
|
+
return {
|
|
762
|
+
status: 'error',
|
|
763
|
+
error: err instanceof Error ? err.message : String(err),
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
};
|
|
768
|
+
// Web search/research tool - search the web using various search APIs
|
|
769
|
+
const webSearchTool = {
|
|
770
|
+
definition: {
|
|
771
|
+
name: 'web_search',
|
|
772
|
+
description: 'Search the web for information. Uses DuckDuckGo or other search providers.',
|
|
773
|
+
parameters: {
|
|
774
|
+
type: 'object',
|
|
775
|
+
properties: {
|
|
776
|
+
query: {
|
|
777
|
+
type: 'string',
|
|
778
|
+
description: 'The search query',
|
|
779
|
+
},
|
|
780
|
+
num_results: {
|
|
781
|
+
type: 'number',
|
|
782
|
+
description: 'Number of results to return (default: 5, max: 10)',
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
required: ['query'],
|
|
786
|
+
},
|
|
787
|
+
},
|
|
788
|
+
handler: async (args) => {
|
|
789
|
+
try {
|
|
790
|
+
const query = encodeURIComponent(String(args.query));
|
|
791
|
+
const numResults = Math.min(args.num_results ? Number(args.num_results) : 5, 10);
|
|
792
|
+
// Try DuckDuckGo HTML first
|
|
793
|
+
try {
|
|
794
|
+
const response = await fetch(`https://html.duckduckgo.com/html/?q=${query}`, {
|
|
795
|
+
headers: {
|
|
796
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
797
|
+
},
|
|
798
|
+
});
|
|
799
|
+
if (response.ok) {
|
|
800
|
+
const html = await response.text();
|
|
801
|
+
// Parse results from HTML
|
|
802
|
+
const results = [];
|
|
803
|
+
// Simple regex-based extraction
|
|
804
|
+
const resultBlocks = html.match(/<div class="result[^"]*">[\s\S]*?<\/div>/g) || [];
|
|
805
|
+
for (const block of resultBlocks.slice(0, numResults)) {
|
|
806
|
+
const titleMatch = block.match(/<a[^>]*class="result__a"[^>]*>([\s\S]*?)<\/a>/);
|
|
807
|
+
const urlMatch = block.match(/<a[^>]*href="([^"]*)"/);
|
|
808
|
+
const snippetMatch = block.match(/<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/);
|
|
809
|
+
if (titleMatch && urlMatch) {
|
|
810
|
+
results.push({
|
|
811
|
+
title: titleMatch[1].replace(/<[^>]*>/g, '').trim(),
|
|
812
|
+
url: urlMatch[1],
|
|
813
|
+
snippet: snippetMatch ? snippetMatch[1].replace(/<[^>]*>/g, '').trim() : '',
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (results.length > 0) {
|
|
818
|
+
const formatted = results.map((r, i) => `${i + 1}. ${r.title}\n URL: ${r.url}\n ${r.snippet}`).join('\n\n');
|
|
819
|
+
return {
|
|
820
|
+
status: 'success',
|
|
821
|
+
output: formatted,
|
|
822
|
+
metadata: { results_count: results.length, source: 'duckduckgo' },
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
// Fallback to alternative search or error
|
|
829
|
+
}
|
|
830
|
+
return {
|
|
831
|
+
status: 'error',
|
|
832
|
+
error: 'Web search failed. The search service may be unavailable or rate-limited.',
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
catch (err) {
|
|
836
|
+
return {
|
|
837
|
+
status: 'error',
|
|
838
|
+
error: err instanceof Error ? err.message : String(err),
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
// Browser automation tool - fetch and extract content from web pages
|
|
844
|
+
const browserTool = {
|
|
845
|
+
definition: {
|
|
846
|
+
name: 'browse_webpage',
|
|
847
|
+
description: 'Fetch a webpage and extract its main content, removing ads and navigation. Useful for reading articles.',
|
|
848
|
+
parameters: {
|
|
849
|
+
type: 'object',
|
|
850
|
+
properties: {
|
|
851
|
+
url: {
|
|
852
|
+
type: 'string',
|
|
853
|
+
description: 'The URL to browse',
|
|
854
|
+
},
|
|
855
|
+
extract_main_content: {
|
|
856
|
+
type: 'boolean',
|
|
857
|
+
description: 'Extract main content only (default: true)',
|
|
858
|
+
},
|
|
859
|
+
},
|
|
860
|
+
required: ['url'],
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
handler: async (args) => {
|
|
864
|
+
try {
|
|
865
|
+
const url = String(args.url);
|
|
866
|
+
const extractMain = args.extract_main_content !== false;
|
|
867
|
+
const response = await fetch(url, {
|
|
868
|
+
headers: {
|
|
869
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
870
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
871
|
+
'Accept-Language': 'en-US,en;q=0.5',
|
|
872
|
+
},
|
|
873
|
+
});
|
|
874
|
+
if (!response.ok) {
|
|
875
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
876
|
+
}
|
|
877
|
+
const html = await response.text();
|
|
878
|
+
if (!extractMain) {
|
|
879
|
+
// Return raw HTML (truncated)
|
|
880
|
+
const truncated = html.length > 10000 ? html.slice(0, 10000) + '\n... (truncated)' : html;
|
|
881
|
+
return {
|
|
882
|
+
status: 'success',
|
|
883
|
+
output: truncated,
|
|
884
|
+
metadata: { url, content_length: html.length, truncated: html.length > 10000 },
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
// Extract title
|
|
888
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
889
|
+
const title = titleMatch ? titleMatch[1].trim() : 'No title';
|
|
890
|
+
// Extract main content - remove scripts, styles, nav, etc.
|
|
891
|
+
let content = html
|
|
892
|
+
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
893
|
+
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
894
|
+
.replace(/<nav[^>]*>[\s\S]*?<\/nav>/gi, '')
|
|
895
|
+
.replace(/<header[^>]*>[\s\S]*?<\/header>/gi, '')
|
|
896
|
+
.replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, '')
|
|
897
|
+
.replace(/<aside[^>]*>[\s\S]*?<\/aside>/gi, '')
|
|
898
|
+
.replace(/<[^>]*>/g, ' ') // Remove remaining tags
|
|
899
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
900
|
+
.trim();
|
|
901
|
+
// Truncate if too long
|
|
902
|
+
const maxLength = 8000;
|
|
903
|
+
const truncated = content.length > maxLength;
|
|
904
|
+
if (truncated) {
|
|
905
|
+
content = content.slice(0, maxLength) + '\n... (content truncated)';
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
status: 'success',
|
|
909
|
+
output: `Title: ${title}\n\nContent:\n${content}`,
|
|
910
|
+
metadata: {
|
|
911
|
+
url,
|
|
912
|
+
title,
|
|
913
|
+
content_length: content.length,
|
|
914
|
+
truncated,
|
|
915
|
+
},
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
catch (err) {
|
|
919
|
+
return {
|
|
920
|
+
status: 'error',
|
|
921
|
+
error: err instanceof Error ? err.message : String(err),
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
};
|
|
926
|
+
// Code execution tool - run code in various languages
|
|
927
|
+
const codeExecutionTool = {
|
|
928
|
+
definition: {
|
|
929
|
+
name: 'execute_code',
|
|
930
|
+
description: 'Execute code in various programming languages (JavaScript, Python, Bash). Returns stdout, stderr, and exit code.',
|
|
931
|
+
parameters: {
|
|
932
|
+
type: 'object',
|
|
933
|
+
properties: {
|
|
934
|
+
code: {
|
|
935
|
+
type: 'string',
|
|
936
|
+
description: 'The code to execute',
|
|
937
|
+
},
|
|
938
|
+
language: {
|
|
939
|
+
type: 'string',
|
|
940
|
+
description: 'Programming language (javascript, python, bash/shell)',
|
|
941
|
+
enum: ['javascript', 'python', 'bash', 'shell'],
|
|
942
|
+
},
|
|
943
|
+
timeout_sec: {
|
|
944
|
+
type: 'number',
|
|
945
|
+
description: 'Timeout in seconds (default: 30)',
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
required: ['code', 'language'],
|
|
949
|
+
},
|
|
950
|
+
},
|
|
951
|
+
handler: async (args) => {
|
|
952
|
+
try {
|
|
953
|
+
const code = String(args.code);
|
|
954
|
+
const language = String(args.language).toLowerCase();
|
|
955
|
+
const timeoutSec = args.timeout_sec ? Number(args.timeout_sec) : 30;
|
|
956
|
+
let command;
|
|
957
|
+
let args_list;
|
|
958
|
+
switch (language) {
|
|
959
|
+
case 'javascript':
|
|
960
|
+
case 'js':
|
|
961
|
+
command = 'node';
|
|
962
|
+
args_list = ['-e', code];
|
|
963
|
+
break;
|
|
964
|
+
case 'python':
|
|
965
|
+
case 'py':
|
|
966
|
+
command = 'python3';
|
|
967
|
+
args_list = ['-c', code];
|
|
968
|
+
break;
|
|
969
|
+
case 'bash':
|
|
970
|
+
case 'shell':
|
|
971
|
+
case 'sh':
|
|
972
|
+
command = 'bash';
|
|
973
|
+
args_list = ['-c', code];
|
|
974
|
+
break;
|
|
975
|
+
default:
|
|
976
|
+
return {
|
|
977
|
+
status: 'error',
|
|
978
|
+
error: `Unsupported language: ${language}. Supported: javascript, python, bash`,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
return new Promise((resolve) => {
|
|
982
|
+
const child = spawn(command, args_list, {
|
|
983
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
984
|
+
});
|
|
985
|
+
let stdout = '';
|
|
986
|
+
let stderr = '';
|
|
987
|
+
let timeout = null;
|
|
988
|
+
if (timeoutSec > 0) {
|
|
989
|
+
timeout = setTimeout(() => {
|
|
990
|
+
child.kill('SIGTERM');
|
|
991
|
+
setTimeout(() => {
|
|
992
|
+
if (!child.killed)
|
|
993
|
+
child.kill('SIGKILL');
|
|
994
|
+
}, 5000);
|
|
995
|
+
}, timeoutSec * 1000);
|
|
996
|
+
}
|
|
997
|
+
child.stdout?.on('data', (data) => {
|
|
998
|
+
stdout += String(data);
|
|
999
|
+
});
|
|
1000
|
+
child.stderr?.on('data', (data) => {
|
|
1001
|
+
stderr += String(data);
|
|
1002
|
+
});
|
|
1003
|
+
child.on('close', (code) => {
|
|
1004
|
+
if (timeout)
|
|
1005
|
+
clearTimeout(timeout);
|
|
1006
|
+
const output = stdout || stderr || '(no output)';
|
|
1007
|
+
const isError = code !== 0;
|
|
1008
|
+
resolve({
|
|
1009
|
+
status: isError ? 'error' : 'success',
|
|
1010
|
+
output,
|
|
1011
|
+
error: isError ? `Exit code: ${code}${stderr ? '\n' + stderr : ''}` : undefined,
|
|
1012
|
+
metadata: {
|
|
1013
|
+
language,
|
|
1014
|
+
exit_code: code,
|
|
1015
|
+
stdout_length: stdout.length,
|
|
1016
|
+
stderr_length: stderr.length,
|
|
1017
|
+
},
|
|
1018
|
+
});
|
|
1019
|
+
});
|
|
1020
|
+
child.on('error', (err) => {
|
|
1021
|
+
if (timeout)
|
|
1022
|
+
clearTimeout(timeout);
|
|
1023
|
+
resolve({
|
|
1024
|
+
status: 'error',
|
|
1025
|
+
error: `Failed to execute: ${err.message}`,
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
catch (err) {
|
|
1031
|
+
return {
|
|
1032
|
+
status: 'error',
|
|
1033
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
},
|
|
1037
|
+
};
|
|
1038
|
+
// Semantic search tool - search within files using embeddings
|
|
1039
|
+
const semanticSearchTool = {
|
|
1040
|
+
definition: {
|
|
1041
|
+
name: 'semantic_search',
|
|
1042
|
+
description: 'Search for semantically similar content in files. Uses embeddings to find conceptually related content, not just keyword matches.',
|
|
1043
|
+
parameters: {
|
|
1044
|
+
type: 'object',
|
|
1045
|
+
properties: {
|
|
1046
|
+
query: {
|
|
1047
|
+
type: 'string',
|
|
1048
|
+
description: 'The semantic query (what concept you are looking for)',
|
|
1049
|
+
},
|
|
1050
|
+
directory: {
|
|
1051
|
+
type: 'string',
|
|
1052
|
+
description: 'Directory to search in',
|
|
1053
|
+
},
|
|
1054
|
+
file_pattern: {
|
|
1055
|
+
type: 'string',
|
|
1056
|
+
description: 'File pattern to limit search (e.g., "*.md", "*.txt")',
|
|
1057
|
+
},
|
|
1058
|
+
top_k: {
|
|
1059
|
+
type: 'number',
|
|
1060
|
+
description: 'Number of top results to return (default: 5)',
|
|
1061
|
+
},
|
|
1062
|
+
},
|
|
1063
|
+
required: ['query', 'directory'],
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
handler: async (args) => {
|
|
1067
|
+
try {
|
|
1068
|
+
const query = String(args.query);
|
|
1069
|
+
const directory = String(args.directory);
|
|
1070
|
+
const filePattern = args.file_pattern ? String(args.file_pattern) : '*';
|
|
1071
|
+
const topK = args.top_k ? Number(args.top_k) : 5;
|
|
1072
|
+
// For now, fallback to grep-based semantic-ish search
|
|
1073
|
+
// In a full implementation, this would use embeddings
|
|
1074
|
+
const keywords = query.toLowerCase().split(/\s+/).filter(w => w.length > 3);
|
|
1075
|
+
const pattern = keywords.join('|');
|
|
1076
|
+
if (!pattern) {
|
|
1077
|
+
return {
|
|
1078
|
+
status: 'error',
|
|
1079
|
+
error: 'Query too short for semantic search',
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
try {
|
|
1083
|
+
const command = `find "${directory}" -name "${filePattern}" -type f -exec grep -l -i -E "${pattern}" {} \\; 2>/dev/null | head -20`;
|
|
1084
|
+
const files = execSync(command, { encoding: 'utf8', timeout: 30000 }).trim().split('\n').filter(Boolean);
|
|
1085
|
+
if (files.length === 0) {
|
|
1086
|
+
return {
|
|
1087
|
+
status: 'success',
|
|
1088
|
+
output: 'No files found matching the semantic query.',
|
|
1089
|
+
metadata: { query, directory, matches: 0 },
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
// Get excerpts from each file
|
|
1093
|
+
const results = [];
|
|
1094
|
+
for (const file of files.slice(0, topK)) {
|
|
1095
|
+
try {
|
|
1096
|
+
const content = await fs.readFile(file, 'utf8');
|
|
1097
|
+
// Find relevant excerpt
|
|
1098
|
+
const lines = content.split('\n');
|
|
1099
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1100
|
+
const line = lines[i].toLowerCase();
|
|
1101
|
+
if (keywords.some(k => line.includes(k))) {
|
|
1102
|
+
const excerpt = lines.slice(Math.max(0, i - 2), i + 3).join('\n');
|
|
1103
|
+
results.push(`File: ${file}\n${excerpt}\n---`);
|
|
1104
|
+
break;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
catch {
|
|
1109
|
+
// Skip files that can't be read
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return {
|
|
1113
|
+
status: 'success',
|
|
1114
|
+
output: results.join('\n\n') || 'Found files but could not extract excerpts.',
|
|
1115
|
+
metadata: {
|
|
1116
|
+
query,
|
|
1117
|
+
directory,
|
|
1118
|
+
files_searched: files.length,
|
|
1119
|
+
results_returned: results.length,
|
|
1120
|
+
},
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
catch (err) {
|
|
1124
|
+
return {
|
|
1125
|
+
status: 'error',
|
|
1126
|
+
error: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
catch (err) {
|
|
1131
|
+
return {
|
|
1132
|
+
status: 'error',
|
|
1133
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
};
|
|
1138
|
+
// Git tools - various git operations
|
|
1139
|
+
const gitStatusTool = {
|
|
1140
|
+
definition: {
|
|
1141
|
+
name: 'git_status',
|
|
1142
|
+
description: 'Check git repository status. Shows modified, staged, and untracked files.',
|
|
1143
|
+
parameters: {
|
|
1144
|
+
type: 'object',
|
|
1145
|
+
properties: {
|
|
1146
|
+
repository_path: {
|
|
1147
|
+
type: 'string',
|
|
1148
|
+
description: 'Path to the git repository (default: current directory)',
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
required: [],
|
|
1152
|
+
},
|
|
1153
|
+
},
|
|
1154
|
+
handler: async (args) => {
|
|
1155
|
+
try {
|
|
1156
|
+
const repoPath = args.repository_path ? String(args.repository_path) : process.cwd();
|
|
1157
|
+
const result = execSync('git status -sb', {
|
|
1158
|
+
cwd: repoPath,
|
|
1159
|
+
encoding: 'utf8',
|
|
1160
|
+
timeout: 10000,
|
|
1161
|
+
});
|
|
1162
|
+
return {
|
|
1163
|
+
status: 'success',
|
|
1164
|
+
output: result.trim() || 'No changes',
|
|
1165
|
+
metadata: { repository: repoPath },
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
catch (err) {
|
|
1169
|
+
return {
|
|
1170
|
+
status: 'error',
|
|
1171
|
+
error: `Not a git repository or git not available: ${err instanceof Error ? err.message : String(err)}`,
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
},
|
|
1175
|
+
};
|
|
1176
|
+
const gitDiffTool = {
|
|
1177
|
+
definition: {
|
|
1178
|
+
name: 'git_diff',
|
|
1179
|
+
description: 'Show git diff for modified files. Can show staged or unstaged changes.',
|
|
1180
|
+
parameters: {
|
|
1181
|
+
type: 'object',
|
|
1182
|
+
properties: {
|
|
1183
|
+
repository_path: {
|
|
1184
|
+
type: 'string',
|
|
1185
|
+
description: 'Path to the git repository',
|
|
1186
|
+
},
|
|
1187
|
+
staged: {
|
|
1188
|
+
type: 'boolean',
|
|
1189
|
+
description: 'Show staged changes instead of unstaged',
|
|
1190
|
+
},
|
|
1191
|
+
file_path: {
|
|
1192
|
+
type: 'string',
|
|
1193
|
+
description: 'Show diff for specific file only',
|
|
1194
|
+
},
|
|
1195
|
+
},
|
|
1196
|
+
required: [],
|
|
1197
|
+
},
|
|
1198
|
+
},
|
|
1199
|
+
handler: async (args) => {
|
|
1200
|
+
try {
|
|
1201
|
+
const repoPath = args.repository_path ? String(args.repository_path) : process.cwd();
|
|
1202
|
+
const staged = Boolean(args.staged);
|
|
1203
|
+
const filePath = args.file_path ? String(args.file_path) : undefined;
|
|
1204
|
+
let command = 'git diff';
|
|
1205
|
+
if (staged)
|
|
1206
|
+
command += ' --cached';
|
|
1207
|
+
if (filePath)
|
|
1208
|
+
command += ` -- "${filePath}"`;
|
|
1209
|
+
const result = execSync(command, {
|
|
1210
|
+
cwd: repoPath,
|
|
1211
|
+
encoding: 'utf8',
|
|
1212
|
+
timeout: 10000,
|
|
1213
|
+
});
|
|
1214
|
+
return {
|
|
1215
|
+
status: 'success',
|
|
1216
|
+
output: result.trim() || 'No changes',
|
|
1217
|
+
metadata: { repository: repoPath, staged, file: filePath },
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
catch (err) {
|
|
1221
|
+
return {
|
|
1222
|
+
status: 'error',
|
|
1223
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
};
|
|
1228
|
+
const gitLogTool = {
|
|
1229
|
+
definition: {
|
|
1230
|
+
name: 'git_log',
|
|
1231
|
+
description: 'Show git commit history with customizable format.',
|
|
1232
|
+
parameters: {
|
|
1233
|
+
type: 'object',
|
|
1234
|
+
properties: {
|
|
1235
|
+
repository_path: {
|
|
1236
|
+
type: 'string',
|
|
1237
|
+
description: 'Path to the git repository',
|
|
1238
|
+
},
|
|
1239
|
+
num_commits: {
|
|
1240
|
+
type: 'number',
|
|
1241
|
+
description: 'Number of commits to show (default: 10)',
|
|
1242
|
+
},
|
|
1243
|
+
oneline: {
|
|
1244
|
+
type: 'boolean',
|
|
1245
|
+
description: 'Show one line per commit',
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
required: [],
|
|
1249
|
+
},
|
|
1250
|
+
},
|
|
1251
|
+
handler: async (args) => {
|
|
1252
|
+
try {
|
|
1253
|
+
const repoPath = args.repository_path ? String(args.repository_path) : process.cwd();
|
|
1254
|
+
const numCommits = args.num_commits ? Number(args.num_commits) : 10;
|
|
1255
|
+
const oneline = Boolean(args.oneline);
|
|
1256
|
+
let command = `git log -n ${numCommits}`;
|
|
1257
|
+
if (oneline)
|
|
1258
|
+
command += ' --oneline';
|
|
1259
|
+
const result = execSync(command, {
|
|
1260
|
+
cwd: repoPath,
|
|
1261
|
+
encoding: 'utf8',
|
|
1262
|
+
timeout: 10000,
|
|
1263
|
+
});
|
|
1264
|
+
return {
|
|
1265
|
+
status: 'success',
|
|
1266
|
+
output: result.trim(),
|
|
1267
|
+
metadata: { repository: repoPath, commits_shown: numCommits },
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
catch (err) {
|
|
1271
|
+
return {
|
|
1272
|
+
status: 'error',
|
|
1273
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
};
|
|
1278
|
+
// HTTP API tool - make HTTP requests
|
|
1279
|
+
const httpRequestTool = {
|
|
1280
|
+
definition: {
|
|
1281
|
+
name: 'http_request',
|
|
1282
|
+
description: 'Make HTTP requests (GET, POST, PUT, DELETE, PATCH). Supports JSON body and custom headers.',
|
|
1283
|
+
parameters: {
|
|
1284
|
+
type: 'object',
|
|
1285
|
+
properties: {
|
|
1286
|
+
url: {
|
|
1287
|
+
type: 'string',
|
|
1288
|
+
description: 'The URL to request',
|
|
1289
|
+
},
|
|
1290
|
+
method: {
|
|
1291
|
+
type: 'string',
|
|
1292
|
+
description: 'HTTP method',
|
|
1293
|
+
enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
|
|
1294
|
+
},
|
|
1295
|
+
headers: {
|
|
1296
|
+
type: 'object',
|
|
1297
|
+
description: 'Custom headers as key-value pairs',
|
|
1298
|
+
},
|
|
1299
|
+
body: {
|
|
1300
|
+
type: 'string',
|
|
1301
|
+
description: 'Request body (for POST, PUT, PATCH)',
|
|
1302
|
+
},
|
|
1303
|
+
body_json: {
|
|
1304
|
+
type: 'object',
|
|
1305
|
+
description: 'JSON body as object (will be serialized)',
|
|
1306
|
+
},
|
|
1307
|
+
},
|
|
1308
|
+
required: ['url'],
|
|
1309
|
+
},
|
|
1310
|
+
},
|
|
1311
|
+
handler: async (args) => {
|
|
1312
|
+
try {
|
|
1313
|
+
const url = String(args.url);
|
|
1314
|
+
const method = args.method ? String(args.method).toUpperCase() : 'GET';
|
|
1315
|
+
const headers = args.headers || {};
|
|
1316
|
+
let body;
|
|
1317
|
+
if (args.body_json) {
|
|
1318
|
+
body = JSON.stringify(args.body_json);
|
|
1319
|
+
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
|
1320
|
+
}
|
|
1321
|
+
else if (args.body) {
|
|
1322
|
+
body = String(args.body);
|
|
1323
|
+
}
|
|
1324
|
+
const response = await fetch(url, {
|
|
1325
|
+
method,
|
|
1326
|
+
headers,
|
|
1327
|
+
body: body && method !== 'GET' && method !== 'HEAD' ? body : undefined,
|
|
1328
|
+
});
|
|
1329
|
+
const responseBody = await response.text();
|
|
1330
|
+
const truncated = responseBody.length > 10000;
|
|
1331
|
+
const output = truncated ? responseBody.slice(0, 10000) + '\n... (truncated)' : responseBody;
|
|
1332
|
+
// Try to parse as JSON for metadata
|
|
1333
|
+
let jsonBody = null;
|
|
1334
|
+
try {
|
|
1335
|
+
jsonBody = JSON.parse(responseBody);
|
|
1336
|
+
}
|
|
1337
|
+
catch {
|
|
1338
|
+
// Not JSON
|
|
1339
|
+
}
|
|
1340
|
+
// Collect headers
|
|
1341
|
+
const headersObj = {};
|
|
1342
|
+
response.headers.forEach((value, key) => {
|
|
1343
|
+
headersObj[key] = value;
|
|
1344
|
+
});
|
|
1345
|
+
return {
|
|
1346
|
+
status: response.ok ? 'success' : 'error',
|
|
1347
|
+
output,
|
|
1348
|
+
error: response.ok ? undefined : `HTTP ${response.status}: ${response.statusText}`,
|
|
1349
|
+
metadata: {
|
|
1350
|
+
status: response.status,
|
|
1351
|
+
statusText: response.statusText,
|
|
1352
|
+
headers: headersObj,
|
|
1353
|
+
body_truncated: truncated,
|
|
1354
|
+
body_length: responseBody.length,
|
|
1355
|
+
json_parsed: jsonBody !== null,
|
|
1356
|
+
},
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
catch (err) {
|
|
1360
|
+
return {
|
|
1361
|
+
status: 'error',
|
|
1362
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
};
|
|
1367
|
+
// JSON/YAML tools - parsing and conversion
|
|
1368
|
+
const parseJsonTool = {
|
|
1369
|
+
definition: {
|
|
1370
|
+
name: 'parse_json',
|
|
1371
|
+
description: 'Parse and validate JSON. Returns formatted JSON or error details.',
|
|
1372
|
+
parameters: {
|
|
1373
|
+
type: 'object',
|
|
1374
|
+
properties: {
|
|
1375
|
+
text: {
|
|
1376
|
+
type: 'string',
|
|
1377
|
+
description: 'JSON text to parse',
|
|
1378
|
+
},
|
|
1379
|
+
file_path: {
|
|
1380
|
+
type: 'string',
|
|
1381
|
+
description: 'Path to JSON file (alternative to text)',
|
|
1382
|
+
},
|
|
1383
|
+
format_output: {
|
|
1384
|
+
type: 'boolean',
|
|
1385
|
+
description: 'Format output with indentation (default: true)',
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
required: [],
|
|
1389
|
+
},
|
|
1390
|
+
},
|
|
1391
|
+
handler: async (args) => {
|
|
1392
|
+
try {
|
|
1393
|
+
let text;
|
|
1394
|
+
if (args.file_path) {
|
|
1395
|
+
text = await fs.readFile(String(args.file_path), 'utf8');
|
|
1396
|
+
}
|
|
1397
|
+
else if (args.text) {
|
|
1398
|
+
text = String(args.text);
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
return {
|
|
1402
|
+
status: 'error',
|
|
1403
|
+
error: 'Either text or file_path must be provided',
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
const parsed = JSON.parse(text);
|
|
1407
|
+
const formatOutput = args.format_output !== false;
|
|
1408
|
+
const output = formatOutput ? JSON.stringify(parsed, null, 2) : JSON.stringify(parsed);
|
|
1409
|
+
// Get type info
|
|
1410
|
+
const type = Array.isArray(parsed) ? 'array' : typeof parsed;
|
|
1411
|
+
const keys = type === 'object' && parsed !== null ? Object.keys(parsed) : null;
|
|
1412
|
+
return {
|
|
1413
|
+
status: 'success',
|
|
1414
|
+
output,
|
|
1415
|
+
metadata: {
|
|
1416
|
+
type,
|
|
1417
|
+
keys: keys,
|
|
1418
|
+
length: Array.isArray(parsed) ? parsed.length : undefined,
|
|
1419
|
+
},
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
catch (err) {
|
|
1423
|
+
return {
|
|
1424
|
+
status: 'error',
|
|
1425
|
+
error: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
},
|
|
1429
|
+
};
|
|
1430
|
+
const jsonToYamlTool = {
|
|
1431
|
+
definition: {
|
|
1432
|
+
name: 'convert_json_yaml',
|
|
1433
|
+
description: 'Convert between JSON and YAML formats.',
|
|
1434
|
+
parameters: {
|
|
1435
|
+
type: 'object',
|
|
1436
|
+
properties: {
|
|
1437
|
+
input: {
|
|
1438
|
+
type: 'string',
|
|
1439
|
+
description: 'Input text (JSON or YAML)',
|
|
1440
|
+
},
|
|
1441
|
+
file_path: {
|
|
1442
|
+
type: 'string',
|
|
1443
|
+
description: 'Path to file (alternative to input)',
|
|
1444
|
+
},
|
|
1445
|
+
to: {
|
|
1446
|
+
type: 'string',
|
|
1447
|
+
description: 'Target format',
|
|
1448
|
+
enum: ['json', 'yaml'],
|
|
1449
|
+
},
|
|
1450
|
+
},
|
|
1451
|
+
required: ['to'],
|
|
1452
|
+
},
|
|
1453
|
+
},
|
|
1454
|
+
handler: async (args) => {
|
|
1455
|
+
try {
|
|
1456
|
+
const targetFormat = String(args.to).toLowerCase();
|
|
1457
|
+
let input;
|
|
1458
|
+
if (args.file_path) {
|
|
1459
|
+
input = await fs.readFile(String(args.file_path), 'utf8');
|
|
1460
|
+
}
|
|
1461
|
+
else if (args.input) {
|
|
1462
|
+
input = String(args.input);
|
|
1463
|
+
}
|
|
1464
|
+
else {
|
|
1465
|
+
return {
|
|
1466
|
+
status: 'error',
|
|
1467
|
+
error: 'Either input or file_path must be provided',
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
// Detect input format and parse
|
|
1471
|
+
let data;
|
|
1472
|
+
try {
|
|
1473
|
+
data = JSON.parse(input);
|
|
1474
|
+
}
|
|
1475
|
+
catch {
|
|
1476
|
+
// Try YAML-like parsing (simplified - just handles basic cases)
|
|
1477
|
+
// For full YAML support, would need js-yaml library
|
|
1478
|
+
const lines = input.split('\n');
|
|
1479
|
+
data = {};
|
|
1480
|
+
let currentKey = '';
|
|
1481
|
+
for (const line of lines) {
|
|
1482
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
1483
|
+
if (match) {
|
|
1484
|
+
currentKey = match[1];
|
|
1485
|
+
data[currentKey] = match[2] || '';
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
let output;
|
|
1490
|
+
if (targetFormat === 'json') {
|
|
1491
|
+
output = JSON.stringify(data, null, 2);
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
// Simple YAML serialization
|
|
1495
|
+
output = Object.entries(data)
|
|
1496
|
+
.map(([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`)
|
|
1497
|
+
.join('\n');
|
|
1498
|
+
}
|
|
1499
|
+
return {
|
|
1500
|
+
status: 'success',
|
|
1501
|
+
output,
|
|
1502
|
+
metadata: { source_format: targetFormat === 'json' ? 'yaml' : 'json', target_format: targetFormat },
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
catch (err) {
|
|
1506
|
+
return {
|
|
1507
|
+
status: 'error',
|
|
1508
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
},
|
|
1512
|
+
};
|
|
1513
|
+
// File diff tool - compare two files
|
|
1514
|
+
const diffFilesTool = {
|
|
1515
|
+
definition: {
|
|
1516
|
+
name: 'diff_files',
|
|
1517
|
+
description: 'Compare two files and show differences. Uses unified diff format.',
|
|
1518
|
+
parameters: {
|
|
1519
|
+
type: 'object',
|
|
1520
|
+
properties: {
|
|
1521
|
+
file1: {
|
|
1522
|
+
type: 'string',
|
|
1523
|
+
description: 'Path to first file',
|
|
1524
|
+
},
|
|
1525
|
+
file2: {
|
|
1526
|
+
type: 'string',
|
|
1527
|
+
description: 'Path to second file',
|
|
1528
|
+
},
|
|
1529
|
+
unified_lines: {
|
|
1530
|
+
type: 'number',
|
|
1531
|
+
description: 'Number of context lines (default: 3)',
|
|
1532
|
+
},
|
|
1533
|
+
},
|
|
1534
|
+
required: ['file1', 'file2'],
|
|
1535
|
+
},
|
|
1536
|
+
},
|
|
1537
|
+
handler: async (args) => {
|
|
1538
|
+
try {
|
|
1539
|
+
const file1 = String(args.file1);
|
|
1540
|
+
const file2 = String(args.file2);
|
|
1541
|
+
const context = args.unified_lines ? Number(args.unified_lines) : 3;
|
|
1542
|
+
const content1 = await fs.readFile(file1, 'utf8');
|
|
1543
|
+
const content2 = await fs.readFile(file2, 'utf8');
|
|
1544
|
+
// Simple line-by-line diff
|
|
1545
|
+
const lines1 = content1.split('\n');
|
|
1546
|
+
const lines2 = content2.split('\n');
|
|
1547
|
+
const diff = [];
|
|
1548
|
+
const maxLen = Math.max(lines1.length, lines2.length);
|
|
1549
|
+
for (let i = 0; i < maxLen; i++) {
|
|
1550
|
+
const line1 = lines1[i];
|
|
1551
|
+
const line2 = lines2[i];
|
|
1552
|
+
if (line1 !== line2) {
|
|
1553
|
+
if (line1 !== undefined)
|
|
1554
|
+
diff.push(`-${line1}`);
|
|
1555
|
+
if (line2 !== undefined)
|
|
1556
|
+
diff.push(`+${line2}`);
|
|
1557
|
+
}
|
|
1558
|
+
else {
|
|
1559
|
+
diff.push(` ${line1}`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
const output = diff.slice(0, 200).join('\n'); // Limit output
|
|
1563
|
+
const truncated = diff.length > 200;
|
|
1564
|
+
return {
|
|
1565
|
+
status: 'success',
|
|
1566
|
+
output: truncated ? output + '\n... (truncated)' : output,
|
|
1567
|
+
metadata: { file1, file2, differences: diff.length },
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
catch (err) {
|
|
1571
|
+
return {
|
|
1572
|
+
status: 'error',
|
|
1573
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
};
|
|
1578
|
+
// Compression tools - create/extract archives
|
|
1579
|
+
const createArchiveTool = {
|
|
1580
|
+
definition: {
|
|
1581
|
+
name: 'create_archive',
|
|
1582
|
+
description: 'Create a zip or tar archive from files or directories.',
|
|
1583
|
+
parameters: {
|
|
1584
|
+
type: 'object',
|
|
1585
|
+
properties: {
|
|
1586
|
+
output_path: {
|
|
1587
|
+
type: 'string',
|
|
1588
|
+
description: 'Path for the output archive',
|
|
1589
|
+
},
|
|
1590
|
+
source_paths: {
|
|
1591
|
+
type: 'array',
|
|
1592
|
+
description: 'Array of file or directory paths to include',
|
|
1593
|
+
items: { type: 'string' },
|
|
1594
|
+
},
|
|
1595
|
+
format: {
|
|
1596
|
+
type: 'string',
|
|
1597
|
+
description: 'Archive format',
|
|
1598
|
+
enum: ['zip', 'tar', 'tar.gz', 'tgz'],
|
|
1599
|
+
},
|
|
1600
|
+
},
|
|
1601
|
+
required: ['output_path', 'source_paths'],
|
|
1602
|
+
},
|
|
1603
|
+
},
|
|
1604
|
+
handler: async (args) => {
|
|
1605
|
+
try {
|
|
1606
|
+
const outputPath = String(args.output_path);
|
|
1607
|
+
const sourcePaths = Array.isArray(args.source_paths) ? args.source_paths.map(String) : [];
|
|
1608
|
+
const format = args.format ? String(args.format) : 'zip';
|
|
1609
|
+
let command;
|
|
1610
|
+
const sources = sourcePaths.map(p => `"${p}"`).join(' ');
|
|
1611
|
+
switch (format) {
|
|
1612
|
+
case 'zip':
|
|
1613
|
+
command = `zip -r "${outputPath}" ${sources}`;
|
|
1614
|
+
break;
|
|
1615
|
+
case 'tar':
|
|
1616
|
+
command = `tar -cf "${outputPath}" ${sources}`;
|
|
1617
|
+
break;
|
|
1618
|
+
case 'tar.gz':
|
|
1619
|
+
case 'tgz':
|
|
1620
|
+
command = `tar -czf "${outputPath}" ${sources}`;
|
|
1621
|
+
break;
|
|
1622
|
+
default:
|
|
1623
|
+
return {
|
|
1624
|
+
status: 'error',
|
|
1625
|
+
error: `Unsupported format: ${format}`,
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
execSync(command, { timeout: 60000 });
|
|
1629
|
+
const stats = await fs.stat(outputPath);
|
|
1630
|
+
return {
|
|
1631
|
+
status: 'success',
|
|
1632
|
+
output: `Archive created: ${outputPath} (${(stats.size / 1024).toFixed(2)} KB)`,
|
|
1633
|
+
metadata: { output_path: outputPath, format, files_included: sourcePaths.length, size_bytes: stats.size },
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
catch (err) {
|
|
1637
|
+
return {
|
|
1638
|
+
status: 'error',
|
|
1639
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
},
|
|
1643
|
+
};
|
|
1644
|
+
const extractArchiveTool = {
|
|
1645
|
+
definition: {
|
|
1646
|
+
name: 'extract_archive',
|
|
1647
|
+
description: 'Extract a zip or tar archive.',
|
|
1648
|
+
parameters: {
|
|
1649
|
+
type: 'object',
|
|
1650
|
+
properties: {
|
|
1651
|
+
archive_path: {
|
|
1652
|
+
type: 'string',
|
|
1653
|
+
description: 'Path to the archive file',
|
|
1654
|
+
},
|
|
1655
|
+
output_directory: {
|
|
1656
|
+
type: 'string',
|
|
1657
|
+
description: 'Directory to extract to (default: same as archive)',
|
|
1658
|
+
},
|
|
1659
|
+
},
|
|
1660
|
+
required: ['archive_path'],
|
|
1661
|
+
},
|
|
1662
|
+
},
|
|
1663
|
+
handler: async (args) => {
|
|
1664
|
+
try {
|
|
1665
|
+
const archivePath = String(args.archive_path);
|
|
1666
|
+
const outputDir = args.output_directory ? String(args.output_directory) : path.dirname(archivePath);
|
|
1667
|
+
// Detect format from extension
|
|
1668
|
+
const ext = path.extname(archivePath).toLowerCase();
|
|
1669
|
+
const baseName = path.basename(archivePath);
|
|
1670
|
+
let command;
|
|
1671
|
+
if (ext === '.zip') {
|
|
1672
|
+
command = `unzip -o "${archivePath}" -d "${outputDir}"`;
|
|
1673
|
+
}
|
|
1674
|
+
else if (ext === '.gz' || baseName.endsWith('.tar.gz') || baseName.endsWith('.tgz')) {
|
|
1675
|
+
command = `tar -xzf "${archivePath}" -C "${outputDir}"`;
|
|
1676
|
+
}
|
|
1677
|
+
else if (ext === '.tar') {
|
|
1678
|
+
command = `tar -xf "${archivePath}" -C "${outputDir}"`;
|
|
1679
|
+
}
|
|
1680
|
+
else {
|
|
1681
|
+
return {
|
|
1682
|
+
status: 'error',
|
|
1683
|
+
error: `Could not detect archive format from: ${archivePath}`,
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
execSync(command, { timeout: 60000 });
|
|
1687
|
+
return {
|
|
1688
|
+
status: 'success',
|
|
1689
|
+
output: `Archive extracted to: ${outputDir}`,
|
|
1690
|
+
metadata: { archive: archivePath, output_directory: outputDir },
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
catch (err) {
|
|
1694
|
+
return {
|
|
1695
|
+
status: 'error',
|
|
1696
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
},
|
|
1700
|
+
};
|
|
1701
|
+
// PDF text extraction tool
|
|
1702
|
+
const extractPdfTool = {
|
|
1703
|
+
definition: {
|
|
1704
|
+
name: 'extract_pdf_text',
|
|
1705
|
+
description: 'Extract text content from a PDF file. Requires pdftotext or similar tool.',
|
|
1706
|
+
parameters: {
|
|
1707
|
+
type: 'object',
|
|
1708
|
+
properties: {
|
|
1709
|
+
pdf_path: {
|
|
1710
|
+
type: 'string',
|
|
1711
|
+
description: 'Path to the PDF file',
|
|
1712
|
+
},
|
|
1713
|
+
page_range: {
|
|
1714
|
+
type: 'string',
|
|
1715
|
+
description: 'Page range to extract (e.g., "1-5" or "1,3,5", default: all)',
|
|
1716
|
+
},
|
|
1717
|
+
},
|
|
1718
|
+
required: ['pdf_path'],
|
|
1719
|
+
},
|
|
1720
|
+
},
|
|
1721
|
+
handler: async (args) => {
|
|
1722
|
+
try {
|
|
1723
|
+
const pdfPath = String(args.pdf_path);
|
|
1724
|
+
const pageRange = args.page_range ? String(args.page_range) : undefined;
|
|
1725
|
+
let command = `pdftotext -layout`;
|
|
1726
|
+
if (pageRange) {
|
|
1727
|
+
command += ` -f ${pageRange.split('-')[0]} -l ${pageRange.split('-')[1] || pageRange.split('-')[0]}`;
|
|
1728
|
+
}
|
|
1729
|
+
command += ` "${pdfPath}" -`; // Output to stdout
|
|
1730
|
+
const output = execSync(command, { encoding: 'utf8', timeout: 30000 });
|
|
1731
|
+
const truncated = output.length > 15000;
|
|
1732
|
+
return {
|
|
1733
|
+
status: 'success',
|
|
1734
|
+
output: truncated ? output.slice(0, 15000) + '\n... (truncated)' : output,
|
|
1735
|
+
metadata: { pdf_path: pdfPath, page_range: pageRange, text_length: output.length, truncated },
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
catch (err) {
|
|
1739
|
+
// Fallback: try Python with PyPDF2
|
|
1740
|
+
try {
|
|
1741
|
+
const pythonScript = `
|
|
1742
|
+
import sys
|
|
1743
|
+
from PyPDF2 import PdfReader
|
|
1744
|
+
reader = PdfReader("${args.pdf_path}")
|
|
1745
|
+
text = ""
|
|
1746
|
+
for page in reader.pages:
|
|
1747
|
+
text += page.extract_text() + "\\n"
|
|
1748
|
+
print(text)
|
|
1749
|
+
`;
|
|
1750
|
+
const output = execSync(`python3 -c "${pythonScript}"`, { encoding: 'utf8', timeout: 30000 });
|
|
1751
|
+
return {
|
|
1752
|
+
status: 'success',
|
|
1753
|
+
output,
|
|
1754
|
+
metadata: { pdf_path: args.pdf_path, method: 'pypdf2' },
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
catch {
|
|
1758
|
+
return {
|
|
1759
|
+
status: 'error',
|
|
1760
|
+
error: `Failed to extract PDF text. Ensure pdftotext (poppler-utils) or PyPDF2 is installed. Original error: ${err instanceof Error ? err.message : String(err)}`,
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
},
|
|
1765
|
+
};
|
|
1766
|
+
// Network tools - ping, port check, DNS
|
|
1767
|
+
const pingTool = {
|
|
1768
|
+
definition: {
|
|
1769
|
+
name: 'ping_host',
|
|
1770
|
+
description: 'Ping a host to check network connectivity.',
|
|
1771
|
+
parameters: {
|
|
1772
|
+
type: 'object',
|
|
1773
|
+
properties: {
|
|
1774
|
+
host: {
|
|
1775
|
+
type: 'string',
|
|
1776
|
+
description: 'Hostname or IP to ping',
|
|
1777
|
+
},
|
|
1778
|
+
count: {
|
|
1779
|
+
type: 'number',
|
|
1780
|
+
description: 'Number of pings (default: 4)',
|
|
1781
|
+
},
|
|
1782
|
+
},
|
|
1783
|
+
required: ['host'],
|
|
1784
|
+
},
|
|
1785
|
+
},
|
|
1786
|
+
handler: async (args) => {
|
|
1787
|
+
try {
|
|
1788
|
+
const host = String(args.host);
|
|
1789
|
+
const count = args.count ? Number(args.count) : 4;
|
|
1790
|
+
const output = execSync(`ping -c ${count} "${host}"`, {
|
|
1791
|
+
encoding: 'utf8',
|
|
1792
|
+
timeout: 30000,
|
|
1793
|
+
});
|
|
1794
|
+
return {
|
|
1795
|
+
status: 'success',
|
|
1796
|
+
output,
|
|
1797
|
+
metadata: { host, count },
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
catch (err) {
|
|
1801
|
+
// ping returns non-zero on some failures but still has output
|
|
1802
|
+
const output = err.stdout;
|
|
1803
|
+
if (output) {
|
|
1804
|
+
return {
|
|
1805
|
+
status: 'error',
|
|
1806
|
+
output,
|
|
1807
|
+
error: 'Host unreachable or ping failed',
|
|
1808
|
+
metadata: { host: args.host },
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
return {
|
|
1812
|
+
status: 'error',
|
|
1813
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
};
|
|
1818
|
+
const checkPortTool = {
|
|
1819
|
+
definition: {
|
|
1820
|
+
name: 'check_port',
|
|
1821
|
+
description: 'Check if a port is open on a host.',
|
|
1822
|
+
parameters: {
|
|
1823
|
+
type: 'object',
|
|
1824
|
+
properties: {
|
|
1825
|
+
host: {
|
|
1826
|
+
type: 'string',
|
|
1827
|
+
description: 'Hostname or IP',
|
|
1828
|
+
},
|
|
1829
|
+
port: {
|
|
1830
|
+
type: 'number',
|
|
1831
|
+
description: 'Port number to check',
|
|
1832
|
+
},
|
|
1833
|
+
timeout_sec: {
|
|
1834
|
+
type: 'number',
|
|
1835
|
+
description: 'Timeout in seconds (default: 5)',
|
|
1836
|
+
},
|
|
1837
|
+
},
|
|
1838
|
+
required: ['host', 'port'],
|
|
1839
|
+
},
|
|
1840
|
+
},
|
|
1841
|
+
handler: async (args) => {
|
|
1842
|
+
try {
|
|
1843
|
+
const host = String(args.host);
|
|
1844
|
+
const port = Number(args.port);
|
|
1845
|
+
const timeout = args.timeout_sec ? Number(args.timeout_sec) : 5;
|
|
1846
|
+
// Try using nc (netcat)
|
|
1847
|
+
try {
|
|
1848
|
+
execSync(`nc -z -w ${timeout} "${host}" ${port}`, { timeout: timeout * 1000 + 1000 });
|
|
1849
|
+
return {
|
|
1850
|
+
status: 'success',
|
|
1851
|
+
output: `Port ${port} on ${host} is OPEN`,
|
|
1852
|
+
metadata: { host, port, open: true },
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
catch {
|
|
1856
|
+
return {
|
|
1857
|
+
status: 'success',
|
|
1858
|
+
output: `Port ${port} on ${host} is CLOSED or filtered`,
|
|
1859
|
+
metadata: { host, port, open: false },
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
catch (err) {
|
|
1864
|
+
return {
|
|
1865
|
+
status: 'error',
|
|
1866
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
};
|
|
1871
|
+
// Docker tool - manage containers
|
|
1872
|
+
const dockerTool = {
|
|
1873
|
+
definition: {
|
|
1874
|
+
name: 'docker',
|
|
1875
|
+
description: 'Run Docker commands. Supports ps, images, logs, exec, run, build.',
|
|
1876
|
+
parameters: {
|
|
1877
|
+
type: 'object',
|
|
1878
|
+
properties: {
|
|
1879
|
+
command: {
|
|
1880
|
+
type: 'string',
|
|
1881
|
+
description: 'Docker command (ps, images, logs, exec, run, build, stop, rm)',
|
|
1882
|
+
enum: ['ps', 'images', 'logs', 'exec', 'run', 'build', 'stop', 'rm', 'compose-up', 'compose-down'],
|
|
1883
|
+
},
|
|
1884
|
+
container: {
|
|
1885
|
+
type: 'string',
|
|
1886
|
+
description: 'Container name or ID (for logs, exec, stop, rm)',
|
|
1887
|
+
},
|
|
1888
|
+
args: {
|
|
1889
|
+
type: 'string',
|
|
1890
|
+
description: 'Additional arguments',
|
|
1891
|
+
},
|
|
1892
|
+
},
|
|
1893
|
+
required: ['command'],
|
|
1894
|
+
},
|
|
1895
|
+
},
|
|
1896
|
+
handler: async (args) => {
|
|
1897
|
+
try {
|
|
1898
|
+
const command = String(args.command);
|
|
1899
|
+
const container = args.container ? String(args.container) : '';
|
|
1900
|
+
const extraArgs = args.args ? String(args.args) : '';
|
|
1901
|
+
let dockerCmd;
|
|
1902
|
+
switch (command) {
|
|
1903
|
+
case 'ps':
|
|
1904
|
+
dockerCmd = 'docker ps -a';
|
|
1905
|
+
break;
|
|
1906
|
+
case 'images':
|
|
1907
|
+
dockerCmd = 'docker images';
|
|
1908
|
+
break;
|
|
1909
|
+
case 'logs':
|
|
1910
|
+
if (!container)
|
|
1911
|
+
return { status: 'error', error: 'Container name required for logs' };
|
|
1912
|
+
dockerCmd = `docker logs --tail 100 ${container}`;
|
|
1913
|
+
break;
|
|
1914
|
+
case 'exec':
|
|
1915
|
+
if (!container)
|
|
1916
|
+
return { status: 'error', error: 'Container name required for exec' };
|
|
1917
|
+
dockerCmd = `docker exec ${container} ${extraArgs || 'sh'}`;
|
|
1918
|
+
break;
|
|
1919
|
+
case 'stop':
|
|
1920
|
+
if (!container)
|
|
1921
|
+
return { status: 'error', error: 'Container name required for stop' };
|
|
1922
|
+
dockerCmd = `docker stop ${container}`;
|
|
1923
|
+
break;
|
|
1924
|
+
case 'rm':
|
|
1925
|
+
if (!container)
|
|
1926
|
+
return { status: 'error', error: 'Container name required for rm' };
|
|
1927
|
+
dockerCmd = `docker rm ${container}`;
|
|
1928
|
+
break;
|
|
1929
|
+
case 'compose-up':
|
|
1930
|
+
dockerCmd = 'docker compose up -d';
|
|
1931
|
+
break;
|
|
1932
|
+
case 'compose-down':
|
|
1933
|
+
dockerCmd = 'docker compose down';
|
|
1934
|
+
break;
|
|
1935
|
+
default:
|
|
1936
|
+
dockerCmd = `docker ${command} ${extraArgs}`;
|
|
1937
|
+
}
|
|
1938
|
+
const output = execSync(dockerCmd, { encoding: 'utf8', timeout: 60000 });
|
|
1939
|
+
return {
|
|
1940
|
+
status: 'success',
|
|
1941
|
+
output: output.trim(),
|
|
1942
|
+
metadata: { command: dockerCmd },
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
catch (err) {
|
|
1946
|
+
return {
|
|
1947
|
+
status: 'error',
|
|
1948
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
},
|
|
1952
|
+
};
|
|
1953
|
+
// CSV processing tool
|
|
1954
|
+
const csvTool = {
|
|
1955
|
+
definition: {
|
|
1956
|
+
name: 'process_csv',
|
|
1957
|
+
description: 'Process CSV files - view, filter, convert to JSON, get statistics.',
|
|
1958
|
+
parameters: {
|
|
1959
|
+
type: 'object',
|
|
1960
|
+
properties: {
|
|
1961
|
+
file_path: {
|
|
1962
|
+
type: 'string',
|
|
1963
|
+
description: 'Path to CSV file',
|
|
1964
|
+
},
|
|
1965
|
+
action: {
|
|
1966
|
+
type: 'string',
|
|
1967
|
+
description: 'Action to perform',
|
|
1968
|
+
enum: ['view', 'head', 'stats', 'to_json'],
|
|
1969
|
+
},
|
|
1970
|
+
limit: {
|
|
1971
|
+
type: 'number',
|
|
1972
|
+
description: 'Number of rows to show (default: 10)',
|
|
1973
|
+
},
|
|
1974
|
+
},
|
|
1975
|
+
required: ['file_path', 'action'],
|
|
1976
|
+
},
|
|
1977
|
+
},
|
|
1978
|
+
handler: async (args) => {
|
|
1979
|
+
try {
|
|
1980
|
+
const filePath = String(args.file_path);
|
|
1981
|
+
const action = String(args.action);
|
|
1982
|
+
const limit = args.limit ? Number(args.limit) : 10;
|
|
1983
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
1984
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
1985
|
+
if (lines.length === 0) {
|
|
1986
|
+
return { status: 'error', error: 'Empty CSV file' };
|
|
1987
|
+
}
|
|
1988
|
+
const headers = lines[0].split(',').map(h => h.trim());
|
|
1989
|
+
switch (action) {
|
|
1990
|
+
case 'head':
|
|
1991
|
+
case 'view': {
|
|
1992
|
+
const rows = lines.slice(1, limit + 1).map(line => {
|
|
1993
|
+
const cells = line.split(',').map(c => c.trim());
|
|
1994
|
+
return cells.map((c, i) => `${headers[i]}: ${c}`).join(' | ');
|
|
1995
|
+
});
|
|
1996
|
+
return {
|
|
1997
|
+
status: 'success',
|
|
1998
|
+
output: `Headers: ${headers.join(', ')}\n\n${rows.join('\n')}`,
|
|
1999
|
+
metadata: { total_rows: lines.length - 1, shown: rows.length },
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
case 'stats': {
|
|
2003
|
+
const colStats = {};
|
|
2004
|
+
headers.forEach(h => colStats[h] = { count: 0, unique: new Set(), numeric: [] });
|
|
2005
|
+
for (const line of lines.slice(1)) {
|
|
2006
|
+
const cells = line.split(',').map(c => c.trim());
|
|
2007
|
+
cells.forEach((cell, i) => {
|
|
2008
|
+
const h = headers[i];
|
|
2009
|
+
colStats[h].count++;
|
|
2010
|
+
colStats[h].unique.add(cell);
|
|
2011
|
+
const num = parseFloat(cell);
|
|
2012
|
+
if (!isNaN(num))
|
|
2013
|
+
colStats[h].numeric.push(num);
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
const stats = headers.map(h => {
|
|
2017
|
+
const s = colStats[h];
|
|
2018
|
+
const numericInfo = s.numeric.length > 0
|
|
2019
|
+
? ` | min: ${Math.min(...s.numeric)} | max: ${Math.max(...s.numeric)} | avg: ${(s.numeric.reduce((a, b) => a + b, 0) / s.numeric.length).toFixed(2)}`
|
|
2020
|
+
: '';
|
|
2021
|
+
return `${h}: ${s.count} rows, ${s.unique.size} unique${numericInfo}`;
|
|
2022
|
+
});
|
|
2023
|
+
return {
|
|
2024
|
+
status: 'success',
|
|
2025
|
+
output: stats.join('\n'),
|
|
2026
|
+
metadata: { columns: headers.length, rows: lines.length - 1 },
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
case 'to_json': {
|
|
2030
|
+
const rows = lines.slice(1).map(line => {
|
|
2031
|
+
const cells = line.split(',').map(c => c.trim());
|
|
2032
|
+
const obj = {};
|
|
2033
|
+
headers.forEach((h, i) => obj[h] = cells[i] || '');
|
|
2034
|
+
return obj;
|
|
2035
|
+
});
|
|
2036
|
+
return {
|
|
2037
|
+
status: 'success',
|
|
2038
|
+
output: JSON.stringify(rows, null, 2),
|
|
2039
|
+
metadata: { rows: rows.length },
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
default:
|
|
2043
|
+
return { status: 'error', error: `Unknown action: ${action}` };
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
catch (err) {
|
|
2047
|
+
return {
|
|
2048
|
+
status: 'error',
|
|
2049
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
},
|
|
2053
|
+
};
|
|
2054
|
+
// SQLite database tool
|
|
2055
|
+
const sqliteTool = {
|
|
2056
|
+
definition: {
|
|
2057
|
+
name: 'query_sqlite',
|
|
2058
|
+
description: 'Execute SQL queries on SQLite databases.',
|
|
2059
|
+
parameters: {
|
|
2060
|
+
type: 'object',
|
|
2061
|
+
properties: {
|
|
2062
|
+
database_path: {
|
|
2063
|
+
type: 'string',
|
|
2064
|
+
description: 'Path to SQLite database file',
|
|
2065
|
+
},
|
|
2066
|
+
query: {
|
|
2067
|
+
type: 'string',
|
|
2068
|
+
description: 'SQL query to execute',
|
|
2069
|
+
},
|
|
2070
|
+
},
|
|
2071
|
+
required: ['database_path', 'query'],
|
|
2072
|
+
},
|
|
2073
|
+
},
|
|
2074
|
+
handler: async (args) => {
|
|
2075
|
+
try {
|
|
2076
|
+
const dbPath = String(args.database_path);
|
|
2077
|
+
const query = String(args.query);
|
|
2078
|
+
const output = execSync(`sqlite3 "${dbPath}" "${query.replace(/"/g, '""')}"`, {
|
|
2079
|
+
encoding: 'utf8',
|
|
2080
|
+
timeout: 30000,
|
|
2081
|
+
});
|
|
2082
|
+
return {
|
|
2083
|
+
status: 'success',
|
|
2084
|
+
output: output.trim(),
|
|
2085
|
+
metadata: { database: dbPath, query },
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
catch (err) {
|
|
2089
|
+
return {
|
|
2090
|
+
status: 'error',
|
|
2091
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
},
|
|
2095
|
+
};
|
|
2096
|
+
// Export all tools
|
|
2097
|
+
export const tools = [
|
|
2098
|
+
// Core file/system tools
|
|
2099
|
+
readFileTool,
|
|
2100
|
+
writeFileTool,
|
|
2101
|
+
editFileTool,
|
|
2102
|
+
listDirTool,
|
|
2103
|
+
grepSearchTool,
|
|
2104
|
+
runCommandTool,
|
|
2105
|
+
findByNameTool,
|
|
2106
|
+
viewUrlTool,
|
|
2107
|
+
askUserTool,
|
|
2108
|
+
createMemoryTool,
|
|
2109
|
+
analyzeImageTool,
|
|
2110
|
+
embeddingTool,
|
|
2111
|
+
webSearchTool,
|
|
2112
|
+
browserTool,
|
|
2113
|
+
codeExecutionTool,
|
|
2114
|
+
semanticSearchTool,
|
|
2115
|
+
gitStatusTool,
|
|
2116
|
+
gitDiffTool,
|
|
2117
|
+
gitLogTool,
|
|
2118
|
+
httpRequestTool,
|
|
2119
|
+
parseJsonTool,
|
|
2120
|
+
jsonToYamlTool,
|
|
2121
|
+
diffFilesTool,
|
|
2122
|
+
createArchiveTool,
|
|
2123
|
+
extractArchiveTool,
|
|
2124
|
+
extractPdfTool,
|
|
2125
|
+
pingTool,
|
|
2126
|
+
checkPortTool,
|
|
2127
|
+
dockerTool,
|
|
2128
|
+
csvTool,
|
|
2129
|
+
sqliteTool,
|
|
2130
|
+
// API integrations
|
|
2131
|
+
...slackTools,
|
|
2132
|
+
...googleTools,
|
|
2133
|
+
...githubTools,
|
|
2134
|
+
...discordTools,
|
|
2135
|
+
...telegramTools,
|
|
2136
|
+
];
|
|
2137
|
+
// Create a map for quick lookup
|
|
2138
|
+
export const toolMap = new Map(tools.map(t => [t.definition.name, t]));
|
|
2139
|
+
//# sourceMappingURL=index.js.map
|