Skip to content

Glob

A glob capability lets the model discover files by path pattern without reading file contents first.

It is the fastest route from “there should be files shaped like X” to a concrete candidate set.

Typical glob calls:

  • **/*.rs
  • src/**/*.ts
  • **/*test*.py
  • packages/*/src/**/tool/*.ts

Glob is often the first tool in a multi-step chain.

Standard chain:

  1. glob to find path candidates
  2. grep to filter by content
  3. read targeted ranges
  4. edit selected files

The challenge is not pattern matching itself.

The challenge is operational determinism and bounded output.

If glob returns unstable order, models produce flaky plans.

If glob returns unbounded results, context gets saturated by path spam.

If glob semantics are unclear, pattern expectations diverge between shell, Python, and ripgrep engines.

A production glob tool should therefore define:

  • accepted pattern dialect
  • path root and path normalization rules
  • hidden-file behavior
  • ignore behavior
  • ordering behavior
  • truncation behavior
  • permission model

Another subtle challenge is trust boundaries.

Path globbing can walk large trees quickly, including symlinked or external directories, if boundaries are not enforced.

Reference implementations show three broad styles:

  • Aider: user command expansion (/add) with repo filtering
  • Codex: no first-class glob tool; composition with list_dir and grep_files
  • OpenCode: dedicated glob tool over ripgrep file enumeration

  • List for directory-shape discovery before pattern filtering.
  • Grep for content-level filtering after path expansion.
  • File Read for bounded inspection of the final file set.

Aider at b9050e1d5faf8096eae7a46a9ecc05a86231384b does not expose a model-callable glob tool.

Glob behavior appears in command handling, primarily /add.

Core flow lives in aider/commands.py:799-898.

cmd_add() parses arguments, checks existing files and ignored files, and then falls back to pattern matching via glob_filtered_to_repo() when direct file resolution fails.

glob_filtered_to_repo() implementation: commands.py:765-797.

Behavior sequence:

  1. ignore empty pattern (:766-767)
  2. absolute path case: treat as exact path candidate (:769-772)
  3. relative pattern case: run Path(self.coder.root).glob(pattern) (:774)
  4. catch parsing/attribute errors and return empty on failure (:775-779)
  5. expand directories recursively via expand_subdir (:781-783)
  6. convert to repo-relative paths when inside root (:785-789)
  7. if git repo exists, filter against tracked file list (:791-795)
  8. return string paths (:796-797)

Directory expansion helper is expand_subdir in commands.py:1669-1678.

It yields:

  • single file if input is a file
  • recursive rglob("*") file set if input is a directory

This means pattern-to-files can recurse deeply and include all files in matched directories.

cmd_add() then handles no-match behavior.

If no file matched and no wildcard characters are present, Aider may ask to create the file (commands.py:838-844).

So /add is both discovery and onboarding.

Repository Filtering via Git Tracked Files

Section titled “Repository Filtering via Git Tracked Files”

Aider narrows glob matches against tracked files when repository context exists.

Filtering point:

  • commands.py:792-795

Tracked set source:

  • aider/repo.py:get_tracked_files (:433-488)

get_tracked_files() includes:

  • files from HEAD tree traversal when commit exists (:448-477)
  • staged files from index entries (:478-483)
  • ignore filtering before return (:486)

Practical effect:

  • /add glob avoids flooding results with untracked build artifacts
  • command behavior remains repo-centric
  • discovery aligns with what Aider can commit/manage

Edge caveat:

If user expects non-tracked files to appear, this filtering can look like missing matches.

Aider’s glob behavior is command-scoped, not tool-scoped.

Consequences:

  • model cannot autonomously issue typed glob calls
  • glob semantics are coupled to /add logic and repository policy
  • output format is command feedback, not a stable tool payload contract

This is coherent with Aider’s overall architecture, where user commands and coder flows mediate most filesystem operations.

For tool-native systems, that same behavior generally needs extraction into a dedicated schema-driven tool.


Codex at 4ab44e2c5cc54ed47e47a6729dfd8aa5a3dc2476 currently has no first-class glob tool in core/src/tools/spec.rs.

Tool registration in spec.rs:1499-1537 shows optional dynamic registration for:

  • apply_patch
  • grep_files
  • read_file
  • list_dir

No glob_files or equivalent path-pattern tool exists.

Therefore models must synthesize glob-like discovery using available primitives.

This is partly intentional:

  • lower surface area
  • fewer semantics to maintain
  • richer composition through existing tools

But it also increases prompt complexity for simple “find paths by pattern” tasks.

Codex list_dir is an experimental function tool.

Spec in spec.rs:1023-1067 defines:

  • dir_path (required, absolute)
  • offset (1-indexed)
  • limit
  • depth

Handler in handlers/list_dir.rs:1-330 provides:

  • strict argument validation:
    • offset > 0
    • limit > 0
    • depth > 0
    • absolute dir_path
  • breadth-first traversal with depth cap
  • deterministic sorting
  • entry formatting with type markers
  • pagination + “More than X entries found” sentinel

Useful constants:

  • MAX_ENTRY_LENGTH = 500
  • default offset = 1
  • default limit = 25
  • default depth = 2

While not pattern matching, list_dir can provide candidate tree structure for follow-up filtering.

grep_files spec (spec.rs:829-878) includes optional include glob.

Handler (handlers/grep_files.rs:109-152) invokes:

rg --files-with-matches --sortr=modified --regexp <pattern> --no-messages [--glob <include>] -- <path>

This gives glob behavior only in conjunction with content search.

Useful constants:

  • DEFAULT_LIMIT = 100
  • MAX_LIMIT = 2000
  • COMMAND_TIMEOUT = 30s

So Codex can simulate “glob plus content condition” but not raw pattern-only file matching.

Codex supports glob-adjacent workflows, not glob-native workflows.

Typical model workaround:

  1. list_dir for rough path map
  2. grep_files with broad regex + include glob
  3. read_file on selected paths

This works, but has tradeoffs:

  • more round trips than direct glob
  • content regex required even for pure path search
  • greater chance of false negatives when regex is too narrow

For OpenOxide, a dedicated glob tool still adds value even with list_dir and grep_files present.


OpenCode at 7ed449974864361bad2c1f1405769fd2c2fcdf42 implements an explicit glob tool in packages/opencode/src/tool/glob.ts:9-80.

glob.ts schema:

  • pattern: string required
  • path?: string optional directory scope

The description text in glob.txt:1-6 emphasizes:

  • fast file pattern matching at codebase scale
  • output sorted by modification time
  • using Task tool for open-ended multi-round discovery
  • batching multiple searches when useful

This provides model guidance at prompt level, while code enforces runtime behavior.

Exact call sequence in glob.ts:

  1. ask glob permission up front (:21-29)
  2. derive search root:
    • params.path if provided
    • otherwise Instance.directory (:31)
  3. resolve to absolute path (:32)
  4. enforce directory-boundary policy with assertExternalDirectory(..., { kind: "directory" }) (:33)
  5. initialize limit control:
    • limit = 100 (:35)
  6. iterate ripgrep file stream via Ripgrep.files (:38-42)
  7. enforce truncation when >= limit (:43-46)
  8. convert each relative entry to absolute path (:47)
  9. query mtime with Bun.file(full).stat() (:48-51)
  10. sort descending by mtime (:57)
  11. render output:
    • “No files found” if empty
    • newline-separated absolute paths if matches
    • truncation advisory footer if truncated (:63-68)
  12. return metadata { count, truncated } (:73-76)

This is a compact but complete implementation.

Notable design choice:

permission is requested before directory resolution results are returned, so user can deny broad scans early.

glob.ts delegates traversal to Ripgrep.files.

packages/opencode/src/file/ripgrep.ts:210-276 defines generator behavior.

Key details:

  • command starts with rg --files --glob=!.git/* (:220)
  • hidden files included by default unless hidden === false (:222)
  • optional follow symlinks and max depth supported (:221, :223)
  • caller globs appended as repeated --glob=<pattern> (:224-227)
  • directory existence pre-check for clearer errors (:230-238)
  • streamed line-by-line output parsing with Unix and Windows line ending handling (:260-266)
  • abort-signal checks before/during/after read loop (:218, :254, :275)

Platform behavior:

  • uses system rg if available (:126-131)
  • otherwise downloads ripgrep 14.1.1 per platform (:140-147)
  • extracts archive and sets executable bit on non-Windows (:197)

This gives OpenCode predictable traversal capability across environments, without assuming ripgrep pre-install.

Glob tool inherits these properties automatically.

Output content strategy is intentionally simple:

  • list absolute file paths
  • append truncation warning when cap hit

Metadata fields:

  • count: number returned (bounded by cap)
  • truncated: boolean

Title field:

  • relative path from worktree to search root (glob.ts:72)

This makes UI display predictable and supports downstream logic without expensive parsing.

One subtle behavior:

mtime sort requires per-file stat call.

If stat fails, mtime falls back to 0 (:51), which pushes such files toward tail.


Glob syntax differs across engines.

Examples of divergence sources:

  • Python Path.glob
  • ripgrep --glob
  • shell expansion rules
  • gitignore-style wildmatch semantics

If prompt guidance assumes one dialect while runtime uses another, models may under-match or over-match.

Mitigation set:

  • document accepted examples in tool description
  • reject invalid patterns with explicit error messaging
  • include returned root path in output
  • include count + truncation metadata

For cross-tool consistency, OpenOxide should choose one canonical matcher backend for glob/list/grep include patterns.

Teams differ on whether glob should include dotfiles.

OpenCode currently includes hidden files by default through ripgrep files generator.

Aider command behavior depends on Python traversal plus repo filtering.

Codex list_dir includes whatever filesystem traversal returns.

Without explicit contract, users and models infer different defaults.

Mitigation:

  • add include_hidden parameter
  • default based on explicit policy, not backend accident
  • echo hidden-file mode in result metadata

Broad patterns like **/* can produce enormous candidate sets.

