@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,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook System - Lifecycle event handlers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { HookEvent, HookDefinition, HookInput, HookOutput } from "../../types/index.js";
|
|
6
|
+
import { spawn } from "child_process";
|
|
7
|
+
|
|
8
|
+
export type HookHandler = (input: HookInput) => Promise<HookOutput>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Prompt evaluator function type - calls LLM to evaluate hook prompt
|
|
12
|
+
*/
|
|
13
|
+
export type PromptEvaluator = (prompt: string, context: HookInput) => Promise<HookOutput>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extended hook definition that supports shell commands, in-process handlers, and LLM prompts
|
|
17
|
+
*/
|
|
18
|
+
export interface ExtendedHookDefinition extends HookDefinition {
|
|
19
|
+
/** In-process handler function (alternative to command) */
|
|
20
|
+
handler?: HookHandler;
|
|
21
|
+
/** Prompt template for LLM-based evaluation (alternative to command) */
|
|
22
|
+
prompt?: string;
|
|
23
|
+
/** Matcher pattern for filtering which tools this hook applies to */
|
|
24
|
+
_matcher?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class HookManager {
|
|
28
|
+
private hooks = new Map<HookEvent, ExtendedHookDefinition[]>();
|
|
29
|
+
private timeout: number;
|
|
30
|
+
private promptEvaluator?: PromptEvaluator;
|
|
31
|
+
|
|
32
|
+
constructor(timeout = 60000, promptEvaluator?: PromptEvaluator) {
|
|
33
|
+
this.timeout = timeout;
|
|
34
|
+
this.promptEvaluator = promptEvaluator;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set the prompt evaluator for LLM-based hooks
|
|
39
|
+
*/
|
|
40
|
+
setPromptEvaluator(evaluator: PromptEvaluator): void {
|
|
41
|
+
this.promptEvaluator = evaluator;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
register(event: HookEvent, definition: HookDefinition | ExtendedHookDefinition): void {
|
|
45
|
+
if (!this.hooks.has(event)) {
|
|
46
|
+
this.hooks.set(event, []);
|
|
47
|
+
}
|
|
48
|
+
this.hooks.get(event)?.push(definition as ExtendedHookDefinition);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Register an in-process handler for an event
|
|
53
|
+
*/
|
|
54
|
+
registerHandler(event: HookEvent, handler: HookHandler, options?: { timeout?: number; enabled?: boolean }): void {
|
|
55
|
+
this.register(event, {
|
|
56
|
+
event,
|
|
57
|
+
command: "", // Not used for in-process handlers
|
|
58
|
+
handler,
|
|
59
|
+
timeout: options?.timeout,
|
|
60
|
+
enabled: options?.enabled ?? true,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
registerAll(hooks: Record<HookEvent, HookDefinition[]>): void {
|
|
65
|
+
for (const [event, definitions] of Object.entries(hooks)) {
|
|
66
|
+
for (const def of definitions) {
|
|
67
|
+
this.register(event as HookEvent, def);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async execute(event: HookEvent, input: Omit<HookInput, "event" | "timestamp">): Promise<HookOutput> {
|
|
73
|
+
const definitions = this.hooks.get(event);
|
|
74
|
+
if (!definitions || definitions.length === 0) {
|
|
75
|
+
return { decision: "allow" };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const fullInput: HookInput = {
|
|
79
|
+
...input,
|
|
80
|
+
event,
|
|
81
|
+
timestamp: Date.now(),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
for (const def of definitions) {
|
|
85
|
+
if (def.enabled === false) continue;
|
|
86
|
+
|
|
87
|
+
// Check matcher if present
|
|
88
|
+
if (def._matcher && input.tool_name) {
|
|
89
|
+
try {
|
|
90
|
+
const regex = new RegExp(def._matcher);
|
|
91
|
+
if (!regex.test(input.tool_name)) {
|
|
92
|
+
// Matcher doesn't match, skip this hook
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
// Invalid regex, skip
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = await this.executeHook(def, fullInput);
|
|
102
|
+
|
|
103
|
+
if (result.decision === "deny" || result.decision === "block") {
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Apply modified input if provided
|
|
108
|
+
if (result.modified_input) {
|
|
109
|
+
Object.assign(input, result.modified_input);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { decision: "allow" };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async executeHook(def: ExtendedHookDefinition, input: HookInput): Promise<HookOutput> {
|
|
117
|
+
// If handler function is provided, use it directly
|
|
118
|
+
if (def.handler) {
|
|
119
|
+
try {
|
|
120
|
+
return await def.handler(input);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
123
|
+
return {
|
|
124
|
+
decision: "deny",
|
|
125
|
+
reason: `Hook handler error: ${errorMessage}`,
|
|
126
|
+
errors: [errorMessage],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// If prompt is provided, use LLM evaluation
|
|
132
|
+
if (def.prompt && this.promptEvaluator) {
|
|
133
|
+
try {
|
|
134
|
+
return await this.promptEvaluator(def.prompt, input);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
137
|
+
return {
|
|
138
|
+
decision: "deny",
|
|
139
|
+
reason: `Prompt hook error: ${errorMessage}`,
|
|
140
|
+
errors: [errorMessage],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Skip hooks with empty commands (e.g., prompt-type hooks without evaluator)
|
|
146
|
+
if (!def.command || def.command.trim() === "") {
|
|
147
|
+
// Allow the operation to proceed if there's no command to execute
|
|
148
|
+
return { decision: "allow" };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Shell command execution
|
|
152
|
+
const timeout = def.timeout || this.timeout;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const result = await new Promise<HookOutput>((resolve, reject) => {
|
|
156
|
+
const proc = spawn(def.command, [], {
|
|
157
|
+
shell: true,
|
|
158
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let stdout = "";
|
|
162
|
+
let stderr = "";
|
|
163
|
+
let timedOut = false;
|
|
164
|
+
|
|
165
|
+
const timer = setTimeout(() => {
|
|
166
|
+
timedOut = true;
|
|
167
|
+
proc.kill();
|
|
168
|
+
resolve({
|
|
169
|
+
decision: "deny",
|
|
170
|
+
reason: "Hook timeout",
|
|
171
|
+
errors: ["Hook execution timed out"],
|
|
172
|
+
});
|
|
173
|
+
}, timeout);
|
|
174
|
+
|
|
175
|
+
proc.stdout?.on("data", (data: Buffer) => {
|
|
176
|
+
stdout += data.toString();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
proc.stderr?.on("data", (data: Buffer) => {
|
|
180
|
+
stderr += data.toString();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
proc.on("close", (code: number | null) => {
|
|
184
|
+
clearTimeout(timer);
|
|
185
|
+
if (timedOut) return; // Already resolved
|
|
186
|
+
|
|
187
|
+
// Try to parse JSON from stdout first (works for all exit codes)
|
|
188
|
+
let parsedOutput: HookOutput | null = null;
|
|
189
|
+
if (stdout.trim()) {
|
|
190
|
+
try {
|
|
191
|
+
parsedOutput = JSON.parse(stdout) as HookOutput;
|
|
192
|
+
} catch {
|
|
193
|
+
// stdout not valid JSON, ignore
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (code === 0) {
|
|
198
|
+
// Success - use parsed output or default to allow
|
|
199
|
+
resolve(parsedOutput || { decision: "allow" });
|
|
200
|
+
} else if (code === 1) {
|
|
201
|
+
// Deny - use parsed output or fall back to stderr
|
|
202
|
+
if (parsedOutput) {
|
|
203
|
+
resolve(parsedOutput);
|
|
204
|
+
} else {
|
|
205
|
+
resolve({
|
|
206
|
+
decision: "deny",
|
|
207
|
+
reason: stderr || "Hook denied execution",
|
|
208
|
+
errors: stderr ? [stderr] : ["Hook denied execution"],
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
} else if (code === 2) {
|
|
212
|
+
// Block - use parsed output or fall back to stderr
|
|
213
|
+
if (parsedOutput) {
|
|
214
|
+
resolve(parsedOutput);
|
|
215
|
+
} else {
|
|
216
|
+
resolve({
|
|
217
|
+
decision: "block",
|
|
218
|
+
reason: stderr || "Hook blocked execution",
|
|
219
|
+
errors: stderr ? [stderr] : ["Hook blocked execution"],
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// Other error - default to deny
|
|
224
|
+
resolve({
|
|
225
|
+
decision: "deny",
|
|
226
|
+
reason: `Hook exited with code ${code}`,
|
|
227
|
+
errors: [`Hook exited with code ${code}`],
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
proc.on("error", (error: Error) => {
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
resolve({
|
|
235
|
+
decision: "deny",
|
|
236
|
+
reason: `Hook error: ${error.message}`,
|
|
237
|
+
errors: [error.message],
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Send input via stdin
|
|
242
|
+
proc.stdin?.write(JSON.stringify(input));
|
|
243
|
+
proc.stdin?.end();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
249
|
+
return {
|
|
250
|
+
decision: "deny",
|
|
251
|
+
reason: `Hook execution failed: ${errorMessage}`,
|
|
252
|
+
errors: [errorMessage],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getHooks(event: HookEvent): ExtendedHookDefinition[] {
|
|
258
|
+
return this.hooks.get(event) || [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
clear(event?: HookEvent): void {
|
|
262
|
+
if (event) {
|
|
263
|
+
this.hooks.delete(event);
|
|
264
|
+
} else {
|
|
265
|
+
this.hooks.clear();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ============================================
|
|
271
|
+
// BUILT-IN HOOKS
|
|
272
|
+
// ============================================
|
|
273
|
+
|
|
274
|
+
export const builtInHooks: Record<string, HookDefinition> = {
|
|
275
|
+
/**
|
|
276
|
+
* Example: Validate file paths before write
|
|
277
|
+
*/
|
|
278
|
+
validateWrite: {
|
|
279
|
+
event: "PreToolUse",
|
|
280
|
+
command: `node -e '
|
|
281
|
+
const input = JSON.parse(require("fs").readFileSync(0, "utf8"));
|
|
282
|
+
if (input.tool_name === "Write") {
|
|
283
|
+
const path = input.tool_input.file_path;
|
|
284
|
+
if (path.includes("..") || path.startsWith("/etc/")) {
|
|
285
|
+
console.log(JSON.stringify({ decision: "deny", reason: "Unsafe path" }));
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
console.log(JSON.stringify({ decision: "allow" }));
|
|
290
|
+
'`,
|
|
291
|
+
timeout: 5000,
|
|
292
|
+
enabled: false,
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Example: Log all tool uses
|
|
297
|
+
*/
|
|
298
|
+
logToolUse: {
|
|
299
|
+
event: "PostToolUse",
|
|
300
|
+
command: `node -e '
|
|
301
|
+
const input = JSON.parse(require("fs").readFileSync(0, "utf8"));
|
|
302
|
+
console.error(\`[LOG] Tool: \${input.tool_name}\`);
|
|
303
|
+
console.log(JSON.stringify({ decision: "allow" }));
|
|
304
|
+
'`,
|
|
305
|
+
timeout: 5000,
|
|
306
|
+
enabled: false,
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// ============================================
|
|
311
|
+
// HOOK EVENT DOCUMENTATION
|
|
312
|
+
// ============================================
|
|
313
|
+
|
|
314
|
+
export const hookEventDocs: Record<HookEvent, string> = {
|
|
315
|
+
PreToolUse: "Before a tool is executed. Can modify input or deny execution.",
|
|
316
|
+
PostToolUse: "After a tool successfully executes. Can process result.",
|
|
317
|
+
PostToolUseFailure: "After a tool fails. Can handle error or retry.",
|
|
318
|
+
Stop: "When the agent stops (end_turn, max_tokens, error).",
|
|
319
|
+
UserPromptSubmit: "When user submits a prompt. Can modify or reject.",
|
|
320
|
+
SessionStart: "When a new session starts.",
|
|
321
|
+
SessionEnd: "When a session ends.",
|
|
322
|
+
Notification: "When a notification is sent.",
|
|
323
|
+
ConfigChange: "When configuration changes.",
|
|
324
|
+
WorktreeCreate: "When a git worktree is created.",
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Exit codes for hook commands:
|
|
329
|
+
* 0 = Success, allow execution
|
|
330
|
+
* 1 = Show stderr, deny execution
|
|
331
|
+
* 2 = Block execution silently
|
|
332
|
+
*/
|
|
333
|
+
export const hookExitCodes = {
|
|
334
|
+
ALLOW: 0,
|
|
335
|
+
DENY: 1,
|
|
336
|
+
BLOCK: 2,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// Re-export prompt evaluator utilities
|
|
340
|
+
export { createPromptEvaluator, createMockPromptEvaluator } from "./prompt-evaluator.js";
|
|
341
|
+
export type { PromptEvaluatorOptions } from "./prompt-evaluator.js";
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Evaluator - LLM-based hook evaluation
|
|
3
|
+
*
|
|
4
|
+
* Evaluates hook prompts by calling the Claude API to make decisions
|
|
5
|
+
* about tool execution based on natural language rules.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { HookInput, HookOutput } from "../../types/index.js";
|
|
9
|
+
import type { PromptEvaluator } from "./index.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options for creating a prompt evaluator
|
|
13
|
+
*/
|
|
14
|
+
export interface PromptEvaluatorOptions {
|
|
15
|
+
/** Anthropic API key */
|
|
16
|
+
apiKey: string;
|
|
17
|
+
/** Model to use for evaluation (default: claude-haiku-4-5 for speed) */
|
|
18
|
+
model?: string;
|
|
19
|
+
/** Max tokens for response (default: 256) */
|
|
20
|
+
maxTokens?: number;
|
|
21
|
+
/** System prompt for the evaluator */
|
|
22
|
+
systemPrompt?: string;
|
|
23
|
+
/** Base URL for API (default: from ANTHROPIC_BASE_URL env or https://api.anthropic.com) */
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Default system prompt for hook evaluation
|
|
29
|
+
*/
|
|
30
|
+
const DEFAULT_SYSTEM_PROMPT = `You are a hook evaluator for Coder. Your job is to evaluate tool usage against security and best-practice rules.
|
|
31
|
+
|
|
32
|
+
You MUST respond with ONLY a valid JSON object in this exact format:
|
|
33
|
+
{
|
|
34
|
+
"decision": "allow" | "deny" | "block",
|
|
35
|
+
"reason": "Optional explanation for the decision",
|
|
36
|
+
"modified_input": { ... } // Optional: modified tool input if you want to change it
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Rules:
|
|
40
|
+
- "allow": Let the tool execute normally
|
|
41
|
+
- "deny": Prevent execution and show the user the reason
|
|
42
|
+
- "block": Prevent execution silently (no message shown)
|
|
43
|
+
- Only modify input if absolutely necessary and you're certain of the correct format
|
|
44
|
+
|
|
45
|
+
Be concise and make quick decisions. Do not explain your reasoning in prose - only use the JSON format.`;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Interpolate variables in a prompt template
|
|
49
|
+
*/
|
|
50
|
+
function interpolatePrompt(template: string, input: HookInput): string {
|
|
51
|
+
let result = template;
|
|
52
|
+
|
|
53
|
+
// Replace $ARGUMENTS with tool input as JSON
|
|
54
|
+
if (input.tool_input) {
|
|
55
|
+
result = result.replace(/\$ARGUMENTS/g, JSON.stringify(input.tool_input, null, 2));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Replace $TOOL_NAME with tool name
|
|
59
|
+
if (input.tool_name) {
|
|
60
|
+
result = result.replace(/\$TOOL_NAME/g, input.tool_name);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Replace $EVENT with event name
|
|
64
|
+
result = result.replace(/\$EVENT/g, input.event);
|
|
65
|
+
|
|
66
|
+
// Replace $SESSION_ID with session ID
|
|
67
|
+
if (input.session_id) {
|
|
68
|
+
result = result.replace(/\$SESSION_ID/g, input.session_id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Replace $TIMESTAMP with timestamp
|
|
72
|
+
result = result.replace(/\$TIMESTAMP/g, String(input.timestamp));
|
|
73
|
+
|
|
74
|
+
// Replace $ERROR with error message if present
|
|
75
|
+
if (input.error) {
|
|
76
|
+
result = result.replace(/\$ERROR/g, input.error);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Replace $TOOL_RESULT with tool result if present
|
|
80
|
+
if (input.tool_result) {
|
|
81
|
+
const resultStr = typeof input.tool_result.content === "string"
|
|
82
|
+
? input.tool_result.content
|
|
83
|
+
: JSON.stringify(input.tool_result.content);
|
|
84
|
+
result = result.replace(/\$TOOL_RESULT/g, resultStr);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Parse the LLM response into a HookOutput
|
|
92
|
+
* Supports multiple JSON formats:
|
|
93
|
+
* 1. Standard: { "decision": "allow" | "deny" | "block", "reason": "..." }
|
|
94
|
+
* 2. Standard format: { "continue": true/false, "hookSpecificOutput": {...} }
|
|
95
|
+
*/
|
|
96
|
+
function parseHookOutput(responseText: string): HookOutput {
|
|
97
|
+
// Try to extract JSON from the response
|
|
98
|
+
// The model might wrap it in markdown code blocks or add extra text
|
|
99
|
+
|
|
100
|
+
let parsed: Record<string, unknown> | null = null;
|
|
101
|
+
|
|
102
|
+
// First, try direct parse
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(responseText) as Record<string, unknown>;
|
|
105
|
+
} catch {
|
|
106
|
+
// Not valid JSON directly
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Try to extract JSON from markdown code block
|
|
110
|
+
if (!parsed) {
|
|
111
|
+
const jsonBlockMatch = responseText.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
112
|
+
if (jsonBlockMatch?.[1]) {
|
|
113
|
+
try {
|
|
114
|
+
parsed = JSON.parse(jsonBlockMatch[1].trim()) as Record<string, unknown>;
|
|
115
|
+
} catch {
|
|
116
|
+
// Invalid JSON in code block
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Try to find JSON object anywhere in the response
|
|
122
|
+
if (!parsed) {
|
|
123
|
+
const jsonMatch = responseText.match(/\{[\s\S]*?\}/);
|
|
124
|
+
if (jsonMatch) {
|
|
125
|
+
try {
|
|
126
|
+
parsed = JSON.parse(jsonMatch[0]) as Record<string, unknown>;
|
|
127
|
+
} catch {
|
|
128
|
+
// Invalid JSON
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!parsed) {
|
|
134
|
+
// If we can't parse JSON, default to allow with a warning
|
|
135
|
+
return {
|
|
136
|
+
decision: "allow",
|
|
137
|
+
reason: "Hook response could not be parsed as JSON, allowing by default",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Format 1: Standard { decision: "allow" | "deny" | "block" }
|
|
142
|
+
if ("decision" in parsed) {
|
|
143
|
+
return {
|
|
144
|
+
decision: (parsed.decision as "allow" | "deny" | "block") || "allow",
|
|
145
|
+
reason: parsed.reason as string | undefined,
|
|
146
|
+
modified_input: parsed.modified_input as Record<string, unknown> | undefined,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Format 2: Standard { continue: true/false, hookSpecificOutput: {...} }
|
|
151
|
+
if ("continue" in parsed) {
|
|
152
|
+
const shouldContinue = Boolean(parsed.continue);
|
|
153
|
+
if (shouldContinue) {
|
|
154
|
+
return { decision: "allow" };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Extract reason from hookSpecificOutput if present
|
|
158
|
+
const hookOutput = parsed.hookSpecificOutput as Record<string, unknown> | undefined;
|
|
159
|
+
const reason = hookOutput?.permissionDecisionReason as string | undefined;
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
decision: "deny",
|
|
163
|
+
reason: reason || "Hook denied execution",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Unknown format, default to allow
|
|
168
|
+
return {
|
|
169
|
+
decision: "allow",
|
|
170
|
+
reason: "Unknown hook response format, allowing by default",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Make a non-streaming API call to Claude
|
|
176
|
+
*/
|
|
177
|
+
async function callClaudeAPI(
|
|
178
|
+
apiKey: string,
|
|
179
|
+
model: string,
|
|
180
|
+
maxTokens: number,
|
|
181
|
+
systemPrompt: string,
|
|
182
|
+
userPrompt: string,
|
|
183
|
+
baseUrl: string = "https://api.anthropic.com"
|
|
184
|
+
): Promise<string> {
|
|
185
|
+
const response = await fetch(`${baseUrl}/v1/messages`, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: {
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
"x-api-key": apiKey,
|
|
190
|
+
"anthropic-version": "2023-06-01",
|
|
191
|
+
},
|
|
192
|
+
body: JSON.stringify({
|
|
193
|
+
model,
|
|
194
|
+
max_tokens: maxTokens,
|
|
195
|
+
system: systemPrompt,
|
|
196
|
+
messages: [
|
|
197
|
+
{
|
|
198
|
+
role: "user",
|
|
199
|
+
content: userPrompt,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
}),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (!response.ok) {
|
|
206
|
+
const errorText = await response.text();
|
|
207
|
+
throw new Error(`API error: ${response.status} - ${errorText}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const data = await response.json() as {
|
|
211
|
+
content: Array<{ type: string; text?: string }>;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// Extract text from response
|
|
215
|
+
const textContent = data.content
|
|
216
|
+
.filter((block) => block.type === "text")
|
|
217
|
+
.map((block) => block.text || "")
|
|
218
|
+
.join("");
|
|
219
|
+
|
|
220
|
+
return textContent;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create a prompt evaluator that uses the Claude API
|
|
225
|
+
*/
|
|
226
|
+
export function createPromptEvaluator(options: PromptEvaluatorOptions): PromptEvaluator {
|
|
227
|
+
const {
|
|
228
|
+
apiKey,
|
|
229
|
+
model = "claude-haiku-4-5", // Use Haiku for fast evaluation
|
|
230
|
+
maxTokens = 256,
|
|
231
|
+
systemPrompt = DEFAULT_SYSTEM_PROMPT,
|
|
232
|
+
baseUrl = process.env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
|
|
233
|
+
} = options;
|
|
234
|
+
|
|
235
|
+
return async (promptTemplate: string, context: HookInput): Promise<HookOutput> => {
|
|
236
|
+
// Interpolate variables in the prompt
|
|
237
|
+
const userPrompt = interpolatePrompt(promptTemplate, context);
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
// Call the API
|
|
241
|
+
const responseText = await callClaudeAPI(
|
|
242
|
+
apiKey,
|
|
243
|
+
model,
|
|
244
|
+
maxTokens,
|
|
245
|
+
systemPrompt,
|
|
246
|
+
userPrompt,
|
|
247
|
+
baseUrl
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
if (!responseText) {
|
|
251
|
+
return { decision: "allow" };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Parse the response
|
|
255
|
+
return parseHookOutput(responseText);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
258
|
+
// On error, allow by default but log the issue
|
|
259
|
+
console.error(`[PromptEvaluator] Error evaluating hook: ${errorMessage}`);
|
|
260
|
+
return {
|
|
261
|
+
decision: "allow",
|
|
262
|
+
reason: `Hook evaluation failed: ${errorMessage}`,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Create a mock prompt evaluator for testing
|
|
270
|
+
* Returns canned responses based on patterns in the prompt
|
|
271
|
+
*/
|
|
272
|
+
export function createMockPromptEvaluator(): PromptEvaluator {
|
|
273
|
+
return async (promptTemplate: string, context: HookInput): Promise<HookOutput> => {
|
|
274
|
+
// Simple mock logic for testing
|
|
275
|
+
const prompt = promptTemplate.toLowerCase();
|
|
276
|
+
const toolInput = context.tool_input;
|
|
277
|
+
|
|
278
|
+
// If checking for MCP alternatives and we have a Bash command
|
|
279
|
+
if (prompt.includes("mcp") && toolInput && "command" in toolInput) {
|
|
280
|
+
const command = String(toolInput.command);
|
|
281
|
+
|
|
282
|
+
// Check for common patterns that have MCP alternatives
|
|
283
|
+
if (command.includes("git status")) {
|
|
284
|
+
return {
|
|
285
|
+
decision: "deny",
|
|
286
|
+
reason: "Consider using the Git MCP tool instead of running git commands directly",
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (command.includes("gh ")) {
|
|
290
|
+
return {
|
|
291
|
+
decision: "deny",
|
|
292
|
+
reason: "Consider using the GitHub MCP tool instead of gh CLI",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Default allow
|
|
298
|
+
return { decision: "allow" };
|
|
299
|
+
};
|
|
300
|
+
}
|