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:
ListDir as its own variant, or fold into Read?
WebSearch: single query or Codex-style queries[]?
WebFetch in scope (Amp already normalizes toward the name) or follow-up?
- Cursor's
ripgrep_raw_search (raw shell string): parse into Grep or
carry as Raw?
- Opencode/Pi passthrough keys for glob/grep/list — I haven't verified
their native field names; corrections welcome.
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::Rawblobs.Today every harness with these tools funnels them into
Raw, each in itsown field dialect, so no consumer (search, renderers, cross-harness
export) can rely on typed fields, and cross-harness trips are silently
lossy:
Glob{pattern}· Ampglob{filePattern}· GrokGlob{glob_pattern, target_directory}· Opencodeglob· Pifind· Cursorglob_file_searchGrep{pattern,…}· Opencodegrep· Pigrep· Cursorripgrep_raw_search{command}(raw shell string) · Antigravitygrep_searchlist→LS· Pils→LS· Antigravitylist_dirWebSearch(action blob) · Ampread_web_page{objective}→WebFetch{prompt}Concrete loss, reproduced: a Claude
Glob{pattern: "**/*.rs"}views as[tool 1 Glob]+ blob and exports to grok missingtarget_directory,while a grok
Globarrives at Claude carrying a mysterypathkey. Thecanonical 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:
viewshows[tool 1 Glob]+ raw JSON;--with grokemits{"glob_pattern": "**/*.rs"}with notarget_directory.Wanted:
viewrenders the pattern as a structured field, search can matchon it, and
--with grokemits a completeGlobin grok's dialect —because Common carries
Tool::Glob { pattern, path }and each codec ownsits rename table, exactly like
Read{file_path}↔path/filePathtoday.Proposed behavior
New
Toolvariants with minimal required fields (every required field isa promise to 19 harnesses), following the established pattern: variant +
Args struct with serde defaults,
from_canonicalarm withRawfallback,to_canonicalarm, renderer + search indexing, per-harness rename wiring,per-format doc updates:
Phasing:
Glob+Grepfirst (names already agreed across harnesses),ListDir/WebSearchafter.Rawfallback preserved everywhere, so oldbinaries 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::Commandprecedent).Open questions for you:
ListDiras its own variant, or fold intoRead?WebSearch: singlequeryor Codex-stylequeries[]?WebFetchin scope (Amp already normalizes toward the name) or follow-up?ripgrep_raw_search(raw shell string): parse intoGreporcarry as
Raw?their native field names; corrections welcome.