Even when output is capped, traversal cost can be high, and user approval prompts may become noisy.

Mitigation patterns:

  • hard result cap
  • optional traversal depth cap
  • stable ordering so truncation is reproducible
  • advisory text prompting narrower patterns

OpenCode already caps at 100 and warns on truncation.

Codex list_dir uses offset/limit/depth for bounded traversal.

OpenOxide should combine both:

  • traversal bound
  • return bound

Pattern search near workspace boundaries is a common security and correctness risk.

Risks:

  • relative .. path escaping
  • symlink-driven traversal outside project
  • mixed separator issues on Windows

Reference patterns:

  • OpenCode uses assertExternalDirectory to ask explicit permission for external targets
  • Aider restricts many operations to repo root and filters to tracked files in /add flow
  • Codex list_dir requires absolute path and validates inputs

OpenOxide should canonicalize paths early and separate permissions for:

  • path boundary crossing
  • file enumeration itself

Introduce first-class glob_files tool.

Suggested schema:

  • pattern: String required
  • base_path: Option<String> absolute root override
  • limit: Option<usize> default 100, hard max 2000
  • max_depth: Option<usize> optional traversal cap
  • include_hidden: bool default false
  • follow_symlinks: bool default false
  • sort: "path" | "mtime_desc" default path

Response shape:

  • paths: Vec<String> absolute
  • count_returned
  • count_total_estimate (optional)
  • truncated
  • base_path
  • pattern
  • elapsed_ms

