Skip to content

RFC: Typed search and listing tools (Glob, Grep, ListDir, WebSearch) #70

Description

@Drix10

What do you want to do?

Work with file-find, content-search, directory-list, and web-search calls
as first-class typed tools instead of opaque Tool::Raw blobs.

Today every harness with these tools funnels them into Raw, each in its
own field dialect, so no consumer (search, renderers, cross-harness
export) can rely on typed fields, and cross-harness trips are silently
lossy:

Op Harness spellings (native name + keys)
File find Claude Glob{pattern} · Amp glob{filePattern} · Grok Glob{glob_pattern, target_directory} · Opencode glob · Pi find · Cursor glob_file_search
Content search Claude/Grok/Amp Grep{pattern,…} · Opencode grep · Pi grep · Cursor ripgrep_raw_search{command} (raw shell string) · Antigravity grep_search
List dir Opencode list→LS · Pi ls→LS · Antigravity list_dir
Web Codex WebSearch (action blob) · Amp read_web_page{objective}→WebFetch{prompt}

Concrete loss, reproduced: a Claude Glob{pattern: "**/*.rs"} views as
[tool 1 Glob] + blob and exports to grok missing target_directory,
while a grok Glob arrives at Claude carrying a mystery path key. The
canonical names (Glob, Grep, LS) already exist by convention in 4+
harnesses.

Example session workflow

Input (Claude Code native, one assistant turn):

{"type": "tool_use", "id": "t1", "name": "Glob", "input": {"pattern": "**/*.rs"}}

Today: view shows [tool 1 Glob] + raw JSON; --with grok emits
{"glob_pattern": "**/*.rs"} with no target_directory.

Wanted: view renders the pattern as a structured field, search can match
on it, and --with grok emits a complete Glob in grok's dialect —
because Common carries Tool::Glob { pattern, path } and each codec owns
its rename table, exactly like Read{file_path} ↔ path/filePath today.

Proposed behavior

New Tool variants with minimal required fields (every required field is
a promise to 19 harnesses), following the established pattern: variant +
Args struct with serde defaults, from_canonical arm with Raw fallback,
to_canonical arm, renderer + search indexing, per-harness rename wiring,
per-format doc updates:

Tool::Glob { pattern: String, path: Option<String> }
Tool::Grep { pattern: String, path: Option<String>, include: Option<String> }
Tool::ListDir { path: Option<String> }
Tool::WebSearch { query: String }

Phasing: Glob + Grep first (names already agreed across harnesses),
ListDir / WebSearch after. Raw fallback preserved everywhere, so old
binaries keep reading new sessions and no harness moves except through the
new mappings (verified old-binary-vs-new on a session corpus, per the
Tool::Command precedent).

Open questions for you:

  1. ListDir as its own variant, or fold into Read?
  2. WebSearch: single query or Codex-style queries[]?
  3. WebFetch in scope (Amp already normalizes toward the name) or follow-up?
  4. Cursor's ripgrep_raw_search (raw shell string): parse into Grep or
    carry as Raw?
  5. Opencode/Pi passthrough keys for glob/grep/list — I haven't verified
    their native field names; corrections welcome.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions