PermissionRequest.tsx
1 import { useInput } from 'ink' 2 import * as React from 'react' 3 import { Tool } from '../../Tool.js' 4 import { AssistantMessage } from '../../query.js' 5 import { FileEditTool } from '../../tools/FileEditTool/FileEditTool.js' 6 import { FileWriteTool } from '../../tools/FileWriteTool/FileWriteTool.js' 7 import { BashTool } from '../../tools/BashTool/BashTool.js' 8 import { FileEditPermissionRequest } from './FileEditPermissionRequest/FileEditPermissionRequest.js' 9 import { BashPermissionRequest } from './BashPermissionRequest/BashPermissionRequest.js' 10 import { FallbackPermissionRequest } from './FallbackPermissionRequest.js' 11 import { useNotifyAfterTimeout } from '../../hooks/useNotifyAfterTimeout.js' 12 import { FileWritePermissionRequest } from './FileWritePermissionRequest/FileWritePermissionRequest.js' 13 import { type CommandSubcommandPrefixResult } from '../../utils/commands.js' 14 import { FilesystemPermissionRequest } from './FilesystemPermissionRequest/FilesystemPermissionRequest.js' 15 import { NotebookEditTool } from '../../tools/NotebookEditTool/NotebookEditTool.js' 16 import { GlobTool } from '../../tools/GlobTool/GlobTool.js' 17 import { GrepTool } from '../../tools/GrepTool/GrepTool.js' 18 import { LSTool } from '../../tools/lsTool/lsTool.js' 19 import { FileReadTool } from '../../tools/FileReadTool/FileReadTool.js' 20 import { NotebookReadTool } from '../../tools/NotebookReadTool/NotebookReadTool.js' 21 22 function permissionComponentForTool(tool: Tool) { 23 switch (tool) { 24 case FileEditTool: 25 return FileEditPermissionRequest 26 case FileWriteTool: 27 return FileWritePermissionRequest 28 case BashTool: 29 return BashPermissionRequest 30 case GlobTool: 31 case GrepTool: 32 case LSTool: 33 case FileReadTool: 34 case NotebookReadTool: 35 case NotebookEditTool: 36 return FilesystemPermissionRequest 37 default: 38 return FallbackPermissionRequest 39 } 40 } 41 42 export type PermissionRequestProps = { 43 toolUseConfirm: ToolUseConfirm 44 onDone(): void 45 verbose: boolean 46 } 47 48 export function toolUseConfirmGetPrefix( 49 toolUseConfirm: ToolUseConfirm, 50 ): string | null { 51 return ( 52 (toolUseConfirm.commandPrefix && 53 !toolUseConfirm.commandPrefix.commandInjectionDetected && 54 toolUseConfirm.commandPrefix.commandPrefix) || 55 null 56 ) 57 } 58 59 export type ToolUseConfirm = { 60 assistantMessage: AssistantMessage 61 tool: Tool 62 description: string 63 input: { [key: string]: unknown } 64 commandPrefix: CommandSubcommandPrefixResult | null 65 // TODO: remove riskScore from ToolUseConfirm 66 riskScore: number | null 67 onAbort(): void 68 onAllow(type: 'permanent' | 'temporary'): void 69 onReject(): void 70 } 71 72 // TODO: Move this to Tool.renderPermissionRequest 73 export function PermissionRequest({ 74 toolUseConfirm, 75 onDone, 76 verbose, 77 }: PermissionRequestProps): React.ReactNode { 78 // Handle Ctrl+C 79 useInput((input, key) => { 80 if (key.ctrl && input === 'c') { 81 onDone() 82 toolUseConfirm.onReject() 83 } 84 }) 85 86 const toolName = toolUseConfirm.tool.userFacingName( 87 toolUseConfirm.input as never, 88 ) 89 useNotifyAfterTimeout(`Claude needs your permission to use ${toolName}`) 90 91 const PermissionComponent = permissionComponentForTool(toolUseConfirm.tool) 92 93 return ( 94 <PermissionComponent 95 toolUseConfirm={toolUseConfirm} 96 onDone={onDone} 97 verbose={verbose} 98 /> 99 ) 100 }