9.1 KiB
9.1 KiB
find
Find filesystem paths by glob; use
searchwhen you need content matches instead of path matches.
Source
- Entry:
packages/coding-agent/src/tools/find.ts - Model-facing prompt:
packages/coding-agent/src/prompts/tools/find.md - Key collaborators:
packages/coding-agent/src/tools/path-utils.ts— normalize inputs; split base path vs glob.packages/coding-agent/src/tools/list-limit.ts— apply result-count caps.packages/coding-agent/src/session/streaming-output.ts— truncate text output at byte cap.packages/coding-agent/src/tools/tool-result.ts— buildcontentanddetails.meta.packages/coding-agent/src/tools/output-meta.ts— encode limit / truncation metadata.packages/coding-agent/src/tools/tool-errors.ts— map user-facing tool errors.packages/coding-agent/src/tools/index.ts— register the built-in local implementation.
Inputs
| Field | Type | Required | Description |
|---|---|---|---|
paths |
string[] |
Yes | One or more globs, files, or directories. Empty strings are rejected. Multiple entries may be merged into one brace-union search when their base paths can be resolved together. |
hidden |
boolean |
No | Whether hidden files are included. Defaults to true (hidden ?? true). |
limit |
number |
No | Max returned paths. Defaults to 1000. Must be a finite positive number; non-integers are floored. |
Outputs
The tool returns a single text block plus structured details.
- Success text: newline-delimited paths, one per line, relative to the session cwd when possible; absolute when outside cwd. Exact file inputs return that file path as one line.
- Empty result text:
No files found matching pattern. - Multi-path partial miss: appends
Skipped missing paths: ...after the result block, or after the empty-result line. detailsmay include:scopePath: display form of the searched root or merged roots.fileCount: number of paths returned after result limiting.files: returned paths as an array.truncated: whether result count or byte truncation occurred.resultLimitReached: reached result limit.missingPaths: skipped missing inputs in multi-path calls.truncation/meta.limits: structured truncation and limit metadata for renderers.
- Streaming: when the runtime supplies
onUpdate, the local implementation emits incremental newline-delimited text snapshots during globbing, throttled to 200 ms.
Flow
FindTool.execute()normalizes eachpathsentry withnormalizePathLikeInput()and/\\/g -> "/"(packages/coding-agent/src/tools/find.ts). Empty normalized entries fail with`paths` must contain non-empty globs or paths.- For multi-path local calls,
partitionExistingPaths(..., parseFindPattern)(packages/coding-agent/src/tools/path-utils.ts) stats each base path. Missing entries are skipped; if all are missing, the tool throwsPath not found: .... Single missing paths still hard-fail. - The tool tries
resolveExplicitFindPatterns()to merge multiple inputs into one search rooted at a common base path. If that does not apply, it parses one input withparseFindPattern(). parseFindPattern()determines(basePath, globPattern, hasGlob):- no glob chars (
*,?,[,{) => search that path with implicit**/*. - glob in the first segment => search from
.and, unless the pattern already starts with**/, prefix it with**/. - glob later in the path => split at the first glob-bearing segment.
- no glob chars (
resolveToCwd()converts the base path to an absolute path under the session cwd. A resolved/is rejected withSearching from root directory '/' is not allowed.limitis defaulted toDEFAULT_LIMIT(1000) and validated as a positive finite integer.hiddendefaults totrue. The tool also creates a 5 s timeout viaAbortSignal.timeout(GLOB_TIMEOUT_MS).- Execution then branches:
- Custom operations branch: if
FindToolOptions.operations.globexists, the tool checks existence withoperations.exists(), short-circuits exact-file inputs viaoperations.stat()when available, then callsoperations.glob(globPattern, searchPath, { ignore: ["**/node_modules/**", "**/.git/**"], limit }). - Built-in local branch: the tool stats
searchPath. Exact-file inputs return immediately. Directory inputs callnatives.glob()withfileType: File,hidden,maxResults: limit,sortByMtime: true,gitignore: true, and the combined abort signal.
- Custom operations branch: if
- In the local branch, optional
onMatchcallbacks convert each match to a cwd-relative display path and emit throttled progress updates. - After native glob returns, JS sorts
result.matchesbymtimedescending ((b.mtime ?? 0) - (a.mtime ?? 0)) before formatting paths. buildResult()appliesapplyListLimit()to cap the array again atlimit, joins paths with\n, then runstruncateHead()withmaxLines: Number.MAX_SAFE_INTEGER. In practice this leaves the 50 KB byte cap in place while disabling the default 3000-line cap.toolResult()packages text plusdetails, and records result-limit / truncation metadata for renderers.
Modes / Variants
- Exact file path: if the parsed input has no glob and the resolved path stats as a file, output is that one path.
- Directory path: if the parsed input has no glob and stats as a directory, the tool searches it with implicit
**/*. - Single glob path: one input parsed by
parseFindPattern(). - Merged multi-path search: multiple inputs resolved by
resolveExplicitFindPatterns()into one brace-union glob rooted at a common base path. - Partial multi-path search with missing inputs: local multi-path calls skip missing base paths and surface them as
missingPaths/Skipped missing paths: .... - Custom delegated search: uses injected
FindOperationsinstead of local fs + native glob.
Side Effects
- Filesystem
- Stats the resolved base path, and in local multi-path mode stats every candidate base path up front.
- Does not write files.
- Subprocesses / native bindings
- Built-in local mode calls the native
@oh-my-pi/pi-nativesglob implementation.
- Built-in local mode calls the native
- Session state (transcript, memory, jobs, checkpoints, registries)
- Emits structured progress updates when
onUpdateis provided. - Adds truncation / limit metadata to the tool result.
- Emits structured progress updates when
- Background work / cancellation
- Local globbing is cancellable through the caller abort signal plus an internal 5 s timeout.
Limits & Caps
- Default result limit:
1000(DEFAULT_LIMITinpackages/coding-agent/src/tools/find.ts). - Local glob timeout:
5000ms (GLOB_TIMEOUT_MSinpackages/coding-agent/src/tools/find.ts). - Output byte cap:
50 * 1024bytes (DEFAULT_MAX_BYTESinpackages/coding-agent/src/session/streaming-output.ts). - Default generic line cap in
truncateHead()is3000, butfindoverridesmaxLinestoNumber.MAX_SAFE_INTEGER, so byte size — not line count — is the practical output truncation cap. - Streaming update throttle:
200ms betweenonUpdateemissions. - Sort order: most recent
mtimefirst in the built-in local branch and promised in the prompt. The tool re-sorts in JS even though native glob receivessortByMtime: trueso native code can still stop early atmaxResults.
Errors
- User-facing
ToolErrors fromFindTool.execute()include:`paths` must contain non-empty globs or pathsPath not found: ...Searching from root directory '/' is not allowedLimit must be a positive numberPath is not a directory: ...find timed out after 5s
- If the caller aborts, the local branch converts
AbortErrorintoToolAbortError. - Non-
ENOENTstat failures and other unexpected errors are rethrown. - Empty matches are not errors; they return the no-files text result.
Notes
- Reach for
findfor filename / path discovery. Reach forsearchwhen the selection criterion is file contents or regex matches;searchtakes apatternand returns anchored content matches, whilefindonly returns matching paths (packages/coding-agent/src/prompts/tools/find.md,packages/coding-agent/src/prompts/tools/search.md). - Bare top-level globs are made recursive.
*.tsis parsed as base.plus glob**/*.ts;src/*.tsstays rooted atsrcwith a non-recursive*.tssegment;src/**/*.tspreserves explicit recursion. .gitignoreis always enabled in the built-in local branch (gitignore: true). There is no model-facing flag to disable it.hiddendefaults totrue; hidden-file exclusion is opt-out, not opt-in.- Multi-path missing-input tolerance only applies in the built-in local branch. The custom-operations branch hard-fails the first missing
searchPathit checks. - The custom
FindOperations.glob()hook receivesignoreandlimit, but not thehiddenflag or an explicit.gitignoretoggle. A remote delegate must account for that itself if it wants parity with the local branch. - Built-in local globbing asks the native layer for
fileType: File, so recursive directory searches yield files, not directories. Directory outputs are only possible through exact-path passthrough or custom delegates that return them.