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, directories, or internal URLs with backing files. Empty strings and comma-joined multi-path entries such as ["a,b"] 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). |
gitignore |
boolean |
No | Whether .gitignore is respected during local native globbing. Defaults to true; set false to include gitignored files. |
limit |
number |
No | Max returned paths. Defaults to 200; finite positive inputs are floored then clamped to 1..200. |
timeout |
number |
No | Timeout in seconds. Defaults to 5; clamped to 0.5..60. On timeout, returns partial matches collected so far with a timeout notice and truncated: true. |
Outputs
The tool returns a single text block plus structured details.
- Success text: matching paths grouped by directory. Each non-root group starts with
# <dir>/and then lists basenames; root-level matches are listed without a header. Directory matches carry a trailing/. Exact file inputs return that file path as one line. - Empty result text:
No files found matching pattern, optionally followed by a timeout or missing-path notice. - 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. Final output is grouped; streaming snapshots are not.
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.limitdefaults toDEFAULT_LIMIT(200), must be positive and finite, is floored, then clamped toMAX_LIMIT(200).hiddenandgitignoreboth default totrue.timeoutis converted to milliseconds and clamped to500..60_000before building anAbortSignal.timeout(...).- 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()withhidden,maxResults: effectiveLimit,sortByMtime: true,gitignore: useGitignore, 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 ateffectiveLimit, formats paths withformatFindGroupedOutput(), appends notices, 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: .... - Internal URL input: supported when the internal router resolves the URL to a backing file. Internal URL globs are rejected.
- 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 the configured internal timeout.
Limits & Caps
- Default result limit:
200(DEFAULT_LIMITinpackages/coding-agent/src/tools/find.ts). - Maximum result limit:
200(MAX_LIMIT); larger inputs are clamped. - Local glob timeout: default
5000ms, clamped to500..60_000ms. - 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 is an array — pass ["a", "b"] not ["a,b"] ...`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: ...- timeout result text is
find timed out after <seconds>s; returning <N> partial matches — increase timeout or narrow patternand is returned as a successful, truncated partial result rather than an error.
- 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. .gitignoredefaults to enabled in the built-in local branch. Usegitignore: falseto disable it for native traversal.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 does not force
fileType: File; it can return files and directories from native glob. Directory outputs also occur through exact-path passthrough or custom delegates that return them.