コマンドとファイル読み取りツール:ExecuteCommand / ReadFile / SearchFiles / ListFiles
役割
このページでは Cline の四つの「能力系」読み取り専用/実行ツールを説明する。すべて IFullyManagedTool インターフェースに登録され、ToolExecutor が統一的に调度する:
- ExecuteCommandToolHandler(
ClineDefaultTool.BASH):ターミナルでシェルコマンドを一つ実行する。タイムアウト、マルチワークスペース、権限検証を伴う(class declaration:58)。 - ReadFileToolHandler(
ClineDefaultTool.FILE_READ):単一ファイルを読み取り、1 始まりの行番号を付ける。start_line/end_line によるスライス、画像ファイルの場合は image block を返す(class declaration:142)。 - SearchFilesToolHandler(
ClineDefaultTool.SEARCH):ripgrep ベースの正規検索。マルチワークスペース並列検索、file_pattern によるフィルタをサポート(class declaration:21)。 - ListFilesToolHandler(
ClineDefaultTool.LIST_FILES):ディレクトリをリストアップ。再帰サポート、200 件上限で打ち切る(class declaration:17)。
この四つのツールはインタラクションのパターンを共有する:handlePartialBlock が UI に事前プッシュして「モデルが X を読んでいる」ことをユーザーに見せ、execute が検証(clineignore + パス + パラメータ)→ 承認 → PreToolUse hook → 実行 → テレメトリを行う。いずれも config.isSubagentExecution を尊重し、子 agent 実行時は一部の UI プッシュと ask をスキップして直接仕事をする。
WriteToFileToolHandler との根本的な差は「読み取り専用または外部副作用」であるという点 —— 読む、リストする、検索するはファイルシステムの状態を変えない。execute_command は状態を変えるが terminal を通すため diff ビューに入らず、だからこのツール群には diff / userEdits の還流パスが無い。
設計動機
- mistake 計数の統一ルール:各ツールはパラメータ欠けや clineignore ヒット時に
consecutiveMistakeCount++し、コア操作が成功した後に= 0にする。YOLO モードの連続エラー上限をツール横断で累積できる(reset counter:377)。 - コマンド分類タイムアウト:execute_command はよくある長時間実行コマンド(npm install、cargo build、pytest など)をマッチして 300 秒タイムアウト、その他は 30 秒とする。短いコマンドが長いタイムアウトに引きずられないようにする(
LONG_RUNNING patterns:22-34)。 - コマンド権限の二重ゲート:
CLINE_COMMAND_PERMISSIONS環境変数による segment レベルのホワイトリスト + clineignore のパス検証、二層で独立して拒否する(permission checks:162-190)。 - 読みファイル重複排除キャッシュ:
fileReadCacheはメタデータ(readCount + mtime + imageBlock)のみを保存し、内容は保存しない。ヒットして mtime が変わっていなければ「既に読んだ」ヒントを直接返し、モデルが同じファイルを繰り返し読んでトークンを消費するのを防ぐ(dedup cache:298-314)。 - 行番号付き表示:
formatFileContentWithLineNumbersは各行にN |プレフィックスを付け、continuation ヒントを添える。モデルが start_line で正確に続きを読めるようにする(formatFileContentWithLineNumbers:79)。 - マルチワークスペース並列検索:SearchFilesToolHandler はマルチワークスペースモードで searchPaths をすべて並列
Promise.allで実行し、workspace 名ごとにバケット分けして集計する(parallel search:279-285)。 - ListFiles の 200 上限:listFiles の第三パラメータ limit=200、超過すると didHitLimit=true を返し、formatFilesList が末尾にヒントを付けてモデルに「もっと具体的な path で絞れ」と促す(
listFiles call:100)。
主要ファイル
LONG_RUNNING patterns:22-34— 長時間実行コマンドの正規配列。npm/cargo/pytest/docker build/torchrun など。timeout resolution:36-56—isLikelyLongRunningCommand+resolveCommandTimeoutSeconds。モデルが与えた timeout パラメータを優先する。ExecuteCommand class:58—name = ClineDefaultTool.BASH、handlePartialBlock は auto-approve 時に完全な block を待ってから say する。workspace hint:135-159— コマンドのプレフィックス@workspace:commandを解析し、executionDir を対応する workspace ルートに変える。permission checks:162-190—commandPermissionController.validateCommand+clineIgnoreController.validateCommand。approval flow:223-279— auto-approve には!requiresApprovalPerLLM && autoApproveSafeまたは risky コマンドのautoApproveSafe && autoApproveAllの二重ゲートが必要。cache clear:319-327— コマンド実行後に fileReadCache 全体をclear()する。sed/git checkout/mv などがファイルを予測不能に変更する可能性があるため。formatFileContentWithLineNumbers:79— スライス、行番号付与、「Showing lines X-Y of Z」ヒントを添える。ReadFile class:142—name = ClineDefaultTool.FILE_READ。dedup cache:298-356— mtime 比較 + readCount 累積、3 回目からは DUPLICATE READ 警告を返す。extract + cache:359-373—extractFileContentを呼ぶ(画像は imageBlock を返す)。失敗時は tool error にし、上位に例外を投げない。determineSearchPaths:35— マルチワークスペース下で hint または全ワークスペースを基に検索パスを展開する。executeSearch:74—regexSearchFiles(ripgrep)を呼び、clineIgnoreController で結果をフィルタする。formatSearchResults:120— マルチワークスペース時は workspace 名でバケット分け、単一ワークスペース時は直接返す。listFiles call:100—listFiles(absolutePath, recursive, 200)が [files, didHitLimit] を返す。
データフロー
ExecuteCommand は四つの中で最も複雑で、核心は承認ゲートとタイムアウト解決にある。モデルが与えた requires_approval + command 文字列がどの承認パスを通るかを決める:
// 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 は [autoApproveSafe, autoApproveAll] のタプルを返す:前者は「safe コマンド自動承認」、後者は「all コマンド自動承認」。モデルが risky(requires_approval=true)と言い、かつ safe 自動承認しか設定されていない時は、手動承認に回り、コマンドの後に強い承認プロンプト文字列を追加する(auto-approve logic:223-227)。承認が通った後、PreToolUse hook、そして 30 秒通知タイマー(自動承認 + 通知オン時のみ設定)、最後に executeCommandTool(finalCommand, timeoutSeconds) が terminal で走る。完了後、fileReadCache が全体クリアされる。
ReadFileToolHandler の核心はキャッシュによる重複排除:
// 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)
}
}mtime が変われば(ユーザーがエディタで手動編集したなど)evict し、変わらなければキャッシュを再利用する。キャッシュには内容を保存せずメモリを節約し、ヒット時もディスクから読み直す。readCount が 3 回を超えると DUPLICATE READ の強いヒントを返す。新規読み取り成功後、extractFileContent で内容を取得し(画像ファイルは imageBlock を userMessageContent にプッシュ)、mtime + readCount=1 をキャッシュに保存する。
SearchFiles と ListFiles はどちらもパス解決 + clineignore + 承認 + 実行 + フォーマットを行い、コード構造が似ている。SearchFiles はマルチワークスペース並列が追加されている:
// 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 は内部で ripgrep を呼び、結果は workspace 名でバケット分けして集計される。
境界と失敗
- パラメータ欠け:execute_command で command / requires_approval が無い、read/search/list で path が無い、search で regex が無い場合はすべて
consecutiveMistakeCount+++sayAndCreateMissingParamErrorに進む(missing params:100-112)。 - コマンド拒否:
CLINE_COMMAND_PERMISSIONSが拒否した時はpermissionDeniedErrorを返し、failedSegment や matchedPattern を付けてモデルが修正できる情報を提供する(permission denied:162-181)。 - 長時間コマンド通知:auto-approve + 通知オン時、30 秒後に「Command is still running」のシステム通知を出し、ユーザーがフリーズしたと勘違いするのを防ぐ(
timeout notification:294-303)。 - 読みファイルが存在しない:
extractFileContentが投げたエラーはtoolError("Error reading file: ...")に catch され、Task 上層に投げない。モデルがエラーを見て自分でパスを修正する(read error:361-373)。 - キャッシュの mtime 不一致:stat が失敗または mtime が変わった場合はどちらもキャッシュを evict し、次回は最新の内容を読めるようにする(
mtime check:304-313)。 - search path 解決失敗:
parseWorkspaceInlinePathがエラーを投げた時はError resolving search path: ...を返し、mistake++ する(path resolution error:233-242)。 - list files 上限超過:
listFilesがエラーを投げる、またはパスが不正な場合はError listing files: ...を返す。成功時の didHitLimit はformatFilesListがヒントに変換する(list error:101-105)。 - 子 agent は UI をスキップ:四つの handler はすべて
isSubagentExecution時に partial say と ask フローをスキップし、直接最後まで仕事をして返す(subagent skip:155-157)。
まとめ
この四つの handler は Cline の「目と手」である:ExecuteCommand はシェルを走らせ、ReadFile はディスクを読み、SearchFiles は ripgrep を走らせ、ListFiles はディレクトリをリストする。「検証 → 承認 → hook → 実行 → テレメトリ」という骨格を共有し、違いは実行詳細にある:execute_command にはコマンド分類タイムアウトと二重承認ゲートがあり、read_file には mtime 重複排除キャッシュがあり、search はマルチワークスペース並列をサポートし、list には 200 件上限がある。すべてのツールで consecutiveMistakeCount は成功後にゼロになり、失敗が累積して YOLO 上限に達すると停止する。
さらに掘り下げるなら次へ:
- 書き込みツール:
/edit-tools/write-to-file - 複数ファイルパッチ:
/edit-tools/apply-patch - ブラウザツール:
/cap-tools/browser - Web fetch/search:
/cap-tools/web