Skip to content

Herramientas de comando y lectura de archivos: ExecuteCommand / ReadFile / SearchFiles / ListFiles

源码版本v4.0.10

Responsabilidades

Esta página cubre las cuatro herramientas «de capacidad» de solo lectura/ejecución de Cline, todas registradas bajo la interfaz IFullyManagedTool y despachadas por ToolExecutor:

  • ExecuteCommandToolHandler (ClineDefaultTool.BASH): corre un comando shell en la terminal, con timeout, multi-workspace y validación de permisos (class declaration:58).
  • ReadFileToolHandler (ClineDefaultTool.FILE_READ): lee un único archivo, añade números de línea 1-based, soporta slicing por start_line/end_line y devuelve image block para archivos de imagen (class declaration:142).
  • SearchFilesToolHandler (ClineDefaultTool.SEARCH): búsqueda por regex basada en ripgrep, soporta búsqueda paralela entre múltiples workspaces y filtro por file_pattern (class declaration:21).
  • ListFilesToolHandler (ClineDefaultTool.LIST_FILES): lista directorios, soporta recursión y trunca a las 200 entradas (class declaration:17).

Estas cuatro comparten un patrón de interacción: handlePartialBlock empuja UI temprano para que el usuario vea «el modelo está leyendo X», execute hace validación (clineignore + rutas + parámetros) → aprobación → PreToolUse hook → ejecución real → telemetría. Todas respetan config.isSubagentExecution: en ejecución de subagent omiten parte del UI push y de ask, y simplemente trabajan.

La diferencia fundamental con WriteToFileToolHandler es «solo lectura o efecto secundario externo»: leer, listar y buscar no cambian el estado del filesystem; execute_command sí cambia estado pero va por terminal, no entra en la vista diff, así que estas herramientas no tienen ruta de retorno diff / userEdits.

Motivación de diseño

  • Regla unificada de mistake counter: cada herramienta hace consecutiveMistakeCount++ cuando falta parámetro o clineignore coincide, y = 0 tras una operación exitosa, de modo que el límite de errores consecutivos en modo YOLO se acumula entre herramientas (reset counter:377).
  • Timeout clasificado por comando: execute_command detecta comandos largos comunes (npm install, cargo build, pytest, etc.) y les asigna 300 s; los demás 30 s, evitando que comandos cortos sufran timeouts largos (LONG_RUNNING patterns:22-34).
  • Doble verificación de permisos para comandos: lista blanca por segmento vía CLINE_COMMAND_PERMISSIONS + validación de rutas con clineignore, dos capas que rechazan de forma independiente (permission checks:162-190).
  • Caché de deduplicación de lectura de archivos: fileReadCache solo guarda metadatos (readCount + mtime + imageBlock), no contenido; si hay hit y mtime no cambió, devuelve un aviso de «ya leído», evitando que el modelo queme tokens releyendo el mismo archivo (dedup cache:298-314).
  • Presentación con números de línea: formatFileContentWithLineNumbers añade un prefijo N | a cada línea y un mensaje de continuación, para que el modelo use start_line y continúe con precisión (formatFileContentWithLineNumbers:79).
  • Búsqueda paralela multi-workspace: en modo multi-workspace, SearchFilesToolHandler lanza todos los searchPaths en paralelo con Promise.all y luego agrupa por nombre de workspace (parallel search:279-285).
  • Tope de 200 en ListFiles: listFiles recibe el tercer parámetro limit=200; al superarlo devuelve didHitLimit=true, y formatFilesList añade un aviso para que el modelo afine con path más específico (listFiles call:100).

