Herramientas de comando y lectura de archivos: ExecuteCommand / ReadFile / SearchFiles / ListFiles
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= 0tras 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:
fileReadCachesolo 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:
formatFileContentWithLineNumbersañade un prefijoN |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.ally 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-56—isLikelyLongRunningCommand+resolveCommandTimeoutSeconds; prioriza el parámetro timeout que pasa el modelo.ExecuteCommand class:58—name = ClineDefaultTool.BASH; handlePartialBlock espera al block completo en auto-approve antes de say.workspace hint:135-159— parsea el prefijo@workspace:commandy cambia executionDir a la raíz del workspace correspondiente.permission checks:162-190—commandPermissionController.validateCommand+clineIgnoreController.validateCommand.approval flow:223-279— auto-approve requiere!requiresApprovalPerLLM && autoApproveSafeo, 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:142—name = 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— llamaextractFileContent(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— llamaregexSearchFiles(ripgrep), filtra resultados con clineIgnoreController.formatSearchResults:120— en multi-workspace agrupa por nombre; en workspace único devuelve directo.listFiles call:100—listFiles(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:
// 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:
// 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:
// 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_PERMISSIONSlo rechaza, devuelvepermissionDeniedErrorcon 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:
extractFileContentlanza y el catch lo convierte entoolError("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
parseWorkspaceInlinePathlanza, devuelveError resolving search path: ...y mistake++ (path resolution error:233-242). - list files excede límite: si
listFileslanza o la ruta es incorrecta, devuelveError listing files: ...; en éxito, didHitLimit se convierte en aviso víaformatFilesList(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