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