Archivos clave

  • LONG_RUNNING patterns:22-34 — arreglo de regex para comandos largos: npm/cargo/pytest/docker build/torchrun, etc.
  • timeout resolution:36-56isLikelyLongRunningCommand + resolveCommandTimeoutSeconds; prioriza el parámetro timeout que pasa el modelo.
  • ExecuteCommand class:58name = ClineDefaultTool.BASH; handlePartialBlock espera al block completo en auto-approve antes de say.
  • workspace hint:135-159 — parsea el prefijo @workspace:command y cambia executionDir a la raíz del workspace correspondiente.
  • permission checks:162-190commandPermissionController.validateCommand + clineIgnoreController.validateCommand.
  • approval flow:223-279 — auto-approve requiere !requiresApprovalPerLLM && autoApproveSafe o, para comandos risky, autoApproveSafe && autoApproveAll (doble verificación).
  • cache clear:319-327 — tras ejecutar el comando, fileReadCache.clear() completo, porque sed/git checkout/mv, etc., pueden modificar archivos de forma impredecible.
  • formatFileContentWithLineNumbers:79 — slicing, números de línea, mensaje «Showing lines X-Y of Z».
  • ReadFile class:142name = ClineDefaultTool.FILE_READ.
  • dedup cache:298-356 — comparación mtime + acumulación readCount; a partir de la 3.ª vez devuelve aviso DUPLICATE READ.
  • extract + cache:359-373 — llama extractFileContent (las imágenes devuelven imageBlock); en fallo devuelve tool error en vez de lanzar al nivel superior.
  • determineSearchPaths:35 — en multi-workspace expande las rutas de búsqueda según hint o a todos los workspaces.
  • executeSearch:74 — llama regexSearchFiles (ripgrep), filtra resultados con clineIgnoreController.
  • formatSearchResults:120 — en multi-workspace agrupa por nombre; en workspace único devuelve directo.
  • listFiles call:100listFiles(absolutePath, recursive, 200) devuelve [files, didHitLimit].

Flujo de datos

ExecuteCommand es el más complejo de los cuatro; el núcleo es la puerta de aprobación y la resolución de timeout. Los campos requires_approval y command que da el modelo deciden qué ruta de aprobación tomar:

typescript
// apps/vscode/src/core/task/tools/handlers/ExecuteCommandToolHandler.ts
timeoutSeconds = resolveCommandTimeoutSeconds(
    command,
    timeoutParam,
    config.yoloModeToggled || config.vscodeTerminalExecutionMode === "backgroundExec",
)
// ...
if (
    config.isSubagentExecution ||
    (!requiresApprovalPerLLM && autoApproveSafe) ||
    (requiresApprovalPerLLM && autoApproveSafe && autoApproveAll)
) {
    // Auto-approve flow
    await config.callbacks.removeLastPartialMessageIfExistsWithType("ask", "command")
    await config.callbacks.say("command", actualCommand, undefined, undefined, false)
    didAutoApprove = true
} else {
    // Manual approval flow
    const didApprove = await ToolResultUtils.askApprovalAndPushFeedback(
        "command",
        actualCommand + `${autoApproveSafe && requiresApprovalPerLLM ? COMMAND_REQ_APP_STRING : ""}`,
        config,
    )
    if (!didApprove) {
        return formatResponse.toolDenied()
    }
}

autoApprover.shouldAutoApproveTool devuelve la tupla [autoApproveSafe, autoApproveAll]: el primero es «auto-aprobar comandos safe», el segundo «auto-aprobar todos los comandos». Si el modelo marca risky (requires_approval=true) y solo se configuró auto-aprobar safe, va por aprobación manual y se añade al comando la cadena de fuerte aprobación (auto-approve logic:223-227). Tras aprobar, PreToolUse hook, luego un timer de notificación a 30 s (solo en auto-approve + notificaciones activadas), y finalmente executeCommandTool(finalCommand, timeoutSeconds) corre en terminal. Al terminar, todo fileReadCache se limpia.

El núcleo de ReadFileToolHandler es la caché de deduplicación:

typescript
// apps/vscode/src/core/task/tools/handlers/ReadFileToolHandler.ts
const cacheKey = absolutePath.toLowerCase()
const cached = config.taskState.fileReadCache.get(cacheKey)
if (cached) {
    try {
        const stat = await import("node:fs/promises").then((fs) => fs.stat(absolutePath))
        if (stat.mtimeMs !== cached.mtime) {
            config.taskState.fileReadCache.delete(cacheKey)
        }
    } catch {
        config.taskState.fileReadCache.delete(cacheKey)
    }
}

Si mtime cambió (el usuario editó a mano en el editor), se evict; si no, se reutiliza la caché. La caché no guarda contenido para ahorrar memoria; tras hit, se relee del disco. Cuando readCount llega a 3 o más, se devuelve un fuerte aviso DUPLICATE READ. Tras una lectura nueva exitosa, extractFileContent obtiene el contenido (las imágenes devuelven imageBlock que se empuja a userMessageContent) y guarda mtime + readCount=1 en la caché.

SearchFiles y ListFiles comparten la estructura: resolución de path + clineignore + aprobación + ejecución + formateo. SearchFiles añade la búsqueda paralela multi-workspace:

typescript
// apps/vscode/src/core/task/tools/handlers/SearchFilesToolHandler.ts
const searchPromises = searchPaths.map(({ absolutePath, workspaceName, workspaceRoot }) =>
    this.executeSearch(config, absolutePath, workspaceName, workspaceRoot, regex, filePattern),
)
const searchResults = await Promise.all(searchPromises)
const results = this.formatSearchResults(config, searchResults, searchPaths)

regexSearchFiles llama a ripgrep en el fondo; los resultados se agrupan por nombre de workspace.

Límites y fallos

  • Falta de parámetros: execute_command sin command / requires_approval, read/search/list sin path, search sin regex: todos consecutiveMistakeCount++ + sayAndCreateMissingParamError (missing params:100-112).
  • Comando rechazado: cuando CLINE_COMMAND_PERMISSIONS lo rechaza, devuelve permissionDeniedError con failedSegment o matchedPattern, dando al modelo información corregible (permission denied:162-181).
  • Notificación de comando largo: en auto-approve + notificaciones activadas, a los 30 s se lanza una notificación «Command is still running», evitando que el usuario crea que se colgó (timeout notification:294-303).
  • Archivo no existe: extractFileContent lanza y el catch lo convierte en toolError("Error reading file: ..."), sin propagar a Task, para que el modelo vea el error y ajuste la ruta (read error:361-373).
  • mtime de caché inconsistente: si stat falla o mtime cambió, se evict la caché para garantizar que la próxima lectura traiga lo más reciente (mtime check:304-313).
  • Falla la resolución de search path: cuando parseWorkspaceInlinePath lanza, devuelve Error resolving search path: ... y mistake++ (path resolution error:233-242).
  • list files excede límite: si listFiles lanza o la ruta es incorrecta, devuelve Error listing files: ...; en éxito, didHitLimit se convierte en aviso vía formatFilesList (list error:101-105).
  • Subagent salta UI: los cuatro handler omiten partial say y ask cuando isSubagentExecution, y simplemente terminan y devuelven (subagent skip:155-157).

Resumen

Estos cuatro handler son los «ojos y manos» de Cline: ExecuteCommand corre shell, ReadFile lee disco, SearchFiles corre ripgrep, ListFiles lista directorios. Comparten el esqueleto «validación → aprobación → hook → ejecución → telemetría»; la diferencia está en los detalles: execute_command tiene timeout clasificado por comando y doble verificación de aprobación, read_file tiene caché dedup por mtime, search soporta paralelismo multi-workspace, list tiene tope 200. El consecutiveMistakeCount de todas las herramientas se reinicia a 0 en éxito; en fallo acumula hasta el límite YOLO.

Para profundizar:

  • Herramientas de escritura: /edit-tools/write-to-file
  • Parchear múltiples archivos: /edit-tools/apply-patch
  • Herramienta de navegador: /cap-tools/browser
  • Web fetch/search: /cap-tools/web

Véase la documentación oficial: Cline 文档 · README