@ebowwa/coder 0.2.1 → 0.7.64
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/README.md +31 -32
- package/dist/core/__tests__/permissions.test.d.ts +12 -0
- package/dist/core/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/core/__tests__/permissions.test.js +851 -0
- package/dist/core/agent-loop/__tests__/compaction.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/compaction.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/compaction.test.js +209 -0
- package/dist/core/agent-loop/__tests__/formatters.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/formatters.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/formatters.test.js +195 -0
- package/dist/core/agent-loop/__tests__/index.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/index.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/index.test.js +121 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.js +340 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.js +178 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.js +331 -0
- package/dist/core/agent-loop/compaction.d.ts +39 -0
- package/dist/core/agent-loop/compaction.d.ts.map +1 -0
- package/dist/core/agent-loop/compaction.js +51 -0
- package/dist/core/agent-loop/formatters.d.ts +21 -0
- package/dist/core/agent-loop/formatters.d.ts.map +1 -0
- package/dist/core/agent-loop/formatters.js +42 -0
- package/dist/core/agent-loop/index.d.ts +25 -0
- package/dist/core/agent-loop/index.d.ts.map +1 -0
- package/dist/core/agent-loop/index.js +83 -0
- package/dist/core/agent-loop/loop-state.d.ts +74 -0
- package/dist/core/agent-loop/loop-state.d.ts.map +1 -0
- package/dist/core/agent-loop/loop-state.js +147 -0
- package/dist/core/agent-loop/message-builder.d.ts +13 -0
- package/dist/core/agent-loop/message-builder.d.ts.map +1 -0
- package/dist/core/agent-loop/message-builder.js +49 -0
- package/dist/core/agent-loop/tool-executor.d.ts +23 -0
- package/dist/core/agent-loop/tool-executor.d.ts.map +1 -0
- package/dist/core/agent-loop/tool-executor.js +152 -0
- package/dist/core/agent-loop/turn-executor.d.ts +57 -0
- package/dist/core/agent-loop/turn-executor.d.ts.map +1 -0
- package/dist/core/agent-loop/turn-executor.js +124 -0
- package/dist/core/agent-loop/types.d.ts +141 -0
- package/dist/core/agent-loop/types.d.ts.map +1 -0
- package/dist/core/agent-loop/types.js +4 -0
- package/dist/core/agent-loop.d.ts +17 -0
- package/dist/core/agent-loop.d.ts.map +1 -0
- package/dist/core/agent-loop.js +16 -0
- package/dist/core/api-client-impl.d.ts +62 -0
- package/dist/core/api-client-impl.d.ts.map +1 -0
- package/dist/core/api-client-impl.js +479 -0
- package/dist/core/api-client.d.ts +6 -0
- package/dist/core/api-client.d.ts.map +1 -0
- package/dist/core/api-client.js +5 -0
- package/dist/core/checkpoints.d.ts +128 -0
- package/dist/core/checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints.js +438 -0
- package/dist/core/claude-md.d.ts +71 -0
- package/dist/core/claude-md.d.ts.map +1 -0
- package/dist/core/claude-md.js +198 -0
- package/dist/core/cognitive-security/hooks.d.ts +138 -0
- package/dist/core/cognitive-security/hooks.d.ts.map +1 -0
- package/dist/core/cognitive-security/hooks.js +389 -0
- package/dist/core/cognitive-security/index.d.ts +751 -0
- package/dist/core/cognitive-security/index.d.ts.map +1 -0
- package/dist/core/cognitive-security/index.js +1123 -0
- package/dist/core/cognitive-security/middleware.d.ts +136 -0
- package/dist/core/cognitive-security/middleware.d.ts.map +1 -0
- package/dist/core/cognitive-security/middleware.js +376 -0
- package/dist/core/config-loader.d.ts +127 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +219 -0
- package/dist/core/context-compaction.d.ts +87 -0
- package/dist/core/context-compaction.d.ts.map +1 -0
- package/dist/core/context-compaction.js +428 -0
- package/dist/core/git-status.d.ts +25 -0
- package/dist/core/git-status.d.ts.map +1 -0
- package/dist/core/git-status.js +204 -0
- package/dist/core/image.d.ts +69 -0
- package/dist/core/image.d.ts.map +1 -0
- package/dist/core/image.js +290 -0
- package/dist/core/image.test.d.ts +2 -0
- package/dist/core/image.test.d.ts.map +1 -0
- package/dist/core/image.test.js +149 -0
- package/dist/core/models.d.ts +123 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +325 -0
- package/dist/core/permissions.d.ts +81 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +327 -0
- package/dist/core/retry.d.ts +25 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +121 -0
- package/dist/core/session-store.d.ts +9 -0
- package/dist/core/session-store.d.ts.map +1 -0
- package/dist/core/session-store.js +10 -0
- package/dist/core/sessions/export.d.ts +47 -0
- package/dist/core/sessions/export.d.ts.map +1 -0
- package/dist/core/sessions/export.js +256 -0
- package/dist/core/sessions/index.d.ts +132 -0
- package/dist/core/sessions/index.d.ts.map +1 -0
- package/dist/core/sessions/index.js +442 -0
- package/dist/core/sessions/metadata.d.ts +77 -0
- package/dist/core/sessions/metadata.d.ts.map +1 -0
- package/dist/core/sessions/metadata.js +233 -0
- package/dist/core/sessions/persistence.d.ts +72 -0
- package/dist/core/sessions/persistence.d.ts.map +1 -0
- package/dist/core/sessions/persistence.js +201 -0
- package/dist/core/sessions/types.d.ts +110 -0
- package/dist/core/sessions/types.d.ts.map +1 -0
- package/dist/core/sessions/types.js +4 -0
- package/dist/core/stream-highlighter.d.ts +18 -0
- package/dist/core/stream-highlighter.d.ts.map +1 -0
- package/dist/core/stream-highlighter.js +916 -0
- package/dist/core/system-reminders.d.ts +89 -0
- package/dist/core/system-reminders.d.ts.map +1 -0
- package/dist/core/system-reminders.js +285 -0
- package/dist/ecosystem/hooks/__tests__/index.test.d.ts +5 -0
- package/dist/ecosystem/hooks/__tests__/index.test.d.ts.map +1 -0
- package/dist/ecosystem/hooks/__tests__/index.test.js +458 -0
- package/dist/ecosystem/hooks/index.d.ts +59 -0
- package/dist/ecosystem/hooks/index.d.ts.map +1 -0
- package/dist/ecosystem/hooks/index.js +294 -0
- package/dist/ecosystem/hooks/prompt-evaluator.d.ts +32 -0
- package/dist/ecosystem/hooks/prompt-evaluator.d.ts.map +1 -0
- package/dist/ecosystem/hooks/prompt-evaluator.js +229 -0
- package/dist/ecosystem/skills/index.d.ts +55 -0
- package/dist/ecosystem/skills/index.d.ts.map +1 -0
- package/dist/ecosystem/skills/index.js +258 -0
- package/dist/ecosystem/tools/__tests__/index.test.d.ts +7 -0
- package/dist/ecosystem/tools/__tests__/index.test.d.ts.map +1 -0
- package/dist/ecosystem/tools/__tests__/index.test.js +856 -0
- package/dist/ecosystem/tools/index.d.ts +24 -0
- package/dist/ecosystem/tools/index.d.ts.map +1 -0
- package/dist/ecosystem/tools/index.js +1709 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -2
- package/dist/interfaces/mcp/client.d.ts +40 -0
- package/dist/interfaces/mcp/client.d.ts.map +1 -0
- package/dist/interfaces/mcp/client.js +309 -0
- package/dist/interfaces/ui/index.d.ts +36 -0
- package/dist/interfaces/ui/index.d.ts.map +1 -0
- package/dist/interfaces/ui/index.js +61 -0
- package/dist/interfaces/ui/spinner.d.ts +140 -0
- package/dist/interfaces/ui/spinner.d.ts.map +1 -0
- package/dist/interfaces/ui/spinner.js +342 -0
- package/dist/interfaces/ui/terminal/cli/index.d.ts +12 -0
- package/dist/interfaces/ui/terminal/cli/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/cli/index.js +167 -0
- package/dist/interfaces/ui/terminal/shared/args.d.ts +39 -0
- package/dist/interfaces/ui/terminal/shared/args.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/args.js +176 -0
- package/dist/interfaces/ui/terminal/shared/index.d.ts +11 -0
- package/dist/interfaces/ui/terminal/shared/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/index.js +16 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.d.ts +124 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.js +246 -0
- package/dist/interfaces/ui/terminal/shared/query.d.ts +22 -0
- package/dist/interfaces/ui/terminal/shared/query.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/query.js +100 -0
- package/dist/interfaces/ui/terminal/shared/setup.d.ts +33 -0
- package/dist/interfaces/ui/terminal/shared/setup.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/setup.js +226 -0
- package/dist/interfaces/ui/terminal/shared/status-line.d.ts +117 -0
- package/dist/interfaces/ui/terminal/shared/status-line.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/status-line.js +267 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts +38 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.js +102 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts +39 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.js +215 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.d.ts +91 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.js +154 -0
- package/dist/interfaces/ui/terminal/tui/InputField.d.ts +18 -0
- package/dist/interfaces/ui/terminal/tui/InputField.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InputField.js +41 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts +16 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.js +451 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts +10 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.js +91 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts +48 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.js +151 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts +9 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.js +36 -0
- package/dist/interfaces/ui/terminal/tui/commands.d.ts +21 -0
- package/dist/interfaces/ui/terminal/tui/commands.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/commands.js +359 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts +115 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.js +306 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts +92 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.js +399 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts +59 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.js +139 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts +68 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.js +340 -0
- package/dist/interfaces/ui/terminal/tui/components/index.d.ts +23 -0
- package/dist/interfaces/ui/terminal/tui/components/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/index.js +51 -0
- package/dist/interfaces/ui/terminal/tui/console.d.ts +20 -0
- package/dist/interfaces/ui/terminal/tui/console.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/console.js +46 -0
- package/dist/interfaces/ui/terminal/tui/index.d.ts +20 -0
- package/dist/interfaces/ui/terminal/tui/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/index.js +28 -0
- package/dist/interfaces/ui/terminal/tui/run.d.ts +13 -0
- package/dist/interfaces/ui/terminal/tui/run.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/run.js +31 -0
- package/dist/interfaces/ui/terminal/tui/spinner.d.ts +44 -0
- package/dist/interfaces/ui/terminal/tui/spinner.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/spinner.js +59 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.d.ts +39 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.js +198 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts +167 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.js +330 -0
- package/dist/interfaces/ui/terminal/tui/types.d.ts +165 -0
- package/dist/interfaces/ui/terminal/tui/types.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/types.js +5 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts +23 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.js +72 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts +90 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.js +188 -0
- package/dist/native/index.d.ts +480 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +1625 -0
- package/dist/teammates/index.d.ts +161 -0
- package/dist/teammates/index.d.ts.map +1 -0
- package/dist/teammates/index.js +827 -0
- package/dist/types/index.d.ts +482 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +52 -0
- package/native/README.md +5 -5
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.node +0 -0
- package/native/package.json +4 -4
- package/package.json +33 -16
- package/packages/src/core/__tests__/permissions.test.ts +1091 -0
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +280 -0
- package/packages/src/core/agent-loop/__tests__/formatters.test.ts +234 -0
- package/packages/src/core/agent-loop/__tests__/index.test.ts +162 -0
- package/packages/src/core/agent-loop/__tests__/loop-state.test.ts +413 -0
- package/packages/src/core/agent-loop/__tests__/message-builder.test.ts +229 -0
- package/packages/src/core/agent-loop/__tests__/tool-executor.test.ts +457 -0
- package/packages/src/core/agent-loop/compaction.ts +88 -0
- package/packages/src/core/agent-loop/formatters.ts +50 -0
- package/packages/src/core/agent-loop/index.ts +135 -0
- package/packages/src/core/agent-loop/loop-state.ts +187 -0
- package/packages/src/core/agent-loop/message-builder.ts +62 -0
- package/packages/src/core/agent-loop/tool-executor.ts +211 -0
- package/packages/src/core/agent-loop/turn-executor.ts +222 -0
- package/packages/src/core/agent-loop/types.ts +148 -0
- package/packages/src/core/agent-loop.ts +18 -0
- package/packages/src/core/api-client-impl.ts +619 -0
- package/packages/src/core/api-client.ts +6 -0
- package/packages/src/core/checkpoints.ts +606 -0
- package/packages/src/core/claude-md.ts +272 -0
- package/packages/src/core/cognitive-security/hooks.ts +590 -0
- package/packages/src/core/cognitive-security/index.ts +2041 -0
- package/packages/src/core/cognitive-security/middleware.ts +536 -0
- package/packages/src/core/config-loader.ts +324 -0
- package/packages/src/core/context-compaction.ts +578 -0
- package/packages/src/core/git-status.ts +262 -0
- package/packages/src/core/image.test.ts +180 -0
- package/packages/src/core/image.ts +350 -0
- package/packages/src/core/lmdb.db +0 -0
- package/packages/src/core/lmdb.db-lock +0 -0
- package/packages/src/core/models.ts +430 -0
- package/packages/src/core/normalizers/todo +4 -0
- package/packages/src/core/permissions.ts +431 -0
- package/packages/src/core/retry.ts +170 -0
- package/packages/src/core/session-store.ts +36 -0
- package/packages/src/core/sessions/export.ts +329 -0
- package/packages/src/core/sessions/index.ts +587 -0
- package/packages/src/core/sessions/metadata.ts +309 -0
- package/packages/src/core/sessions/persistence.ts +244 -0
- package/packages/src/core/sessions/types.ts +169 -0
- package/packages/src/core/stream-highlighter.ts +1123 -0
- package/packages/src/core/system-reminders.ts +402 -0
- package/packages/src/core/todo +8 -0
- package/packages/src/ecosystem/hooks/__tests__/index.test.ts +561 -0
- package/packages/src/ecosystem/hooks/index.ts +341 -0
- package/packages/src/ecosystem/hooks/prompt-evaluator.ts +300 -0
- package/packages/src/ecosystem/skills/index.ts +295 -0
- package/packages/src/ecosystem/tools/__tests__/index.test.ts +1335 -0
- package/packages/src/ecosystem/tools/index.ts +1877 -0
- package/packages/src/index.ts +120 -0
- package/packages/src/interfaces/mcp/client.ts +389 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/index.ts +161 -0
- package/packages/src/interfaces/ui/lmdb.db +0 -0
- package/packages/src/interfaces/ui/lmdb.db-lock +0 -0
- package/packages/src/interfaces/ui/spinner.ts +451 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +228 -0
- package/packages/src/interfaces/ui/terminal/lmdb.db +0 -0
- package/packages/src/interfaces/ui/terminal/lmdb.db-lock +0 -0
- package/packages/src/interfaces/ui/terminal/shared/args.ts +222 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +71 -0
- package/packages/src/interfaces/ui/terminal/shared/loading-state.ts +322 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +146 -0
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +295 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +358 -0
- package/packages/src/interfaces/ui/terminal/shared/system-prompt.ts +146 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +262 -0
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +232 -0
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +62 -0
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +537 -0
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +107 -0
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +240 -0
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +54 -0
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +438 -0
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +584 -0
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +614 -0
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +333 -0
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +604 -0
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +118 -0
- package/packages/src/interfaces/ui/terminal/tui/console.ts +49 -0
- package/packages/src/interfaces/ui/terminal/tui/index.ts +90 -0
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +42 -0
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +69 -0
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +390 -0
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +422 -0
- package/packages/src/interfaces/ui/terminal/tui/types.ts +186 -0
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +104 -0
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +239 -0
- package/packages/src/lmdb.db +0 -0
- package/packages/src/lmdb.db-lock +0 -0
- package/packages/src/native/index.ts +2345 -0
- package/packages/src/teammates/index.ts +982 -0
- package/packages/src/types/index.ts +722 -0
- package/dist/cli.js +0 -148
- package/dist/index-0pkak453.js +0 -136
- package/dist/index-0qd0x8b4.js +0 -110
- package/dist/index-0x3kprq6.js +0 -240
- package/dist/index-1eawy937.js +0 -308
- package/dist/index-24m2aygy.js +0 -240
- package/dist/index-29xcjnne.js +0 -280
- package/dist/index-2avyytn5.js +0 -349
- package/dist/index-4ms367ey.js +0 -136
- package/dist/index-4w2t3b0m.js +0 -240
- package/dist/index-4xfgd8nz.js +0 -261
- package/dist/index-5acjp9gc.js +0 -157
- package/dist/index-5s15hr56.js +0 -136
- package/dist/index-6e4wf341.js +0 -349
- package/dist/index-6fvnkedw.js +0 -240
- package/dist/index-6rqpmd4g.js +0 -128
- package/dist/index-77ckwnbm.js +0 -280
- package/dist/index-9knxy49k.js +0 -128
- package/dist/index-9zrnw4zx.js +0 -128
- package/dist/index-bk21w99v.js +0 -280
- package/dist/index-c41n76fv.js +0 -240
- package/dist/index-cb4ppjdt.js +0 -255
- package/dist/index-cfb2edt6.js +0 -240
- package/dist/index-cmfa38hh.js +0 -308
- package/dist/index-datjz8q1.js +0 -257
- package/dist/index-eadf4wvn.js +0 -240
- package/dist/index-em5k0m3z.js +0 -345
- package/dist/index-gh8r333a.js +0 -110
- package/dist/index-gkx6k2tr.js +0 -261
- package/dist/index-h5cabfks.js +0 -155
- package/dist/index-hcrpwyy3.js +0 -261
- package/dist/index-hk7fwwa8.js +0 -257
- package/dist/index-jb8cw7f8.js +0 -136
- package/dist/index-kbyw4th1.js +0 -347
- package/dist/index-kgj5gqnm.js +0 -345
- package/dist/index-mdf6xp1z.js +0 -255
- package/dist/index-mrhv8kvc.js +0 -280
- package/dist/index-mt4743dd.js +0 -161
- package/dist/index-qnwsg97q.js +0 -240
- package/dist/index-qwdy6x44.js +0 -261
- package/dist/index-rmj77261.js +0 -157
- package/dist/index-sbbw1a61.js +0 -349
- package/dist/index-svy5bcpn.js +0 -345
- package/dist/index-tvmy7tm9.js +0 -261
- package/dist/index-tzz4vzkj.js +0 -312
- package/dist/index-vz80zmhe.js +0 -110
- package/dist/index-wed2fk67.js +0 -240
- package/dist/index-wksgzz8e.js +0 -280
- package/dist/index-wn2m4wma.js +0 -240
- package/dist/index-xha05vjc.js +0 -257
- package/dist/index-yc6eh8p8.js +0 -136
- package/dist/index-ycjxx9ft.js +0 -240
- package/dist/index-z0gzd0fc.js +0 -110
- package/dist/index-z8cwtf8j.js +0 -240
- package/dist/index-zy5mtt00.js +0 -128
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Tools Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive tests for Read, Write, Edit, Bash, Glob, and Grep tools.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, it, expect, beforeEach, afterEach } from "bun:test";
|
|
7
|
+
import { mkdtemp, writeFile, mkdir, rm, stat } from "fs/promises";
|
|
8
|
+
import { tmpdir } from "os";
|
|
9
|
+
import { join, dirname } from "path";
|
|
10
|
+
import { ReadTool, WriteTool, EditTool, BashTool, GlobTool, GrepTool, getToolByName, builtInTools, } from "../index.js";
|
|
11
|
+
// Helper to create a default tool context
|
|
12
|
+
function createToolContext(workingDirectory) {
|
|
13
|
+
return {
|
|
14
|
+
workingDirectory,
|
|
15
|
+
permissionMode: "bypassPermissions",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe("Built-in Tools Registry", () => {
|
|
19
|
+
test("builtInTools array contains all expected tools", () => {
|
|
20
|
+
const toolNames = builtInTools.map((t) => t.name);
|
|
21
|
+
expect(toolNames).toContain("Read");
|
|
22
|
+
expect(toolNames).toContain("Write");
|
|
23
|
+
expect(toolNames).toContain("Edit");
|
|
24
|
+
expect(toolNames).toContain("Bash");
|
|
25
|
+
expect(toolNames).toContain("Glob");
|
|
26
|
+
expect(toolNames).toContain("Grep");
|
|
27
|
+
});
|
|
28
|
+
test("getToolByName returns correct tool", () => {
|
|
29
|
+
expect(getToolByName("Read")?.name).toBe("Read");
|
|
30
|
+
expect(getToolByName("Write")?.name).toBe("Write");
|
|
31
|
+
expect(getToolByName("NonExistent")).toBeUndefined();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("ReadTool", () => {
|
|
35
|
+
let tempDir;
|
|
36
|
+
let context;
|
|
37
|
+
beforeEach(async () => {
|
|
38
|
+
tempDir = await mkdtemp(join(tmpdir(), "read-tool-test-"));
|
|
39
|
+
context = createToolContext(tempDir);
|
|
40
|
+
});
|
|
41
|
+
afterEach(async () => {
|
|
42
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
43
|
+
});
|
|
44
|
+
describe("successful operations", () => {
|
|
45
|
+
test("reads a simple text file", async () => {
|
|
46
|
+
const filePath = join(tempDir, "test.txt");
|
|
47
|
+
await writeFile(filePath, "Hello, World!");
|
|
48
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
49
|
+
expect(result.is_error).toBeUndefined();
|
|
50
|
+
expect(result.content).toContain("Hello, World!");
|
|
51
|
+
});
|
|
52
|
+
test("reads a file with multiple lines", async () => {
|
|
53
|
+
const filePath = join(tempDir, "multiline.txt");
|
|
54
|
+
const content = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5";
|
|
55
|
+
await writeFile(filePath, content);
|
|
56
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
57
|
+
expect(result.is_error).toBeUndefined();
|
|
58
|
+
expect(result.content).toContain("Line 1");
|
|
59
|
+
expect(result.content).toContain("Line 5");
|
|
60
|
+
});
|
|
61
|
+
test("reads file with line numbers (cat -n format)", async () => {
|
|
62
|
+
const filePath = join(tempDir, "numbered.txt");
|
|
63
|
+
await writeFile(filePath, "First\nSecond\nThird");
|
|
64
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
65
|
+
expect(result.content).toMatch(/1\tFirst/);
|
|
66
|
+
expect(result.content).toMatch(/2\tSecond/);
|
|
67
|
+
expect(result.content).toMatch(/3\tThird/);
|
|
68
|
+
});
|
|
69
|
+
test("reads file with offset", async () => {
|
|
70
|
+
const filePath = join(tempDir, "offset.txt");
|
|
71
|
+
await writeFile(filePath, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5");
|
|
72
|
+
const result = await ReadTool.handler({ file_path: filePath, offset: 3 }, context);
|
|
73
|
+
expect(result.content).toContain("Line 3");
|
|
74
|
+
expect(result.content).not.toContain("Line 1");
|
|
75
|
+
expect(result.content).not.toContain("Line 2");
|
|
76
|
+
});
|
|
77
|
+
test("reads file with limit", async () => {
|
|
78
|
+
const filePath = join(tempDir, "limit.txt");
|
|
79
|
+
await writeFile(filePath, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5");
|
|
80
|
+
const result = await ReadTool.handler({ file_path: filePath, limit: 2 }, context);
|
|
81
|
+
expect(result.content).toContain("Line 1");
|
|
82
|
+
expect(result.content).toContain("Line 2");
|
|
83
|
+
expect(result.content).not.toContain("Line 5");
|
|
84
|
+
});
|
|
85
|
+
test("reads file with offset and limit", async () => {
|
|
86
|
+
const filePath = join(tempDir, "offset-limit.txt");
|
|
87
|
+
await writeFile(filePath, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5");
|
|
88
|
+
const result = await ReadTool.handler({ file_path: filePath, offset: 2, limit: 2 }, context);
|
|
89
|
+
expect(result.content).toContain("Line 2");
|
|
90
|
+
expect(result.content).toContain("Line 3");
|
|
91
|
+
expect(result.content).not.toContain("Line 1");
|
|
92
|
+
expect(result.content).not.toContain("Line 5");
|
|
93
|
+
});
|
|
94
|
+
test("reads an empty file", async () => {
|
|
95
|
+
const filePath = join(tempDir, "empty.txt");
|
|
96
|
+
await writeFile(filePath, "");
|
|
97
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
98
|
+
expect(result.is_error).toBeUndefined();
|
|
99
|
+
// Empty file returns a single line with line number but no content
|
|
100
|
+
expect(result.content).toBe("1\t");
|
|
101
|
+
});
|
|
102
|
+
test("reads a file with special characters", async () => {
|
|
103
|
+
const filePath = join(tempDir, "special.txt");
|
|
104
|
+
await writeFile(filePath, "Special: \t<Tab>\nUnicode: \u00e9\u00e8\u00ea");
|
|
105
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
106
|
+
expect(result.is_error).toBeUndefined();
|
|
107
|
+
expect(result.content).toContain("<Tab>");
|
|
108
|
+
expect(result.content).toContain("\u00e9\u00e8\u00ea");
|
|
109
|
+
});
|
|
110
|
+
test("reads a JSON file", async () => {
|
|
111
|
+
const filePath = join(tempDir, "data.json");
|
|
112
|
+
await writeFile(filePath, JSON.stringify({ key: "value", num: 42 }));
|
|
113
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
114
|
+
expect(result.is_error).toBeUndefined();
|
|
115
|
+
expect(result.content).toContain('"key"');
|
|
116
|
+
expect(result.content).toContain('"value"');
|
|
117
|
+
});
|
|
118
|
+
test("reads a TypeScript file", async () => {
|
|
119
|
+
const filePath = join(tempDir, "code.ts");
|
|
120
|
+
await writeFile(filePath, "const x: number = 42;\nexport { x };");
|
|
121
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
122
|
+
expect(result.is_error).toBeUndefined();
|
|
123
|
+
expect(result.content).toContain("const x: number = 42");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe("truncation warning", () => {
|
|
127
|
+
test("shows truncation warning when file exceeds limit", async () => {
|
|
128
|
+
const filePath = join(tempDir, "long.txt");
|
|
129
|
+
const lines = Array(3000)
|
|
130
|
+
.fill(null)
|
|
131
|
+
.map((_, i) => `Line ${i + 1}`)
|
|
132
|
+
.join("\n");
|
|
133
|
+
await writeFile(filePath, lines);
|
|
134
|
+
const result = await ReadTool.handler({ file_path: filePath, limit: 100 }, context);
|
|
135
|
+
expect(result.content).toContain("WARNING");
|
|
136
|
+
expect(result.content).toContain("3000 lines");
|
|
137
|
+
expect(result.content).toContain("limit: 100");
|
|
138
|
+
});
|
|
139
|
+
test("does not show truncation warning when file is within limit", async () => {
|
|
140
|
+
const filePath = join(tempDir, "short.txt");
|
|
141
|
+
await writeFile(filePath, "Line 1\nLine 2\nLine 3");
|
|
142
|
+
const result = await ReadTool.handler({ file_path: filePath, limit: 100 }, context);
|
|
143
|
+
expect(result.content).not.toContain("WARNING");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe("error handling", () => {
|
|
147
|
+
test("returns error for non-existent file", async () => {
|
|
148
|
+
const result = await ReadTool.handler({ file_path: join(tempDir, "nonexistent.txt") }, context);
|
|
149
|
+
expect(result.is_error).toBe(true);
|
|
150
|
+
expect(result.content).toContain("Error");
|
|
151
|
+
expect(result.content).toContain("not found");
|
|
152
|
+
});
|
|
153
|
+
test("returns error for empty file_path", async () => {
|
|
154
|
+
const result = await ReadTool.handler({ file_path: "" }, context);
|
|
155
|
+
expect(result.is_error).toBe(true);
|
|
156
|
+
expect(result.content).toContain("required");
|
|
157
|
+
});
|
|
158
|
+
test("returns error for missing file_path", async () => {
|
|
159
|
+
const result = await ReadTool.handler({}, context);
|
|
160
|
+
expect(result.is_error).toBe(true);
|
|
161
|
+
expect(result.content).toContain("required");
|
|
162
|
+
});
|
|
163
|
+
test("returns error for whitespace-only file_path", async () => {
|
|
164
|
+
const result = await ReadTool.handler({ file_path: " " }, context);
|
|
165
|
+
expect(result.is_error).toBe(true);
|
|
166
|
+
expect(result.content).toContain("required");
|
|
167
|
+
});
|
|
168
|
+
test("returns error for directory path instead of file", async () => {
|
|
169
|
+
const dirPath = join(tempDir, "subdir");
|
|
170
|
+
await mkdir(dirPath);
|
|
171
|
+
const result = await ReadTool.handler({ file_path: dirPath }, context);
|
|
172
|
+
// Bun.file can read directories but returns empty or error
|
|
173
|
+
// The behavior depends on the implementation
|
|
174
|
+
expect(result).toBeDefined();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("binary file handling", () => {
|
|
178
|
+
test("returns error for binary exclusion files", async () => {
|
|
179
|
+
const filePath = join(tempDir, "test.exe");
|
|
180
|
+
// Write a small binary file (fake executable header)
|
|
181
|
+
await writeFile(filePath, Buffer.from([0x4d, 0x5a, 0x90, 0x00]));
|
|
182
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
183
|
+
expect(result.is_error).toBe(true);
|
|
184
|
+
expect(result.content).toContain("Binary file detected");
|
|
185
|
+
});
|
|
186
|
+
test("returns error for zip files", async () => {
|
|
187
|
+
const filePath = join(tempDir, "archive.zip");
|
|
188
|
+
// Write a minimal zip header
|
|
189
|
+
await writeFile(filePath, Buffer.from([0x50, 0x4b, 0x03, 0x04]));
|
|
190
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
191
|
+
expect(result.is_error).toBe(true);
|
|
192
|
+
expect(result.content).toContain("Binary file detected");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe("WriteTool", () => {
|
|
197
|
+
let tempDir;
|
|
198
|
+
let context;
|
|
199
|
+
beforeEach(async () => {
|
|
200
|
+
tempDir = await mkdtemp(join(tmpdir(), "write-tool-test-"));
|
|
201
|
+
context = createToolContext(tempDir);
|
|
202
|
+
});
|
|
203
|
+
afterEach(async () => {
|
|
204
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
205
|
+
});
|
|
206
|
+
describe("successful operations", () => {
|
|
207
|
+
test("writes a new file", async () => {
|
|
208
|
+
const filePath = join(tempDir, "new.txt");
|
|
209
|
+
const result = await WriteTool.handler({ file_path: filePath, content: "Hello, World!" }, context);
|
|
210
|
+
expect(result.is_error).toBeUndefined();
|
|
211
|
+
expect(result.content).toContain("Successfully wrote");
|
|
212
|
+
// Verify file was created
|
|
213
|
+
const file = Bun.file(filePath);
|
|
214
|
+
expect(await file.exists()).toBe(true);
|
|
215
|
+
expect(await file.text()).toBe("Hello, World!");
|
|
216
|
+
});
|
|
217
|
+
test("overwrites an existing file", async () => {
|
|
218
|
+
const filePath = join(tempDir, "existing.txt");
|
|
219
|
+
await writeFile(filePath, "Original content");
|
|
220
|
+
const result = await WriteTool.handler({ file_path: filePath, content: "New content" }, context);
|
|
221
|
+
expect(result.is_error).toBeUndefined();
|
|
222
|
+
const file = Bun.file(filePath);
|
|
223
|
+
expect(await file.text()).toBe("New content");
|
|
224
|
+
});
|
|
225
|
+
test("writes an empty file", async () => {
|
|
226
|
+
const filePath = join(tempDir, "empty.txt");
|
|
227
|
+
const result = await WriteTool.handler({ file_path: filePath, content: "" }, context);
|
|
228
|
+
expect(result.is_error).toBeUndefined();
|
|
229
|
+
const file = Bun.file(filePath);
|
|
230
|
+
expect(await file.text()).toBe("");
|
|
231
|
+
});
|
|
232
|
+
test("writes a file with special characters", async () => {
|
|
233
|
+
const filePath = join(tempDir, "special.txt");
|
|
234
|
+
const content = "Special: \t<Tab>\nUnicode: \u00e9\u00e8\u00ea\nEmoji: \ud83d\ude00";
|
|
235
|
+
const result = await WriteTool.handler({ file_path: filePath, content }, context);
|
|
236
|
+
expect(result.is_error).toBeUndefined();
|
|
237
|
+
const file = Bun.file(filePath);
|
|
238
|
+
expect(await file.text()).toBe(content);
|
|
239
|
+
});
|
|
240
|
+
test("writes a large file", async () => {
|
|
241
|
+
const filePath = join(tempDir, "large.txt");
|
|
242
|
+
const content = "x".repeat(100000);
|
|
243
|
+
const result = await WriteTool.handler({ file_path: filePath, content }, context);
|
|
244
|
+
expect(result.is_error).toBeUndefined();
|
|
245
|
+
const file = Bun.file(filePath);
|
|
246
|
+
expect(await file.text()).toBe(content);
|
|
247
|
+
});
|
|
248
|
+
test("writes JSON content", async () => {
|
|
249
|
+
const filePath = join(tempDir, "data.json");
|
|
250
|
+
const content = JSON.stringify({ key: "value", nested: { a: 1 } });
|
|
251
|
+
const result = await WriteTool.handler({ file_path: filePath, content }, context);
|
|
252
|
+
expect(result.is_error).toBeUndefined();
|
|
253
|
+
const file = Bun.file(filePath);
|
|
254
|
+
const parsed = JSON.parse(await file.text());
|
|
255
|
+
expect(parsed.key).toBe("value");
|
|
256
|
+
});
|
|
257
|
+
test("creates nested directories if needed", async () => {
|
|
258
|
+
const filePath = join(tempDir, "nested", "deep", "file.txt");
|
|
259
|
+
// Bun.write creates parent directories automatically
|
|
260
|
+
const result = await WriteTool.handler({ file_path: filePath, content: "nested content" }, context);
|
|
261
|
+
expect(result.is_error).toBeUndefined();
|
|
262
|
+
const file = Bun.file(filePath);
|
|
263
|
+
expect(await file.exists()).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
describe("error handling", () => {
|
|
267
|
+
test("returns error for empty file_path", async () => {
|
|
268
|
+
const result = await WriteTool.handler({ file_path: "", content: "test" }, context);
|
|
269
|
+
expect(result.is_error).toBe(true);
|
|
270
|
+
expect(result.content).toContain("required");
|
|
271
|
+
});
|
|
272
|
+
test("returns error for missing file_path", async () => {
|
|
273
|
+
const result = await WriteTool.handler({ content: "test" }, context);
|
|
274
|
+
expect(result.is_error).toBe(true);
|
|
275
|
+
expect(result.content).toContain("required");
|
|
276
|
+
});
|
|
277
|
+
test("returns error for missing content", async () => {
|
|
278
|
+
const result = await WriteTool.handler({ file_path: join(tempDir, "test.txt") }, context);
|
|
279
|
+
expect(result.is_error).toBe(true);
|
|
280
|
+
expect(result.content).toContain("required");
|
|
281
|
+
});
|
|
282
|
+
test("returns error for null content", async () => {
|
|
283
|
+
const result = await WriteTool.handler({ file_path: join(tempDir, "test.txt"), content: null }, context);
|
|
284
|
+
expect(result.is_error).toBe(true);
|
|
285
|
+
expect(result.content).toContain("required");
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
describe("EditTool", () => {
|
|
290
|
+
let tempDir;
|
|
291
|
+
let context;
|
|
292
|
+
beforeEach(async () => {
|
|
293
|
+
tempDir = await mkdtemp(join(tmpdir(), "edit-tool-test-"));
|
|
294
|
+
context = createToolContext(tempDir);
|
|
295
|
+
});
|
|
296
|
+
afterEach(async () => {
|
|
297
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
298
|
+
});
|
|
299
|
+
describe("successful operations", () => {
|
|
300
|
+
test("edits a file with exact string replacement", async () => {
|
|
301
|
+
const filePath = join(tempDir, "edit.txt");
|
|
302
|
+
await writeFile(filePath, "Hello, World!");
|
|
303
|
+
const result = await EditTool.handler({
|
|
304
|
+
file_path: filePath,
|
|
305
|
+
old_string: "World",
|
|
306
|
+
new_string: "Universe",
|
|
307
|
+
}, context);
|
|
308
|
+
expect(result.is_error).toBeUndefined();
|
|
309
|
+
expect(result.content).toContain("Successfully edited");
|
|
310
|
+
const file = Bun.file(filePath);
|
|
311
|
+
expect(await file.text()).toBe("Hello, Universe!");
|
|
312
|
+
});
|
|
313
|
+
test("edits multi-line content", async () => {
|
|
314
|
+
const filePath = join(tempDir, "multiline.txt");
|
|
315
|
+
await writeFile(filePath, "Line 1\nLine 2\nLine 3");
|
|
316
|
+
const result = await EditTool.handler({
|
|
317
|
+
file_path: filePath,
|
|
318
|
+
old_string: "Line 2",
|
|
319
|
+
new_string: "Modified Line 2",
|
|
320
|
+
}, context);
|
|
321
|
+
expect(result.is_error).toBeUndefined();
|
|
322
|
+
const file = Bun.file(filePath);
|
|
323
|
+
const text = await file.text();
|
|
324
|
+
expect(text).toContain("Modified Line 2");
|
|
325
|
+
expect(text).toContain("Line 1");
|
|
326
|
+
expect(text).toContain("Line 3");
|
|
327
|
+
});
|
|
328
|
+
test("replaces all occurrences with replace_all", async () => {
|
|
329
|
+
const filePath = join(tempDir, "replace-all.txt");
|
|
330
|
+
await writeFile(filePath, "foo bar foo baz foo");
|
|
331
|
+
const result = await EditTool.handler({
|
|
332
|
+
file_path: filePath,
|
|
333
|
+
old_string: "foo",
|
|
334
|
+
new_string: "qux",
|
|
335
|
+
replace_all: true,
|
|
336
|
+
}, context);
|
|
337
|
+
expect(result.is_error).toBeUndefined();
|
|
338
|
+
expect(result.content).toContain("3 occurrences");
|
|
339
|
+
const file = Bun.file(filePath);
|
|
340
|
+
expect(await file.text()).toBe("qux bar qux baz qux");
|
|
341
|
+
});
|
|
342
|
+
test("replaces with empty string (deletion)", async () => {
|
|
343
|
+
const filePath = join(tempDir, "delete.txt");
|
|
344
|
+
await writeFile(filePath, "Hello, World!");
|
|
345
|
+
const result = await EditTool.handler({
|
|
346
|
+
file_path: filePath,
|
|
347
|
+
old_string: ", World",
|
|
348
|
+
new_string: "",
|
|
349
|
+
}, context);
|
|
350
|
+
expect(result.is_error).toBeUndefined();
|
|
351
|
+
const file = Bun.file(filePath);
|
|
352
|
+
expect(await file.text()).toBe("Hello!");
|
|
353
|
+
});
|
|
354
|
+
test("replaces with longer string", async () => {
|
|
355
|
+
const filePath = join(tempDir, "expand.txt");
|
|
356
|
+
await writeFile(filePath, "Hi");
|
|
357
|
+
const result = await EditTool.handler({
|
|
358
|
+
file_path: filePath,
|
|
359
|
+
old_string: "Hi",
|
|
360
|
+
new_string: "Hello, this is a much longer greeting!",
|
|
361
|
+
}, context);
|
|
362
|
+
expect(result.is_error).toBeUndefined();
|
|
363
|
+
const file = Bun.file(filePath);
|
|
364
|
+
expect(await file.text()).toBe("Hello, this is a much longer greeting!");
|
|
365
|
+
});
|
|
366
|
+
test("edits file with special regex characters in old_string", async () => {
|
|
367
|
+
const filePath = join(tempDir, "regex.txt");
|
|
368
|
+
await writeFile(filePath, "Price: $100 (50% off)");
|
|
369
|
+
const result = await EditTool.handler({
|
|
370
|
+
file_path: filePath,
|
|
371
|
+
old_string: "$100 (50% off)",
|
|
372
|
+
new_string: "$80",
|
|
373
|
+
}, context);
|
|
374
|
+
expect(result.is_error).toBeUndefined();
|
|
375
|
+
const file = Bun.file(filePath);
|
|
376
|
+
expect(await file.text()).toBe("Price: $80");
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
describe("error handling", () => {
|
|
380
|
+
test("returns error when old_string not found", async () => {
|
|
381
|
+
const filePath = join(tempDir, "not-found.txt");
|
|
382
|
+
await writeFile(filePath, "Hello, World!");
|
|
383
|
+
const result = await EditTool.handler({
|
|
384
|
+
file_path: filePath,
|
|
385
|
+
old_string: "NonExistent",
|
|
386
|
+
new_string: "Replacement",
|
|
387
|
+
}, context);
|
|
388
|
+
expect(result.is_error).toBe(true);
|
|
389
|
+
expect(result.content).toContain("not found");
|
|
390
|
+
});
|
|
391
|
+
test("returns error when string appears multiple times without replace_all", async () => {
|
|
392
|
+
const filePath = join(tempDir, "multiple.txt");
|
|
393
|
+
await writeFile(filePath, "foo bar foo baz foo");
|
|
394
|
+
const result = await EditTool.handler({
|
|
395
|
+
file_path: filePath,
|
|
396
|
+
old_string: "foo",
|
|
397
|
+
new_string: "qux",
|
|
398
|
+
}, context);
|
|
399
|
+
expect(result.is_error).toBe(true);
|
|
400
|
+
expect(result.content).toContain("multiple times");
|
|
401
|
+
expect(result.content).toContain("replace_all");
|
|
402
|
+
});
|
|
403
|
+
test("returns error for non-existent file", async () => {
|
|
404
|
+
const result = await EditTool.handler({
|
|
405
|
+
file_path: join(tempDir, "nonexistent.txt"),
|
|
406
|
+
old_string: "old",
|
|
407
|
+
new_string: "new",
|
|
408
|
+
}, context);
|
|
409
|
+
expect(result.is_error).toBe(true);
|
|
410
|
+
});
|
|
411
|
+
test("returns error for missing file_path", async () => {
|
|
412
|
+
const result = await EditTool.handler({ old_string: "old", new_string: "new" }, context);
|
|
413
|
+
expect(result.is_error).toBe(true);
|
|
414
|
+
expect(result.content).toContain("required");
|
|
415
|
+
});
|
|
416
|
+
test("returns error for missing old_string", async () => {
|
|
417
|
+
const result = await EditTool.handler({ file_path: join(tempDir, "test.txt"), new_string: "new" }, context);
|
|
418
|
+
expect(result.is_error).toBe(true);
|
|
419
|
+
expect(result.content).toContain("required");
|
|
420
|
+
});
|
|
421
|
+
test("returns error for missing new_string", async () => {
|
|
422
|
+
const result = await EditTool.handler({ file_path: join(tempDir, "test.txt"), old_string: "old" }, context);
|
|
423
|
+
expect(result.is_error).toBe(true);
|
|
424
|
+
expect(result.content).toContain("required");
|
|
425
|
+
});
|
|
426
|
+
test("returns error when replace_all finds no matches", async () => {
|
|
427
|
+
const filePath = join(tempDir, "no-match.txt");
|
|
428
|
+
await writeFile(filePath, "Hello, World!");
|
|
429
|
+
const result = await EditTool.handler({
|
|
430
|
+
file_path: filePath,
|
|
431
|
+
old_string: "NonExistent",
|
|
432
|
+
new_string: "Replacement",
|
|
433
|
+
replace_all: true,
|
|
434
|
+
}, context);
|
|
435
|
+
expect(result.is_error).toBe(true);
|
|
436
|
+
expect(result.content).toContain("not found");
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
describe("BashTool", () => {
|
|
441
|
+
let tempDir;
|
|
442
|
+
let context;
|
|
443
|
+
beforeEach(async () => {
|
|
444
|
+
tempDir = await mkdtemp(join(tmpdir(), "bash-tool-test-"));
|
|
445
|
+
context = createToolContext(tempDir);
|
|
446
|
+
});
|
|
447
|
+
afterEach(async () => {
|
|
448
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
449
|
+
});
|
|
450
|
+
describe("successful operations", () => {
|
|
451
|
+
test("executes echo command", async () => {
|
|
452
|
+
const result = await BashTool.handler({ command: "echo 'Hello'" }, context);
|
|
453
|
+
expect(result.is_error).toBeUndefined();
|
|
454
|
+
expect(result.content).toContain("Hello");
|
|
455
|
+
});
|
|
456
|
+
test("executes pwd command", async () => {
|
|
457
|
+
const result = await BashTool.handler({ command: "pwd" }, context);
|
|
458
|
+
expect(result.is_error).toBeUndefined();
|
|
459
|
+
expect(result.content).toContain(tempDir.split("/").pop());
|
|
460
|
+
});
|
|
461
|
+
test("executes ls command", async () => {
|
|
462
|
+
await writeFile(join(tempDir, "test.txt"), "content");
|
|
463
|
+
const result = await BashTool.handler({ command: "ls" }, context);
|
|
464
|
+
expect(result.is_error).toBeUndefined();
|
|
465
|
+
expect(result.content).toContain("test.txt");
|
|
466
|
+
});
|
|
467
|
+
test("executes command with pipes", async () => {
|
|
468
|
+
const result = await BashTool.handler({ command: "echo 'line1\nline2\nline3' | wc -l" }, context);
|
|
469
|
+
expect(result.is_error).toBeUndefined();
|
|
470
|
+
expect(typeof result.content === "string" ? result.content.trim() : result.content).toBe("3");
|
|
471
|
+
});
|
|
472
|
+
test("executes command with redirection", async () => {
|
|
473
|
+
const result = await BashTool.handler({ command: "echo 'test content' > output.txt" }, context);
|
|
474
|
+
expect(result.is_error).toBeUndefined();
|
|
475
|
+
const file = Bun.file(join(tempDir, "output.txt"));
|
|
476
|
+
expect(await file.text()).toContain("test content");
|
|
477
|
+
});
|
|
478
|
+
test("executes command with environment variables", async () => {
|
|
479
|
+
const result = await BashTool.handler({ command: "MY_VAR=hello && echo $MY_VAR" }, context);
|
|
480
|
+
expect(result.is_error).toBeUndefined();
|
|
481
|
+
expect(result.content).toContain("hello");
|
|
482
|
+
});
|
|
483
|
+
test("returns (no output) for commands with no stdout", async () => {
|
|
484
|
+
const result = await BashTool.handler({ command: "mkdir -p subdir" }, context);
|
|
485
|
+
expect(result.is_error).toBeUndefined();
|
|
486
|
+
expect(result.content).toContain("no output");
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
describe("error handling", () => {
|
|
490
|
+
test("returns error for non-zero exit code", async () => {
|
|
491
|
+
const result = await BashTool.handler({ command: "exit 1" }, context);
|
|
492
|
+
expect(result.is_error).toBe(true);
|
|
493
|
+
expect(result.content).toContain("Exit code: 1");
|
|
494
|
+
});
|
|
495
|
+
test("returns error for command not found", async () => {
|
|
496
|
+
const result = await BashTool.handler({ command: "nonexistent_command_xyz" }, context);
|
|
497
|
+
expect(result.is_error).toBe(true);
|
|
498
|
+
});
|
|
499
|
+
test("returns error for missing command", async () => {
|
|
500
|
+
const result = await BashTool.handler({}, context);
|
|
501
|
+
expect(result.is_error).toBe(true);
|
|
502
|
+
expect(result.content).toContain("required");
|
|
503
|
+
});
|
|
504
|
+
test("returns error for empty command", async () => {
|
|
505
|
+
const result = await BashTool.handler({ command: "" }, context);
|
|
506
|
+
expect(result.is_error).toBe(true);
|
|
507
|
+
expect(result.content).toContain("required");
|
|
508
|
+
});
|
|
509
|
+
test("returns error for whitespace-only command", async () => {
|
|
510
|
+
const result = await BashTool.handler({ command: " " }, context);
|
|
511
|
+
expect(result.is_error).toBe(true);
|
|
512
|
+
expect(result.content).toContain("required");
|
|
513
|
+
});
|
|
514
|
+
test("includes stderr in error output", async () => {
|
|
515
|
+
const result = await BashTool.handler({ command: "ls /nonexistent_directory_xyz" }, context);
|
|
516
|
+
expect(result.is_error).toBe(true);
|
|
517
|
+
// Should include error message about non-existent directory
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
describe("timeout handling", () => {
|
|
521
|
+
test("uses default timeout when not specified", async () => {
|
|
522
|
+
const result = await BashTool.handler({ command: "echo 'quick'" }, context);
|
|
523
|
+
expect(result.is_error).toBeUndefined();
|
|
524
|
+
});
|
|
525
|
+
test("respects custom timeout", async () => {
|
|
526
|
+
const result = await BashTool.handler({ command: "echo 'test'", timeout: 5000 }, context);
|
|
527
|
+
expect(result.is_error).toBeUndefined();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
describe("working directory", () => {
|
|
531
|
+
test("executes in specified working directory", async () => {
|
|
532
|
+
const subDir = join(tempDir, "subdir");
|
|
533
|
+
await mkdir(subDir);
|
|
534
|
+
await writeFile(join(subDir, "unique.txt"), "content");
|
|
535
|
+
const localContext = createToolContext(subDir);
|
|
536
|
+
const result = await BashTool.handler({ command: "ls" }, localContext);
|
|
537
|
+
expect(result.content).toContain("unique.txt");
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
describe("GlobTool", () => {
|
|
542
|
+
let tempDir;
|
|
543
|
+
let context;
|
|
544
|
+
beforeEach(async () => {
|
|
545
|
+
tempDir = await mkdtemp(join(tmpdir(), "glob-tool-test-"));
|
|
546
|
+
context = createToolContext(tempDir);
|
|
547
|
+
// Create test file structure
|
|
548
|
+
await writeFile(join(tempDir, "file1.txt"), "content");
|
|
549
|
+
await writeFile(join(tempDir, "file2.txt"), "content");
|
|
550
|
+
await writeFile(join(tempDir, "script.ts"), "content");
|
|
551
|
+
await writeFile(join(tempDir, "config.json"), "content");
|
|
552
|
+
await mkdir(join(tempDir, "subdir"));
|
|
553
|
+
await writeFile(join(tempDir, "subdir", "nested.txt"), "content");
|
|
554
|
+
await writeFile(join(tempDir, "subdir", "nested.ts"), "content");
|
|
555
|
+
});
|
|
556
|
+
afterEach(async () => {
|
|
557
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
558
|
+
});
|
|
559
|
+
describe("successful operations", () => {
|
|
560
|
+
test("finds files with *.txt pattern", async () => {
|
|
561
|
+
const result = await GlobTool.handler({ pattern: "*.txt" }, context);
|
|
562
|
+
expect(result.is_error).toBeUndefined();
|
|
563
|
+
expect(result.content).toContain("file1.txt");
|
|
564
|
+
expect(result.content).toContain("file2.txt");
|
|
565
|
+
});
|
|
566
|
+
test("finds files with **/*.ts pattern (recursive)", async () => {
|
|
567
|
+
const result = await GlobTool.handler({ pattern: "**/*.ts" }, context);
|
|
568
|
+
expect(result.is_error).toBeUndefined();
|
|
569
|
+
expect(result.content).toContain("script.ts");
|
|
570
|
+
expect(result.content).toContain("nested.ts");
|
|
571
|
+
});
|
|
572
|
+
test("finds files with **/* pattern (all files)", async () => {
|
|
573
|
+
const result = await GlobTool.handler({ pattern: "**/*" }, context);
|
|
574
|
+
expect(result.is_error).toBeUndefined();
|
|
575
|
+
expect(result.content).toContain("file1.txt");
|
|
576
|
+
expect(result.content).toContain("script.ts");
|
|
577
|
+
expect(result.content).toContain("nested.txt");
|
|
578
|
+
});
|
|
579
|
+
test("finds files in specific directory", async () => {
|
|
580
|
+
const result = await GlobTool.handler({ pattern: "*.txt", path: join(tempDir, "subdir") }, context);
|
|
581
|
+
expect(result.is_error).toBeUndefined();
|
|
582
|
+
expect(result.content).toContain("nested.txt");
|
|
583
|
+
expect(result.content).not.toContain("file1.txt");
|
|
584
|
+
});
|
|
585
|
+
test("finds JSON files", async () => {
|
|
586
|
+
const result = await GlobTool.handler({ pattern: "*.json" }, context);
|
|
587
|
+
expect(result.is_error).toBeUndefined();
|
|
588
|
+
expect(result.content).toContain("config.json");
|
|
589
|
+
});
|
|
590
|
+
test("returns absolute paths", async () => {
|
|
591
|
+
const result = await GlobTool.handler({ pattern: "*.txt" }, context);
|
|
592
|
+
expect(result.content).toContain(tempDir);
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
describe("no matches", () => {
|
|
596
|
+
test("returns message when no files match", async () => {
|
|
597
|
+
const result = await GlobTool.handler({ pattern: "*.nonexistent" }, context);
|
|
598
|
+
expect(result.is_error).toBeUndefined();
|
|
599
|
+
expect(result.content).toContain("No files found");
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
describe("error handling", () => {
|
|
603
|
+
test("returns error for missing pattern", async () => {
|
|
604
|
+
const result = await GlobTool.handler({}, context);
|
|
605
|
+
expect(result.is_error).toBe(true);
|
|
606
|
+
expect(result.content).toContain("required");
|
|
607
|
+
});
|
|
608
|
+
test("returns error for empty pattern", async () => {
|
|
609
|
+
const result = await GlobTool.handler({ pattern: "" }, context);
|
|
610
|
+
expect(result.is_error).toBe(true);
|
|
611
|
+
expect(result.content).toContain("required");
|
|
612
|
+
});
|
|
613
|
+
test("returns error for non-existent directory", async () => {
|
|
614
|
+
const result = await GlobTool.handler({ pattern: "*.txt", path: "/nonexistent/path" }, context);
|
|
615
|
+
// Glob may succeed with empty results or fail depending on implementation
|
|
616
|
+
expect(result).toBeDefined();
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
describe("GrepTool", () => {
|
|
621
|
+
let tempDir;
|
|
622
|
+
let context;
|
|
623
|
+
beforeEach(async () => {
|
|
624
|
+
tempDir = await mkdtemp(join(tmpdir(), "grep-tool-test-"));
|
|
625
|
+
context = createToolContext(tempDir);
|
|
626
|
+
// Create test files
|
|
627
|
+
await writeFile(join(tempDir, "file1.txt"), "Hello World\nThis is a test\nAnother line");
|
|
628
|
+
await writeFile(join(tempDir, "file2.txt"), "Hello Universe\nDifferent content\nTest pattern here");
|
|
629
|
+
await writeFile(join(tempDir, "code.ts"), "const greeting = 'Hello';\nfunction test() { return 'Hello'; }");
|
|
630
|
+
await mkdir(join(tempDir, "subdir"));
|
|
631
|
+
await writeFile(join(tempDir, "subdir", "nested.txt"), "Nested Hello\nAnother test");
|
|
632
|
+
});
|
|
633
|
+
afterEach(async () => {
|
|
634
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
635
|
+
});
|
|
636
|
+
describe("successful operations", () => {
|
|
637
|
+
test("searches for pattern in directory", async () => {
|
|
638
|
+
const result = await GrepTool.handler({ pattern: "Hello" }, context);
|
|
639
|
+
expect(result.is_error).toBeUndefined();
|
|
640
|
+
expect(result.content).toContain("Hello");
|
|
641
|
+
});
|
|
642
|
+
test("searches for pattern in specific file", async () => {
|
|
643
|
+
const result = await GrepTool.handler({ pattern: "test", path: join(tempDir, "file1.txt") }, context);
|
|
644
|
+
// Note: GrepTool uses ripgrep which may require the path to be a directory
|
|
645
|
+
// when searching with --json flag. Check that the tool returns results.
|
|
646
|
+
// If path is a file, ripgrep may behave differently.
|
|
647
|
+
expect(result).toBeDefined();
|
|
648
|
+
});
|
|
649
|
+
test("searches case-insensitively with -i flag", async () => {
|
|
650
|
+
await writeFile(join(tempDir, "case.txt"), "HELLO world");
|
|
651
|
+
const result = await GrepTool.handler({ pattern: "hello", "-i:": true }, context);
|
|
652
|
+
expect(result.is_error).toBeUndefined();
|
|
653
|
+
expect(result.content).toContain("HELLO");
|
|
654
|
+
});
|
|
655
|
+
test("filters by glob pattern", async () => {
|
|
656
|
+
const result = await GrepTool.handler({ pattern: "Hello", glob: "*.ts" }, context);
|
|
657
|
+
expect(result.is_error).toBeUndefined();
|
|
658
|
+
expect(result.content).toContain("code.ts");
|
|
659
|
+
expect(result.content).not.toContain("file1.txt");
|
|
660
|
+
});
|
|
661
|
+
test("returns files_with_matches mode", async () => {
|
|
662
|
+
const result = await GrepTool.handler({ pattern: "Hello", output_mode: "files_with_matches" }, context);
|
|
663
|
+
expect(result.is_error).toBeUndefined();
|
|
664
|
+
// Should list files, not line content
|
|
665
|
+
});
|
|
666
|
+
test("returns count mode", async () => {
|
|
667
|
+
const result = await GrepTool.handler({ pattern: "Hello", output_mode: "count" }, context);
|
|
668
|
+
expect(result.is_error).toBeUndefined();
|
|
669
|
+
// Should return counts
|
|
670
|
+
});
|
|
671
|
+
test("respects head_limit", async () => {
|
|
672
|
+
const result = await GrepTool.handler({ pattern: "Hello", head_limit: 1 }, context);
|
|
673
|
+
expect(result.is_error).toBeUndefined();
|
|
674
|
+
// Should limit results
|
|
675
|
+
});
|
|
676
|
+
test("searches with regex pattern", async () => {
|
|
677
|
+
const result = await GrepTool.handler({ pattern: "H[a-z]+o" }, context);
|
|
678
|
+
expect(result.is_error).toBeUndefined();
|
|
679
|
+
expect(result.content).toContain("Hello");
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
describe("no matches", () => {
|
|
683
|
+
test("returns message when no matches found", async () => {
|
|
684
|
+
const result = await GrepTool.handler({ pattern: "NonExistentPatternXYZ123" }, context);
|
|
685
|
+
expect(result.is_error).toBeUndefined();
|
|
686
|
+
expect(result.content).toContain("No matches");
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
describe("error handling", () => {
|
|
690
|
+
test("returns error for missing pattern", async () => {
|
|
691
|
+
const result = await GrepTool.handler({}, context);
|
|
692
|
+
expect(result.is_error).toBe(true);
|
|
693
|
+
expect(result.content).toContain("required");
|
|
694
|
+
});
|
|
695
|
+
test("returns error for empty pattern", async () => {
|
|
696
|
+
const result = await GrepTool.handler({ pattern: "" }, context);
|
|
697
|
+
expect(result.is_error).toBe(true);
|
|
698
|
+
expect(result.content).toContain("required");
|
|
699
|
+
});
|
|
700
|
+
test("returns error for whitespace-only pattern", async () => {
|
|
701
|
+
const result = await GrepTool.handler({ pattern: " " }, context);
|
|
702
|
+
expect(result.is_error).toBe(true);
|
|
703
|
+
expect(result.content).toContain("required");
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
describe("Tool Integration", () => {
|
|
708
|
+
let tempDir;
|
|
709
|
+
let context;
|
|
710
|
+
beforeEach(async () => {
|
|
711
|
+
tempDir = await mkdtemp(join(tmpdir(), "tool-integration-test-"));
|
|
712
|
+
context = createToolContext(tempDir);
|
|
713
|
+
});
|
|
714
|
+
afterEach(async () => {
|
|
715
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
716
|
+
});
|
|
717
|
+
test("Write -> Read workflow", async () => {
|
|
718
|
+
const filePath = join(tempDir, "workflow.txt");
|
|
719
|
+
// Write
|
|
720
|
+
const writeResult = await WriteTool.handler({ file_path: filePath, content: "Test content" }, context);
|
|
721
|
+
expect(writeResult.is_error).toBeUndefined();
|
|
722
|
+
// Read
|
|
723
|
+
const readResult = await ReadTool.handler({ file_path: filePath }, context);
|
|
724
|
+
expect(readResult.is_error).toBeUndefined();
|
|
725
|
+
expect(readResult.content).toContain("Test content");
|
|
726
|
+
});
|
|
727
|
+
test("Write -> Edit -> Read workflow", async () => {
|
|
728
|
+
const filePath = join(tempDir, "edit-workflow.txt");
|
|
729
|
+
// Write
|
|
730
|
+
await WriteTool.handler({ file_path: filePath, content: "Original content" }, context);
|
|
731
|
+
// Edit
|
|
732
|
+
const editResult = await EditTool.handler({ file_path: filePath, old_string: "Original", new_string: "Modified" }, context);
|
|
733
|
+
expect(editResult.is_error).toBeUndefined();
|
|
734
|
+
// Read
|
|
735
|
+
const readResult = await ReadTool.handler({ file_path: filePath }, context);
|
|
736
|
+
expect(readResult.content).toContain("Modified content");
|
|
737
|
+
});
|
|
738
|
+
test("Bash -> Glob -> Read workflow", async () => {
|
|
739
|
+
// Create files via bash
|
|
740
|
+
await BashTool.handler({ command: "echo 'file content' > created.txt" }, context);
|
|
741
|
+
// Find file via glob
|
|
742
|
+
const globResult = await GlobTool.handler({ pattern: "*.txt" }, context);
|
|
743
|
+
expect(globResult.content).toContain("created.txt");
|
|
744
|
+
// Extract file path and read
|
|
745
|
+
const filePath = join(tempDir, "created.txt");
|
|
746
|
+
const readResult = await ReadTool.handler({ file_path: filePath }, context);
|
|
747
|
+
expect(readResult.content).toContain("file content");
|
|
748
|
+
});
|
|
749
|
+
test("Glob -> Grep workflow", async () => {
|
|
750
|
+
// Create multiple files
|
|
751
|
+
await writeFile(join(tempDir, "a.ts"), "export const X = 1;");
|
|
752
|
+
await writeFile(join(tempDir, "b.ts"), "export const Y = 2;");
|
|
753
|
+
await writeFile(join(tempDir, "c.js"), "const Z = 3;");
|
|
754
|
+
// Find TypeScript files
|
|
755
|
+
const globResult = await GlobTool.handler({ pattern: "*.ts" }, context);
|
|
756
|
+
expect(globResult.content).toContain("a.ts");
|
|
757
|
+
expect(globResult.content).toContain("b.ts");
|
|
758
|
+
expect(globResult.content).not.toContain("c.js");
|
|
759
|
+
// Search for export in TypeScript files only
|
|
760
|
+
const grepResult = await GrepTool.handler({ pattern: "export", glob: "*.ts" }, context);
|
|
761
|
+
expect(grepResult.content).toContain("export");
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
describe("Edge Cases", () => {
|
|
765
|
+
let tempDir;
|
|
766
|
+
let context;
|
|
767
|
+
beforeEach(async () => {
|
|
768
|
+
tempDir = await mkdtemp(join(tmpdir(), "edge-cases-test-"));
|
|
769
|
+
context = createToolContext(tempDir);
|
|
770
|
+
});
|
|
771
|
+
afterEach(async () => {
|
|
772
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
773
|
+
});
|
|
774
|
+
describe("Read edge cases", () => {
|
|
775
|
+
test("handles file with very long lines", async () => {
|
|
776
|
+
const filePath = join(tempDir, "long-line.txt");
|
|
777
|
+
await writeFile(filePath, "x".repeat(5000));
|
|
778
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
779
|
+
expect(result.is_error).toBeUndefined();
|
|
780
|
+
});
|
|
781
|
+
test("handles file with only newlines", async () => {
|
|
782
|
+
const filePath = join(tempDir, "newlines.txt");
|
|
783
|
+
await writeFile(filePath, "\n\n\n\n");
|
|
784
|
+
const result = await ReadTool.handler({ file_path: filePath }, context);
|
|
785
|
+
expect(result.is_error).toBeUndefined();
|
|
786
|
+
});
|
|
787
|
+
test("handles offset beyond file length", async () => {
|
|
788
|
+
const filePath = join(tempDir, "short.txt");
|
|
789
|
+
await writeFile(filePath, "Short file");
|
|
790
|
+
const result = await ReadTool.handler({ file_path: filePath, offset: 1000 }, context);
|
|
791
|
+
expect(result.is_error).toBeUndefined();
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
describe("Write edge cases", () => {
|
|
795
|
+
test("handles content with null bytes", async () => {
|
|
796
|
+
const filePath = join(tempDir, "null.txt");
|
|
797
|
+
const content = "Before\x00After";
|
|
798
|
+
const result = await WriteTool.handler({ file_path: filePath, content }, context);
|
|
799
|
+
expect(result.is_error).toBeUndefined();
|
|
800
|
+
});
|
|
801
|
+
test("handles very long file paths", async () => {
|
|
802
|
+
const longName = "a".repeat(200) + ".txt";
|
|
803
|
+
const filePath = join(tempDir, longName);
|
|
804
|
+
// This may fail on some filesystems
|
|
805
|
+
try {
|
|
806
|
+
const result = await WriteTool.handler({ file_path: filePath, content: "test" }, context);
|
|
807
|
+
// If it succeeds, great
|
|
808
|
+
expect(result).toBeDefined();
|
|
809
|
+
}
|
|
810
|
+
catch {
|
|
811
|
+
// If it fails due to filesystem limits, that's expected
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
describe("Edit edge cases", () => {
|
|
816
|
+
test("handles old_string at end of file", async () => {
|
|
817
|
+
const filePath = join(tempDir, "end.txt");
|
|
818
|
+
await writeFile(filePath, "Start End");
|
|
819
|
+
const result = await EditTool.handler({ file_path: filePath, old_string: "End", new_string: "Finish" }, context);
|
|
820
|
+
expect(result.is_error).toBeUndefined();
|
|
821
|
+
const file = Bun.file(filePath);
|
|
822
|
+
expect(await file.text()).toBe("Start Finish");
|
|
823
|
+
});
|
|
824
|
+
test("handles old_string at start of file", async () => {
|
|
825
|
+
const filePath = join(tempDir, "start.txt");
|
|
826
|
+
await writeFile(filePath, "Start End");
|
|
827
|
+
const result = await EditTool.handler({ file_path: filePath, old_string: "Start", new_string: "Beginning" }, context);
|
|
828
|
+
expect(result.is_error).toBeUndefined();
|
|
829
|
+
const file = Bun.file(filePath);
|
|
830
|
+
expect(await file.text()).toBe("Beginning End");
|
|
831
|
+
});
|
|
832
|
+
test("handles multi-line old_string", async () => {
|
|
833
|
+
const filePath = join(tempDir, "multi.txt");
|
|
834
|
+
await writeFile(filePath, "Line 1\nLine 2\nLine 3");
|
|
835
|
+
const result = await EditTool.handler({
|
|
836
|
+
file_path: filePath,
|
|
837
|
+
old_string: "Line 1\nLine 2",
|
|
838
|
+
new_string: "Replaced",
|
|
839
|
+
}, context);
|
|
840
|
+
expect(result.is_error).toBeUndefined();
|
|
841
|
+
const file = Bun.file(filePath);
|
|
842
|
+
expect(await file.text()).toBe("Replaced\nLine 3");
|
|
843
|
+
});
|
|
844
|
+
});
|
|
845
|
+
describe("Bash edge cases", () => {
|
|
846
|
+
test("handles command with quotes", async () => {
|
|
847
|
+
const result = await BashTool.handler({ command: 'echo "Hello \'World\'"' }, context);
|
|
848
|
+
expect(result.is_error).toBeUndefined();
|
|
849
|
+
expect(result.content).toContain("Hello");
|
|
850
|
+
});
|
|
851
|
+
test("handles command with backslashes", async () => {
|
|
852
|
+
const result = await BashTool.handler({ command: "echo 'test\\nvalue'" }, context);
|
|
853
|
+
expect(result.is_error).toBeUndefined();
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
});
|