/ src / components / permissions / PermissionRequest.tsx
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  }