Policy shape:

  • permission kind glob
  • separate permission kind external_directory when root is outside workspace

Use ripgrep-backed traversal as default, for consistency with grep/list tools.

Execution model:

  1. canonicalize base path
  2. validate directory exists
  3. enforce boundary policy
  4. build rg --files args with glob filters
  5. stream results with cancellation support
  6. apply limit and sorting
  7. return structured response + summary text

Validation behaviors:

  • empty pattern -> model-facing error
  • invalid pattern -> model-facing error with example
  • nonexistent root -> typed PathNotFound

Cross-platform considerations:

  • normalize separators in output
  • preserve absolute path representation per OS
  • test line ending handling in streaming parser

Determinism matters for reproducible agent behavior.

Recommended default ordering:

  • lexicographic normalized path

Optional mode:

  • mtime descending

If using mtime sort, stat failures should be tracked and surfaced, not silently ignored.

Limit behavior:

  • hard cap from config
  • request cap clamped to hard cap
  • truncated true when more candidates existed

Pagination future extension:

  • offset_token over sorted result set
  • allows large result exploration without massive single responses

Implementation options:

  • ripgrep subprocess via tokio::process
  • or ignore crate walker + globset matching

Recommended baseline:

  • start with ripgrep subprocess for parity with grep stack
  • add optional native walker later if needed

Support crates:

  • serde + schemars for schema typing
  • thiserror for typed error responses
  • tokio for async execution and cancellation
  • camino or existing absolute-path utility for normalization

Testing matrix:

  • simple pattern
  • recursive pattern
  • hidden include/exclude
  • symlink follow off/on
  • external directory permission denied/approved
  • truncation path with deterministic order
  • Windows path separator behavior
  • cancellation mid-stream

A dedicated glob tool is low conceptual complexity, but high leverage.

Done well, it reduces prompt ambiguity, shrinks unnecessary content search, and gives models a stable discovery primitive for large repositories.