@travisennis/acai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.acai/acai.json +9 -0
- package/.acai/prompts/add-openrouter-model.md +13 -0
- package/.acai/prompts/project-status.md +4 -0
- package/.acai/prompts/update-architecture-document.md +9 -0
- package/.acai/rules/learned-rules.md +9 -0
- package/.ai/docs/available-tools.txt +3 -0
- package/.ai/docs/cognitive_complexity_refactoring_progress.md +65 -0
- package/.ai/docs/deleted_tools.md +168 -0
- package/.ai/docs/deleted_tools_88ced9ef.md +56 -0
- package/.ai/docs/image-pasting.md +46 -0
- package/.ai/docs/initialize-app.md +117 -0
- package/.ai/docs/issue-4-plan.md +44 -0
- package/.ai/docs/marked-renderer-debug.md +15 -0
- package/.ai/docs/marked-renderer-refactor-plan.md +64 -0
- package/.ai/docs/memory-use-cases.md +55 -0
- package/.ai/docs/prompt-consistency.md +31 -0
- package/.ai/docs/refactoring-tools.md +98 -0
- package/.ai/docs/system-prompt-update.md +174 -0
- package/.ai/docs/system_prompt.txt +210 -0
- package/.ai/docs/tasks.md +49 -0
- package/.ai/plan.md +131 -0
- package/.ai/prompt.md +1 -0
- package/.ai/scripts/fetch_models.js +27 -0
- package/.ai/scripts/generateSystemPrompt.ts +15 -0
- package/.ai/scripts/list-tools.mjs +4 -0
- package/.ai/scripts/p5_geometric_shapes.js +149 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +3 -0
- package/.husky/pre-push +1 -0
- package/.ignore +4 -0
- package/AGENTS.md +25 -0
- package/ARCHITECTURE.md +304 -0
- package/LICENSE +21 -0
- package/README.md +392 -0
- package/TODO.md +2 -0
- package/biome.json +61 -0
- package/commitlint.config.js +3 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +116 -0
- package/dist/commands/application-log-command.d.ts +2 -0
- package/dist/commands/application-log-command.js +43 -0
- package/dist/commands/clear-command.d.ts +2 -0
- package/dist/commands/clear-command.js +12 -0
- package/dist/commands/compact-command.d.ts +2 -0
- package/dist/commands/compact-command.js +51 -0
- package/dist/commands/copy-command.d.ts +2 -0
- package/dist/commands/copy-command.js +51 -0
- package/dist/commands/edit-command.d.ts +2 -0
- package/dist/commands/edit-command.js +53 -0
- package/dist/commands/edit-prompt-command.d.ts +2 -0
- package/dist/commands/edit-prompt-command.js +25 -0
- package/dist/commands/exit-command.d.ts +2 -0
- package/dist/commands/exit-command.js +14 -0
- package/dist/commands/files-command.d.ts +2 -0
- package/dist/commands/files-command.js +63 -0
- package/dist/commands/generate-rules-command.d.ts +2 -0
- package/dist/commands/generate-rules-command.js +61 -0
- package/dist/commands/help-command.d.ts +2 -0
- package/dist/commands/help-command.js +19 -0
- package/dist/commands/init-command.d.ts +2 -0
- package/dist/commands/init-command.js +40 -0
- package/dist/commands/last-log-command.d.ts +2 -0
- package/dist/commands/last-log-command.js +76 -0
- package/dist/commands/manager.d.ts +22 -0
- package/dist/commands/manager.js +123 -0
- package/dist/commands/model-command.d.ts +2 -0
- package/dist/commands/model-command.js +84 -0
- package/dist/commands/paste-command.d.ts +2 -0
- package/dist/commands/paste-command.js +40 -0
- package/dist/commands/prompt-command.d.ts +2 -0
- package/dist/commands/prompt-command.js +111 -0
- package/dist/commands/reset-command.d.ts +2 -0
- package/dist/commands/reset-command.js +16 -0
- package/dist/commands/rules-command.d.ts +2 -0
- package/dist/commands/rules-command.js +68 -0
- package/dist/commands/save-command.d.ts +2 -0
- package/dist/commands/save-command.js +14 -0
- package/dist/commands/types.d.ts +26 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/usage-command.d.ts +2 -0
- package/dist/commands/usage-command.js +21 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.js +193 -0
- package/dist/conversation-analyzer.d.ts +10 -0
- package/dist/conversation-analyzer.js +88 -0
- package/dist/dedent.d.ts +3 -0
- package/dist/dedent.js +38 -0
- package/dist/formatting.d.ts +17 -0
- package/dist/formatting.js +103 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +213 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +24 -0
- package/dist/mentions.d.ts +9 -0
- package/dist/mentions.js +182 -0
- package/dist/messages.d.ts +69 -0
- package/dist/messages.js +261 -0
- package/dist/middleware/audit-message.d.ts +5 -0
- package/dist/middleware/audit-message.js +95 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/rate-limit.d.ts +4 -0
- package/dist/middleware/rate-limit.js +17 -0
- package/dist/models/ai-config.d.ts +12 -0
- package/dist/models/ai-config.js +87 -0
- package/dist/models/anthropic-provider.d.ts +25 -0
- package/dist/models/anthropic-provider.js +184 -0
- package/dist/models/deepseek-provider.d.ts +20 -0
- package/dist/models/deepseek-provider.js +42 -0
- package/dist/models/google-provider.d.ts +19 -0
- package/dist/models/google-provider.js +56 -0
- package/dist/models/manager.d.ts +15 -0
- package/dist/models/manager.js +48 -0
- package/dist/models/openai-provider.d.ts +22 -0
- package/dist/models/openai-provider.js +70 -0
- package/dist/models/openrouter-provider.d.ts +36 -0
- package/dist/models/openrouter-provider.js +276 -0
- package/dist/models/providers.d.ts +33 -0
- package/dist/models/providers.js +116 -0
- package/dist/models/xai-provider.d.ts +20 -0
- package/dist/models/xai-provider.js +47 -0
- package/dist/parsing.d.ts +2 -0
- package/dist/parsing.js +18 -0
- package/dist/prompts/manager.d.ts +19 -0
- package/dist/prompts/manager.js +71 -0
- package/dist/prompts.d.ts +4 -0
- package/dist/prompts.js +158 -0
- package/dist/repl-prompt.d.ts +14 -0
- package/dist/repl-prompt.js +147 -0
- package/dist/repl.d.ts +27 -0
- package/dist/repl.js +431 -0
- package/dist/source/cli.d.ts +19 -0
- package/dist/source/cli.js +116 -0
- package/dist/source/commands/application-log-command.d.ts +2 -0
- package/dist/source/commands/application-log-command.js +43 -0
- package/dist/source/commands/clear-command.d.ts +2 -0
- package/dist/source/commands/clear-command.js +12 -0
- package/dist/source/commands/compact-command.d.ts +2 -0
- package/dist/source/commands/compact-command.js +51 -0
- package/dist/source/commands/copy-command.d.ts +2 -0
- package/dist/source/commands/copy-command.js +51 -0
- package/dist/source/commands/edit-command.d.ts +2 -0
- package/dist/source/commands/edit-command.js +53 -0
- package/dist/source/commands/edit-prompt-command.d.ts +2 -0
- package/dist/source/commands/edit-prompt-command.js +25 -0
- package/dist/source/commands/exit-command.d.ts +2 -0
- package/dist/source/commands/exit-command.js +14 -0
- package/dist/source/commands/files-command.d.ts +2 -0
- package/dist/source/commands/files-command.js +63 -0
- package/dist/source/commands/generate-rules-command.d.ts +2 -0
- package/dist/source/commands/generate-rules-command.js +61 -0
- package/dist/source/commands/help-command.d.ts +2 -0
- package/dist/source/commands/help-command.js +19 -0
- package/dist/source/commands/init-command.d.ts +2 -0
- package/dist/source/commands/init-command.js +40 -0
- package/dist/source/commands/last-log-command.d.ts +2 -0
- package/dist/source/commands/last-log-command.js +76 -0
- package/dist/source/commands/manager.d.ts +22 -0
- package/dist/source/commands/manager.js +123 -0
- package/dist/source/commands/model-command.d.ts +2 -0
- package/dist/source/commands/model-command.js +84 -0
- package/dist/source/commands/paste-command.d.ts +2 -0
- package/dist/source/commands/paste-command.js +40 -0
- package/dist/source/commands/prompt-command.d.ts +2 -0
- package/dist/source/commands/prompt-command.js +111 -0
- package/dist/source/commands/reset-command.d.ts +2 -0
- package/dist/source/commands/reset-command.js +16 -0
- package/dist/source/commands/rules-command.d.ts +2 -0
- package/dist/source/commands/rules-command.js +68 -0
- package/dist/source/commands/save-command.d.ts +2 -0
- package/dist/source/commands/save-command.js +14 -0
- package/dist/source/commands/types.d.ts +26 -0
- package/dist/source/commands/types.js +1 -0
- package/dist/source/commands/usage-command.d.ts +2 -0
- package/dist/source/commands/usage-command.js +21 -0
- package/dist/source/config.d.ts +60 -0
- package/dist/source/config.js +193 -0
- package/dist/source/conversation-analyzer.d.ts +10 -0
- package/dist/source/conversation-analyzer.js +88 -0
- package/dist/source/dedent.d.ts +3 -0
- package/dist/source/dedent.js +38 -0
- package/dist/source/formatting.d.ts +17 -0
- package/dist/source/formatting.js +103 -0
- package/dist/source/index.d.ts +18 -0
- package/dist/source/index.js +213 -0
- package/dist/source/logger.d.ts +2 -0
- package/dist/source/logger.js +24 -0
- package/dist/source/mentions.d.ts +9 -0
- package/dist/source/mentions.js +182 -0
- package/dist/source/messages.d.ts +69 -0
- package/dist/source/messages.js +261 -0
- package/dist/source/middleware/audit-message.d.ts +5 -0
- package/dist/source/middleware/audit-message.js +95 -0
- package/dist/source/middleware/index.d.ts +2 -0
- package/dist/source/middleware/index.js +2 -0
- package/dist/source/middleware/rate-limit.d.ts +4 -0
- package/dist/source/middleware/rate-limit.js +17 -0
- package/dist/source/models/ai-config.d.ts +12 -0
- package/dist/source/models/ai-config.js +87 -0
- package/dist/source/models/anthropic-provider.d.ts +25 -0
- package/dist/source/models/anthropic-provider.js +184 -0
- package/dist/source/models/deepseek-provider.d.ts +20 -0
- package/dist/source/models/deepseek-provider.js +42 -0
- package/dist/source/models/google-provider.d.ts +19 -0
- package/dist/source/models/google-provider.js +56 -0
- package/dist/source/models/manager.d.ts +15 -0
- package/dist/source/models/manager.js +48 -0
- package/dist/source/models/openai-provider.d.ts +22 -0
- package/dist/source/models/openai-provider.js +70 -0
- package/dist/source/models/openrouter-provider.d.ts +36 -0
- package/dist/source/models/openrouter-provider.js +276 -0
- package/dist/source/models/providers.d.ts +33 -0
- package/dist/source/models/providers.js +116 -0
- package/dist/source/models/xai-provider.d.ts +20 -0
- package/dist/source/models/xai-provider.js +47 -0
- package/dist/source/parsing.d.ts +2 -0
- package/dist/source/parsing.js +18 -0
- package/dist/source/prompts/manager.d.ts +19 -0
- package/dist/source/prompts/manager.js +71 -0
- package/dist/source/prompts.d.ts +4 -0
- package/dist/source/prompts.js +158 -0
- package/dist/source/repl-prompt.d.ts +14 -0
- package/dist/source/repl-prompt.js +147 -0
- package/dist/source/repl.d.ts +27 -0
- package/dist/source/repl.js +431 -0
- package/dist/source/terminal/formatting.d.ts +37 -0
- package/dist/source/terminal/formatting.js +106 -0
- package/dist/source/terminal/index.d.ts +94 -0
- package/dist/source/terminal/index.js +420 -0
- package/dist/source/terminal/markdown-utils.d.ts +2 -0
- package/dist/source/terminal/markdown-utils.js +81 -0
- package/dist/source/terminal/markdown.d.ts +1 -0
- package/dist/source/terminal/markdown.js +111 -0
- package/dist/source/terminal/types.d.ts +71 -0
- package/dist/source/terminal/types.js +1 -0
- package/dist/source/terminal-output.d.ts +8 -0
- package/dist/source/terminal-output.js +213 -0
- package/dist/source/terminal-output.test.d.ts +8 -0
- package/dist/source/terminal-output.test.js +213 -0
- package/dist/source/token-tracker.d.ts +14 -0
- package/dist/source/token-tracker.js +53 -0
- package/dist/source/token-utils.d.ts +7 -0
- package/dist/source/token-utils.js +13 -0
- package/dist/source/tools/agent.d.ts +17 -0
- package/dist/source/tools/agent.js +87 -0
- package/dist/source/tools/bash.d.ts +19 -0
- package/dist/source/tools/bash.js +294 -0
- package/dist/source/tools/code-interpreter.d.ts +12 -0
- package/dist/source/tools/code-interpreter.js +131 -0
- package/dist/source/tools/command-validation.d.ts +8 -0
- package/dist/source/tools/command-validation.js +69 -0
- package/dist/source/tools/delete-file.d.ts +12 -0
- package/dist/source/tools/delete-file.js +56 -0
- package/dist/source/tools/directory-tree.d.ts +12 -0
- package/dist/source/tools/directory-tree.js +38 -0
- package/dist/source/tools/edit-file.d.ts +19 -0
- package/dist/source/tools/edit-file.js +107 -0
- package/dist/source/tools/filesystem-utils.d.ts +22 -0
- package/dist/source/tools/filesystem-utils.js +191 -0
- package/dist/source/tools/git-utils.d.ts +14 -0
- package/dist/source/tools/git-utils.js +64 -0
- package/dist/source/tools/grep.d.ts +17 -0
- package/dist/source/tools/grep.js +138 -0
- package/dist/source/tools/index.d.ts +161 -0
- package/dist/source/tools/index.js +209 -0
- package/dist/source/tools/memory-read.d.ts +13 -0
- package/dist/source/tools/memory-read.js +135 -0
- package/dist/source/tools/memory-write.d.ts +12 -0
- package/dist/source/tools/memory-write.js +83 -0
- package/dist/source/tools/move-file.d.ts +13 -0
- package/dist/source/tools/move-file.js +44 -0
- package/dist/source/tools/read-file.d.ts +17 -0
- package/dist/source/tools/read-file.js +86 -0
- package/dist/source/tools/read-multiple-files.d.ts +14 -0
- package/dist/source/tools/read-multiple-files.js +55 -0
- package/dist/source/tools/save-file.d.ts +17 -0
- package/dist/source/tools/save-file.js +98 -0
- package/dist/source/tools/think.d.ts +11 -0
- package/dist/source/tools/think.js +45 -0
- package/dist/source/tools/types.d.ts +29 -0
- package/dist/source/tools/types.js +14 -0
- package/dist/source/tools/web-fetch.d.ts +47 -0
- package/dist/source/tools/web-fetch.js +246 -0
- package/dist/source/tools/web-search.d.ts +13 -0
- package/dist/source/tools/web-search.js +80 -0
- package/dist/source/utils/process.d.ts +36 -0
- package/dist/source/utils/process.js +75 -0
- package/dist/source/version.d.ts +1 -0
- package/dist/source/version.js +21 -0
- package/dist/terminal/formatting.d.ts +37 -0
- package/dist/terminal/formatting.js +106 -0
- package/dist/terminal/index.d.ts +94 -0
- package/dist/terminal/index.js +420 -0
- package/dist/terminal/markdown-utils.d.ts +2 -0
- package/dist/terminal/markdown-utils.js +81 -0
- package/dist/terminal/markdown.d.ts +1 -0
- package/dist/terminal/markdown.js +111 -0
- package/dist/terminal/types.d.ts +71 -0
- package/dist/terminal/types.js +1 -0
- package/dist/terminal-output.d.ts +8 -0
- package/dist/terminal-output.js +213 -0
- package/dist/token-tracker.d.ts +14 -0
- package/dist/token-tracker.js +53 -0
- package/dist/token-utils.d.ts +7 -0
- package/dist/token-utils.js +13 -0
- package/dist/tools/agent.d.ts +17 -0
- package/dist/tools/agent.js +87 -0
- package/dist/tools/bash.d.ts +19 -0
- package/dist/tools/bash.js +294 -0
- package/dist/tools/code-interpreter.d.ts +12 -0
- package/dist/tools/code-interpreter.js +131 -0
- package/dist/tools/command-validation.d.ts +8 -0
- package/dist/tools/command-validation.js +69 -0
- package/dist/tools/delete-file.d.ts +12 -0
- package/dist/tools/delete-file.js +56 -0
- package/dist/tools/directory-tree.d.ts +12 -0
- package/dist/tools/directory-tree.js +38 -0
- package/dist/tools/edit-file.d.ts +19 -0
- package/dist/tools/edit-file.js +107 -0
- package/dist/tools/filesystem-utils.d.ts +22 -0
- package/dist/tools/filesystem-utils.js +191 -0
- package/dist/tools/git-utils.d.ts +14 -0
- package/dist/tools/git-utils.js +64 -0
- package/dist/tools/grep.d.ts +17 -0
- package/dist/tools/grep.js +138 -0
- package/dist/tools/index.d.ts +161 -0
- package/dist/tools/index.js +209 -0
- package/dist/tools/memory-read.d.ts +13 -0
- package/dist/tools/memory-read.js +135 -0
- package/dist/tools/memory-write.d.ts +12 -0
- package/dist/tools/memory-write.js +83 -0
- package/dist/tools/move-file.d.ts +13 -0
- package/dist/tools/move-file.js +44 -0
- package/dist/tools/read-file.d.ts +17 -0
- package/dist/tools/read-file.js +86 -0
- package/dist/tools/read-multiple-files.d.ts +14 -0
- package/dist/tools/read-multiple-files.js +55 -0
- package/dist/tools/save-file.d.ts +17 -0
- package/dist/tools/save-file.js +98 -0
- package/dist/tools/think.d.ts +11 -0
- package/dist/tools/think.js +45 -0
- package/dist/tools/types.d.ts +29 -0
- package/dist/tools/types.js +14 -0
- package/dist/tools/web-fetch.d.ts +47 -0
- package/dist/tools/web-fetch.js +246 -0
- package/dist/tools/web-search.d.ts +13 -0
- package/dist/tools/web-search.js +80 -0
- package/dist/utils/process.d.ts +36 -0
- package/dist/utils/process.js +75 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +21 -0
- package/knip.json +5 -0
- package/package.json +83 -0
- package/source/cli.ts +172 -0
- package/source/commands/application-log-command.ts +53 -0
- package/source/commands/clear-command.ts +14 -0
- package/source/commands/compact-command.ts +64 -0
- package/source/commands/copy-command.ts +55 -0
- package/source/commands/edit-command.ts +63 -0
- package/source/commands/edit-prompt-command.ts +31 -0
- package/source/commands/exit-command.ts +18 -0
- package/source/commands/files-command.ts +85 -0
- package/source/commands/generate-rules-command.ts +82 -0
- package/source/commands/help-command.ts +27 -0
- package/source/commands/init-command.ts +48 -0
- package/source/commands/last-log-command.ts +88 -0
- package/source/commands/manager.ts +151 -0
- package/source/commands/model-command.ts +123 -0
- package/source/commands/paste-command.ts +62 -0
- package/source/commands/prompt-command.ts +150 -0
- package/source/commands/reset-command.ts +22 -0
- package/source/commands/rules-command.ts +76 -0
- package/source/commands/save-command.ts +20 -0
- package/source/commands/types.ts +28 -0
- package/source/commands/usage-command.ts +26 -0
- package/source/config.ts +223 -0
- package/source/conversation-analyzer.ts +115 -0
- package/source/dedent.ts +53 -0
- package/source/formatting.ts +132 -0
- package/source/index.ts +240 -0
- package/source/logger.ts +29 -0
- package/source/mentions.ts +227 -0
- package/source/messages.ts +360 -0
- package/source/middleware/audit-message.ts +133 -0
- package/source/middleware/index.ts +2 -0
- package/source/middleware/rate-limit.ts +24 -0
- package/source/models/ai-config.ts +109 -0
- package/source/models/anthropic-provider.ts +199 -0
- package/source/models/deepseek-provider.ts +53 -0
- package/source/models/google-provider.ts +68 -0
- package/source/models/manager.ts +84 -0
- package/source/models/openai-provider.ts +81 -0
- package/source/models/openrouter-provider.ts +288 -0
- package/source/models/providers.ts +197 -0
- package/source/models/xai-provider.ts +59 -0
- package/source/parsing.ts +20 -0
- package/source/prompts/manager.ts +90 -0
- package/source/prompts.ts +172 -0
- package/source/repl-prompt.ts +196 -0
- package/source/repl.ts +572 -0
- package/source/terminal/formatting.ts +121 -0
- package/source/terminal/index.ts +518 -0
- package/source/terminal/markdown-utils.ts +89 -0
- package/source/terminal/markdown.ts +155 -0
- package/source/terminal/types.ts +84 -0
- package/source/terminal-output.test.ts +266 -0
- package/source/token-tracker.ts +78 -0
- package/source/token-utils.ts +17 -0
- package/source/tools/agent.ts +107 -0
- package/source/tools/bash.ts +367 -0
- package/source/tools/code-interpreter.ts +172 -0
- package/source/tools/command-validation.ts +81 -0
- package/source/tools/delete-file.ts +71 -0
- package/source/tools/directory-tree.ts +54 -0
- package/source/tools/edit-file.ts +155 -0
- package/source/tools/filesystem-utils.ts +265 -0
- package/source/tools/git-utils.ts +70 -0
- package/source/tools/grep.ts +184 -0
- package/source/tools/index.ts +278 -0
- package/source/tools/memory-read.ts +174 -0
- package/source/tools/memory-write.ts +105 -0
- package/source/tools/move-file.ts +59 -0
- package/source/tools/read-file.ts +129 -0
- package/source/tools/read-multiple-files.ts +80 -0
- package/source/tools/save-file.ts +147 -0
- package/source/tools/think.ts +51 -0
- package/source/tools/types.ts +58 -0
- package/source/tools/web-fetch.ts +327 -0
- package/source/tools/web-search.ts +101 -0
- package/source/utils/process.ts +121 -0
- package/source/version.ts +21 -0
- package/test/commands/copy-command.test.ts +69 -0
- package/test/config.test.ts +200 -0
- package/test/terminal/markdown-utils.test.ts +124 -0
- package/test/tools/bash-tool.test.ts +58 -0
- package/test/tools/code-interpreter.test.ts +91 -0
- package/test/tools/command-validation.test.ts +48 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { input, select } from "@inquirer/prompts";
|
|
2
|
+
import { tool } from "ai";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { applyFileEdits, joinWorkingDir, validatePath, } from "./filesystem-utils.js";
|
|
6
|
+
export const EditFileTool = {
|
|
7
|
+
name: "editFile",
|
|
8
|
+
};
|
|
9
|
+
export const createEditFileTool = async ({ workingDir, terminal, sendData, autoAcceptAll, }) => {
|
|
10
|
+
const allowedDirectory = workingDir;
|
|
11
|
+
let autoAcceptEdits = autoAcceptAll;
|
|
12
|
+
return {
|
|
13
|
+
[EditFileTool.name]: tool({
|
|
14
|
+
description: "Make line-based edits to a text file. Each edit replaces exact line sequences " +
|
|
15
|
+
"with new content. Returns a git-style diff showing the changes made. " +
|
|
16
|
+
"Only works within allowed directories.",
|
|
17
|
+
inputSchema: z.object({
|
|
18
|
+
path: z.string().describe("The path of the file to edit."),
|
|
19
|
+
edits: z.array(z.object({
|
|
20
|
+
oldText: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe("Text to search for - must match exactly and enough context must be provided to uniquely match the target text"),
|
|
23
|
+
newText: z.string().describe("Text to replace with"),
|
|
24
|
+
})),
|
|
25
|
+
}),
|
|
26
|
+
execute: async ({ path, edits }, { toolCallId }) => {
|
|
27
|
+
sendData?.({
|
|
28
|
+
id: toolCallId,
|
|
29
|
+
event: "tool-init",
|
|
30
|
+
data: `Editing file: ${chalk.cyan(path)}`,
|
|
31
|
+
});
|
|
32
|
+
try {
|
|
33
|
+
const validPath = await validatePath(joinWorkingDir(path, workingDir), allowedDirectory);
|
|
34
|
+
if (terminal) {
|
|
35
|
+
terminal.writeln(`\n${chalk.blue.bold("●")} Proposing file changes: ${chalk.cyan(path)}`);
|
|
36
|
+
terminal.lineBreak();
|
|
37
|
+
const result = await applyFileEdits(validPath, edits, true);
|
|
38
|
+
terminal.writeln(`The agent is proposing the following ${chalk.cyan(edits.length)} edits:`);
|
|
39
|
+
terminal.lineBreak();
|
|
40
|
+
terminal.display(result);
|
|
41
|
+
terminal.lineBreak();
|
|
42
|
+
let userChoice;
|
|
43
|
+
if (autoAcceptEdits) {
|
|
44
|
+
terminal.writeln(chalk.green("✓ Auto-accepting edits (all future edits will be accepted)"));
|
|
45
|
+
userChoice = "accept";
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
userChoice = await select({
|
|
49
|
+
message: "What would you like to do with these changes?",
|
|
50
|
+
choices: [
|
|
51
|
+
{ name: "Accept these changes", value: "accept" },
|
|
52
|
+
{
|
|
53
|
+
name: "Accept all future edits (including these)",
|
|
54
|
+
value: "accept-all",
|
|
55
|
+
},
|
|
56
|
+
{ name: "Reject these changes", value: "reject" },
|
|
57
|
+
],
|
|
58
|
+
default: "accept",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
terminal.lineBreak();
|
|
62
|
+
if (userChoice === "accept-all") {
|
|
63
|
+
autoAcceptEdits = true;
|
|
64
|
+
terminal.writeln(chalk.yellow("✓ Auto-accept mode enabled for all future edits"));
|
|
65
|
+
terminal.lineBreak();
|
|
66
|
+
}
|
|
67
|
+
if (userChoice === "accept" || userChoice === "accept-all") {
|
|
68
|
+
const finalEdits = await applyFileEdits(validPath, edits, false);
|
|
69
|
+
// Send completion message indicating success
|
|
70
|
+
sendData?.({
|
|
71
|
+
id: toolCallId,
|
|
72
|
+
event: "tool-completion",
|
|
73
|
+
data: "Edits accepted and applied successfully.",
|
|
74
|
+
});
|
|
75
|
+
return finalEdits;
|
|
76
|
+
}
|
|
77
|
+
const reason = await input({ message: "Feedback: " });
|
|
78
|
+
terminal.lineBreak();
|
|
79
|
+
// Send completion message indicating rejection
|
|
80
|
+
sendData?.({
|
|
81
|
+
id: toolCallId,
|
|
82
|
+
event: "tool-completion",
|
|
83
|
+
data: `Edits rejected by user. Reason: ${reason}`,
|
|
84
|
+
});
|
|
85
|
+
return `The user rejected these changes. Reason: ${reason}`;
|
|
86
|
+
}
|
|
87
|
+
const finalEdits = await applyFileEdits(validPath, edits, false);
|
|
88
|
+
// Send completion message indicating success
|
|
89
|
+
sendData?.({
|
|
90
|
+
id: toolCallId,
|
|
91
|
+
event: "tool-completion",
|
|
92
|
+
data: "Edits accepted and applied successfully.",
|
|
93
|
+
});
|
|
94
|
+
return finalEdits;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
sendData?.({
|
|
98
|
+
event: "tool-error",
|
|
99
|
+
id: toolCallId,
|
|
100
|
+
data: `Failed to edit file: ${error.message}`,
|
|
101
|
+
});
|
|
102
|
+
return `Failed to edit file: ${error.message}`;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
}),
|
|
106
|
+
};
|
|
107
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { TokenCounter } from "../token-utils.ts";
|
|
2
|
+
export declare function normalizePath(p: string): string;
|
|
3
|
+
export declare function joinWorkingDir(userPath: string, workingDir: string): string;
|
|
4
|
+
export declare function expandHome(filepath: string): string;
|
|
5
|
+
export declare function validatePath(requestedPath: string, allowedDirectory: string): Promise<string>;
|
|
6
|
+
export interface FileEdit {
|
|
7
|
+
oldText: string;
|
|
8
|
+
newText: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function applyFileEdits(filePath: string, edits: FileEdit[], dryRun?: boolean): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Generates a string representation of a directory tree starting from the given path.
|
|
13
|
+
* @param dirPath - The path of the directory to generate the tree for.
|
|
14
|
+
* @returns A Promise that resolves to a string representation of the directory tree.
|
|
15
|
+
*/
|
|
16
|
+
export declare function directoryTree(dirPath: string): Promise<string>;
|
|
17
|
+
export declare function readFileAndCountTokens(filePath: string, workingDir: string, allowedDirectory: string, tokenCounter: TokenCounter, maxTokens: number): Promise<{
|
|
18
|
+
path: string;
|
|
19
|
+
content: string | null;
|
|
20
|
+
tokenCount: number;
|
|
21
|
+
error: string | null;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createTwoFilesPatch } from "diff";
|
|
5
|
+
import ignore from "ignore";
|
|
6
|
+
// Normalize all paths consistently
|
|
7
|
+
export function normalizePath(p) {
|
|
8
|
+
return path.normalize(p);
|
|
9
|
+
}
|
|
10
|
+
// Handle path joining with working directory
|
|
11
|
+
export function joinWorkingDir(userPath, workingDir) {
|
|
12
|
+
if (path.isAbsolute(userPath)) {
|
|
13
|
+
return path.normalize(userPath);
|
|
14
|
+
}
|
|
15
|
+
return path.normalize(path.join(workingDir, userPath));
|
|
16
|
+
}
|
|
17
|
+
export function expandHome(filepath) {
|
|
18
|
+
if (filepath.startsWith("~/") || filepath === "~") {
|
|
19
|
+
return path.join(os.homedir(), filepath.slice(1));
|
|
20
|
+
}
|
|
21
|
+
return filepath;
|
|
22
|
+
}
|
|
23
|
+
// Security utilities
|
|
24
|
+
export async function validatePath(requestedPath, allowedDirectory) {
|
|
25
|
+
const expandedPath = expandHome(requestedPath);
|
|
26
|
+
const absolute = path.isAbsolute(expandedPath)
|
|
27
|
+
? path.resolve(expandedPath)
|
|
28
|
+
: path.resolve(process.cwd(), expandedPath);
|
|
29
|
+
const normalizedRequested = normalizePath(absolute);
|
|
30
|
+
// Check if path is within allowed directories
|
|
31
|
+
const isAllowed = normalizedRequested.startsWith(allowedDirectory);
|
|
32
|
+
if (!isAllowed) {
|
|
33
|
+
throw new Error(`Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectory}`);
|
|
34
|
+
}
|
|
35
|
+
// Handle symlinks by checking their real path
|
|
36
|
+
try {
|
|
37
|
+
const realPath = await fs.realpath(absolute);
|
|
38
|
+
const normalizedReal = normalizePath(realPath);
|
|
39
|
+
const isRealPathAllowed = normalizedReal.startsWith(allowedDirectory);
|
|
40
|
+
if (!isRealPathAllowed) {
|
|
41
|
+
throw new Error("Access denied - symlink target outside allowed directories");
|
|
42
|
+
}
|
|
43
|
+
return realPath;
|
|
44
|
+
}
|
|
45
|
+
catch (_error) {
|
|
46
|
+
// For new files that don't exist yet, verify parent directory
|
|
47
|
+
const parentDir = path.dirname(absolute);
|
|
48
|
+
try {
|
|
49
|
+
const realParentPath = await fs.realpath(parentDir);
|
|
50
|
+
const normalizedParent = normalizePath(realParentPath);
|
|
51
|
+
const isParentAllowed = normalizedParent.startsWith(allowedDirectory);
|
|
52
|
+
if (!isParentAllowed) {
|
|
53
|
+
throw new Error("Access denied - parent directory outside allowed directories");
|
|
54
|
+
}
|
|
55
|
+
return absolute;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// file editing and diffing utilities
|
|
63
|
+
function normalizeLineEndings(text) {
|
|
64
|
+
return text.replace(/\r\n/g, "\n");
|
|
65
|
+
}
|
|
66
|
+
function createUnifiedDiff(originalContent, newContent, filepath = "file") {
|
|
67
|
+
// Ensure consistent line endings for diff
|
|
68
|
+
const normalizedOriginal = normalizeLineEndings(originalContent);
|
|
69
|
+
const normalizedNew = normalizeLineEndings(newContent);
|
|
70
|
+
return createTwoFilesPatch(filepath, filepath, normalizedOriginal, normalizedNew, "original", "modified");
|
|
71
|
+
}
|
|
72
|
+
export async function applyFileEdits(filePath, edits, dryRun = false) {
|
|
73
|
+
// Read file content literally
|
|
74
|
+
const originalContent = await fs.readFile(filePath, "utf-8");
|
|
75
|
+
if (edits.find((edit) => edit.oldText.length === 0)) {
|
|
76
|
+
throw new Error("Invalid oldText in edit. The value of oldText must be at least one character");
|
|
77
|
+
}
|
|
78
|
+
// Apply edits sequentially
|
|
79
|
+
let modifiedContent = originalContent;
|
|
80
|
+
for (const edit of edits) {
|
|
81
|
+
const { oldText, newText } = edit; // Use literal oldText and newText
|
|
82
|
+
const normalizedContent = normalizeLineEndings(modifiedContent);
|
|
83
|
+
const normalizedOldText = normalizeLineEndings(oldText);
|
|
84
|
+
if (normalizedContent.includes(normalizedOldText)) {
|
|
85
|
+
modifiedContent = normalizedContent.replace(normalizedOldText, newText);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// If literal match is not found, throw an error.
|
|
89
|
+
// The previous complex fallback logic is removed to ensure literal matching.
|
|
90
|
+
throw new Error(`Could not find literal match for edit:\n${edit.oldText}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Create unified diff (createUnifiedDiff normalizes line endings internally for diffing)
|
|
94
|
+
const diff = createUnifiedDiff(originalContent, modifiedContent, filePath);
|
|
95
|
+
// Format diff with appropriate number of backticks
|
|
96
|
+
let numBackticks = 3;
|
|
97
|
+
while (diff.includes("`".repeat(numBackticks))) {
|
|
98
|
+
numBackticks++;
|
|
99
|
+
}
|
|
100
|
+
const formattedDiff = `${"`".repeat(numBackticks)}diff\n${diff}${"`".repeat(numBackticks)}\n\n`;
|
|
101
|
+
if (!dryRun) {
|
|
102
|
+
// Write the modified content (which has literal newlines from newText, and preserves original newlines not part of oldText/newText)
|
|
103
|
+
await fs.writeFile(filePath, modifiedContent, "utf-8");
|
|
104
|
+
}
|
|
105
|
+
return formattedDiff;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Generates the indentation string for a given level in the directory tree.
|
|
109
|
+
* @param level - The current level in the directory tree.
|
|
110
|
+
* @param isLast - Indicates if the current item is the last in its parent directory.
|
|
111
|
+
* @returns The indentation string for the current level.
|
|
112
|
+
*/
|
|
113
|
+
function getIndent(level, isLast) {
|
|
114
|
+
const indent = "│ ".repeat(level - 1);
|
|
115
|
+
return level === 0 ? "" : `${indent}${isLast ? "└── " : "├── "}`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Recursively generates a string representation of a directory tree.
|
|
119
|
+
* @param dirPath - The path of the directory to generate the tree for.
|
|
120
|
+
* @param level - The current level in the directory tree (default: 1).
|
|
121
|
+
* @returns A Promise that resolves to a string representation of the directory tree.
|
|
122
|
+
* @throws Will log an error if there's an issue reading the directory.
|
|
123
|
+
*/
|
|
124
|
+
async function generateDirectoryTree(dirPath, ig, level = 1) {
|
|
125
|
+
const name = path.basename(dirPath);
|
|
126
|
+
let output = `${getIndent(level, false)}${name}\n`;
|
|
127
|
+
const items = await fs.readdir(dirPath);
|
|
128
|
+
const filteredItems = ig.filter(items);
|
|
129
|
+
for (let i = 0; i < filteredItems.length; i++) {
|
|
130
|
+
const item = filteredItems[i] ?? "";
|
|
131
|
+
const itemPath = path.join(dirPath, item);
|
|
132
|
+
const isLast = i === items.length - 1;
|
|
133
|
+
const stats = await fs.stat(itemPath);
|
|
134
|
+
if (stats.isDirectory()) {
|
|
135
|
+
output += await generateDirectoryTree(itemPath, ig, level + 1);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
output += `${getIndent(level + 1, isLast)}${item}\n`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return output;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Generates a string representation of a directory tree starting from the given path.
|
|
145
|
+
* @param dirPath - The path of the directory to generate the tree for.
|
|
146
|
+
* @returns A Promise that resolves to a string representation of the directory tree.
|
|
147
|
+
*/
|
|
148
|
+
export async function directoryTree(dirPath) {
|
|
149
|
+
let ig;
|
|
150
|
+
try {
|
|
151
|
+
const ignoreFile = await fs.readFile(path.join(process.cwd(), ".gitignore"));
|
|
152
|
+
ig = ignore().add(ignoreFile.toString()).add(".git");
|
|
153
|
+
}
|
|
154
|
+
catch (_error) {
|
|
155
|
+
// If .gitignore doesn't exist, create basic ignore with just .git
|
|
156
|
+
ig = ignore().add(".git");
|
|
157
|
+
}
|
|
158
|
+
return (await generateDirectoryTree(dirPath, ig)).trim();
|
|
159
|
+
}
|
|
160
|
+
export async function readFileAndCountTokens(filePath, workingDir, allowedDirectory, tokenCounter, maxTokens) {
|
|
161
|
+
try {
|
|
162
|
+
const validPath = await validatePath(joinWorkingDir(filePath, workingDir), allowedDirectory);
|
|
163
|
+
const content = await fs.readFile(validPath, "utf-8");
|
|
164
|
+
let tokenCount = 0;
|
|
165
|
+
try {
|
|
166
|
+
tokenCount = tokenCounter.count(content);
|
|
167
|
+
}
|
|
168
|
+
catch (tokenError) {
|
|
169
|
+
console.error("Error calculating token count:", tokenError);
|
|
170
|
+
// Handle token calculation error if needed
|
|
171
|
+
}
|
|
172
|
+
const maxTokenMessage = `File content (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}). Use readFile with startLine/lineCount or grepFiles for targeted access.`;
|
|
173
|
+
const finalContent = tokenCount > maxTokens ? maxTokenMessage : content;
|
|
174
|
+
const actualTokenCount = tokenCount > maxTokens ? 0 : tokenCount; // Don't count tokens for skipped files
|
|
175
|
+
return {
|
|
176
|
+
path: filePath,
|
|
177
|
+
content: finalContent,
|
|
178
|
+
tokenCount: actualTokenCount,
|
|
179
|
+
error: null,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
184
|
+
return {
|
|
185
|
+
path: filePath,
|
|
186
|
+
content: null,
|
|
187
|
+
tokenCount: 0,
|
|
188
|
+
error: errorMessage,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function getDiffStat(): Promise<{
|
|
2
|
+
filesChanged: number;
|
|
3
|
+
insertions: number;
|
|
4
|
+
deletions: number;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const inGitDirectory: () => Promise<boolean>;
|
|
7
|
+
/**
|
|
8
|
+
* Check if there are uncommitted changes
|
|
9
|
+
*/
|
|
10
|
+
export declare function hasUncommittedChanges(): Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Get the current git branch name
|
|
13
|
+
*/
|
|
14
|
+
export declare function getCurrentBranch(): Promise<string | null>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { memoize } from "@travisennis/stdlib/functional";
|
|
2
|
+
import { simpleGit } from "simple-git";
|
|
3
|
+
import { executeCommand } from "../utils/process.js";
|
|
4
|
+
// Function to get diff stats
|
|
5
|
+
export async function getDiffStat() {
|
|
6
|
+
const git = simpleGit(process.cwd());
|
|
7
|
+
try {
|
|
8
|
+
// Get diff stat comparing working directory to HEAD
|
|
9
|
+
const diffSummary = await git.diffSummary(["--stat"]);
|
|
10
|
+
return {
|
|
11
|
+
filesChanged: diffSummary.files.length,
|
|
12
|
+
insertions: diffSummary.insertions,
|
|
13
|
+
deletions: diffSummary.deletions,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
// Handle cases where git diff fails (e.g., initial commit)
|
|
18
|
+
console.error("Error getting git diff stat:", error);
|
|
19
|
+
return {
|
|
20
|
+
filesChanged: 0,
|
|
21
|
+
insertions: 0,
|
|
22
|
+
deletions: 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export const inGitDirectory = memoize(async () => {
|
|
27
|
+
const { code } = await executeCommand(["git", "rev-parse", "--is-inside-work-tree"], {
|
|
28
|
+
cwd: process.cwd(),
|
|
29
|
+
throwOnError: false,
|
|
30
|
+
});
|
|
31
|
+
return code === 0;
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Check if there are uncommitted changes
|
|
35
|
+
*/
|
|
36
|
+
export async function hasUncommittedChanges() {
|
|
37
|
+
if (!(await inGitDirectory())) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const git = simpleGit(process.cwd());
|
|
41
|
+
try {
|
|
42
|
+
const status = await git.status();
|
|
43
|
+
return status.files.length > 0;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the current git branch name
|
|
51
|
+
*/
|
|
52
|
+
export async function getCurrentBranch() {
|
|
53
|
+
if (!(await inGitDirectory())) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const git = simpleGit(process.cwd());
|
|
57
|
+
try {
|
|
58
|
+
const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
59
|
+
return branch.trim();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SendData } from "./types.ts";
|
|
2
|
+
export declare const GrepTool: {
|
|
3
|
+
name: "grepFiles";
|
|
4
|
+
};
|
|
5
|
+
export declare const createGrepTool: (options?: {
|
|
6
|
+
sendData?: SendData | undefined;
|
|
7
|
+
}) => {
|
|
8
|
+
grepFiles: import("ai").Tool<{
|
|
9
|
+
path: string;
|
|
10
|
+
pattern: string;
|
|
11
|
+
recursive: boolean | null;
|
|
12
|
+
ignoreCase: boolean | null;
|
|
13
|
+
filePattern: string | null;
|
|
14
|
+
contextLines: number | null;
|
|
15
|
+
searchIgnored: boolean | null;
|
|
16
|
+
}, string>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { inspect } from "node:util";
|
|
3
|
+
import { tool } from "ai";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export const GrepTool = {
|
|
7
|
+
name: "grepFiles",
|
|
8
|
+
};
|
|
9
|
+
export const createGrepTool = (options = {}) => {
|
|
10
|
+
const { sendData } = options;
|
|
11
|
+
return {
|
|
12
|
+
[GrepTool.name]: tool({
|
|
13
|
+
description: "Search files for patterns using ripgrep",
|
|
14
|
+
inputSchema: z.object({
|
|
15
|
+
pattern: z.string().describe("The regex pattern to search for"),
|
|
16
|
+
path: z.string().describe("The path to search in"),
|
|
17
|
+
recursive: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.nullable()
|
|
20
|
+
.describe("Pass null to use the default (true, search recursively)."),
|
|
21
|
+
ignoreCase: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.nullable()
|
|
24
|
+
.describe("Pass null to use the default (false, case-sensitive search)."),
|
|
25
|
+
filePattern: z
|
|
26
|
+
.string()
|
|
27
|
+
.nullable()
|
|
28
|
+
.describe("Pass null if no file pattern filter is needed."),
|
|
29
|
+
contextLines: z
|
|
30
|
+
.number()
|
|
31
|
+
.nullable()
|
|
32
|
+
.describe("Pass null if no context lines are needed."),
|
|
33
|
+
searchIgnored: z
|
|
34
|
+
.boolean()
|
|
35
|
+
.nullable()
|
|
36
|
+
.describe("Pass null to use the default (false, don't search ignored files)."),
|
|
37
|
+
}),
|
|
38
|
+
execute: ({ pattern, path, recursive, ignoreCase, filePattern, contextLines, searchIgnored, }, { toolCallId }) => {
|
|
39
|
+
try {
|
|
40
|
+
sendData?.({
|
|
41
|
+
event: "tool-init",
|
|
42
|
+
id: toolCallId,
|
|
43
|
+
data: `Searching codebase for "${chalk.cyan(inspect(pattern))}" in ${chalk.cyan(path)}`,
|
|
44
|
+
});
|
|
45
|
+
const result = grepFiles(pattern, path, {
|
|
46
|
+
recursive,
|
|
47
|
+
ignoreCase,
|
|
48
|
+
filePattern,
|
|
49
|
+
contextLines,
|
|
50
|
+
searchIgnored,
|
|
51
|
+
});
|
|
52
|
+
const matchCount = result === "No matches found."
|
|
53
|
+
? 0
|
|
54
|
+
: result
|
|
55
|
+
.trim()
|
|
56
|
+
.split("\n")
|
|
57
|
+
.filter((line) => {
|
|
58
|
+
if (line === "--") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// A match line from ripgrep with --line-number has the format:
|
|
62
|
+
// path:linenumber:match
|
|
63
|
+
// A context line has the format:
|
|
64
|
+
// path-linenumber-match
|
|
65
|
+
// This regex distinguishes between them by looking for the colon after the line number.
|
|
66
|
+
return /^(.+?):(\d+):(.*)$/.test(line);
|
|
67
|
+
}).length;
|
|
68
|
+
sendData?.({
|
|
69
|
+
event: "tool-completion",
|
|
70
|
+
id: toolCallId,
|
|
71
|
+
data: `Found ${chalk.cyan(matchCount)} matches.`,
|
|
72
|
+
});
|
|
73
|
+
return Promise.resolve(result);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
sendData?.({
|
|
77
|
+
event: "tool-error",
|
|
78
|
+
id: toolCallId,
|
|
79
|
+
data: `Error searching for "${pattern}" in ${path}`,
|
|
80
|
+
});
|
|
81
|
+
return Promise.resolve(error.message);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Search files for patterns using ripgrep
|
|
89
|
+
*
|
|
90
|
+
* @param pattern - The regex pattern to search for
|
|
91
|
+
* @param path - The path to search in
|
|
92
|
+
* @param options - Additional options for the grep command
|
|
93
|
+
* @returns The result of the grep command
|
|
94
|
+
*/
|
|
95
|
+
function grepFiles(pattern, path, options = {}) {
|
|
96
|
+
try {
|
|
97
|
+
// Handle null values by providing defaults
|
|
98
|
+
const effectiveRecursive = options.recursive === null ? true : options.recursive;
|
|
99
|
+
const effectiveIgnoreCase = options.ignoreCase === null ? false : options.ignoreCase;
|
|
100
|
+
const effectiveSearchIgnored = options.searchIgnored === null ? false : options.searchIgnored;
|
|
101
|
+
const effectiveFilePattern = options.filePattern;
|
|
102
|
+
const effectiveContextLines = options.contextLines;
|
|
103
|
+
// Build the ripgrep command
|
|
104
|
+
let command = "rg --line-number";
|
|
105
|
+
// Ripgrep is recursive by default, so we only need to add
|
|
106
|
+
// --no-recursive if effectiveRecursive is explicitly false
|
|
107
|
+
if (effectiveRecursive === false) {
|
|
108
|
+
command += " --max-depth=0";
|
|
109
|
+
}
|
|
110
|
+
if (effectiveIgnoreCase) {
|
|
111
|
+
command += " --ignore-case";
|
|
112
|
+
}
|
|
113
|
+
if (effectiveContextLines !== null && effectiveContextLines !== undefined) {
|
|
114
|
+
command += ` --context=${effectiveContextLines}`;
|
|
115
|
+
}
|
|
116
|
+
// Add pattern (escaped for shell)
|
|
117
|
+
command += ` ${JSON.stringify(pattern)}`;
|
|
118
|
+
// Add path
|
|
119
|
+
command += ` ${path}`;
|
|
120
|
+
// Add file pattern if specified
|
|
121
|
+
if (effectiveFilePattern !== null && effectiveFilePattern !== undefined) {
|
|
122
|
+
command += ` --glob=${JSON.stringify(effectiveFilePattern)}`;
|
|
123
|
+
}
|
|
124
|
+
if (effectiveSearchIgnored) {
|
|
125
|
+
command += " --no-ignore";
|
|
126
|
+
}
|
|
127
|
+
// Execute the command
|
|
128
|
+
const result = execSync(command, { encoding: "utf-8" });
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof Error && "status" in error && error.status === 1) {
|
|
133
|
+
// Status 1 in ripgrep just means "no matches found"
|
|
134
|
+
return "No matches found.";
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`Error executing ripgrep: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|