cosh_tools/find/types.rs
1//! Input and output types shared by all `find` tools.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6/// Parameters for `find_glob`.
7#[derive(Debug, Deserialize, JsonSchema)]
8pub struct GlobInput {
9 /// The glob pattern to match (e.g. "**/*.rs", "src/**", "*.toml").
10 pub pattern: String,
11 /// The root directory to search within. Omitted: defaults to the working
12 /// directory.
13 pub path: Option<String>,
14 /// Multiple search roots in one call. When present, overrides `path`.
15 /// Each target is validated individually by the path guard; missing
16 /// targets are skipped with a warning instead of failing the whole call.
17 pub paths: Option<Vec<String>>,
18 /// Restrict results to a filesystem kind: `"file"`, `"dir"`, or `"symlink"`.
19 pub file_type: Option<String>,
20 /// Search subdirectories recursively (default: `true`).
21 pub recursive: Option<bool>,
22 /// Include hidden files and directories whose names start with `.` (default: `false`).
23 pub hidden: Option<bool>,
24 /// Maximum number of entries to return.
25 pub max_results: Option<u32>,
26 /// Respect `.gitignore` rules (default: `true`).
27 pub gitignore: Option<bool>,
28 /// Sort results by modification time, most recent first (default: `true`).
29 pub sort_by_mtime: Option<bool>,
30 /// Output layout for the `formatted` field: `"flat"`, `"grouped"`, or
31 /// `"tree"`. When unset, no formatted rendering is attached.
32 pub format: Option<String>,
33 /// Abort the search after this many milliseconds.
34 pub timeout_ms: Option<u32>,
35}
36
37/// Parameters for `find_grep`.
38#[derive(Debug, Deserialize, JsonSchema)]
39pub struct GrepInput {
40 /// The regex pattern to search for in file contents.
41 pub pattern: String,
42 /// The root directory to search within.
43 pub path: String,
44 /// Multiple search targets (files or directories) to search in a single
45 /// call. When present, overrides `path`. Each target is validated
46 /// individually by the path guard.
47 pub paths: Option<Vec<String>>,
48 /// Restrict matches to a 1-based inclusive line range `"start-end"`.
49 /// Requires every target to be a single file.
50 pub line_range: Option<String>,
51 /// Restrict the search to files whose names match this glob (e.g., `"*.rs"`).
52 pub glob: Option<String>,
53 /// Restrict the search to files of a given language type (e.g., `"rust"`, `"py"`, `"js"`).
54 pub file_type: Option<String>,
55 /// Case-insensitive matching (default: `false`).
56 pub ignore_case: Option<bool>,
57 /// Maximum total number of matches to return across all files.
58 pub max_count: Option<u32>,
59 /// Files to skip before collecting results — use to paginate when the
60 /// prior call hit the file window limit.
61 pub skip: Option<u32>,
62 /// Lines of context to include before each match.
63 pub context_before: Option<u32>,
64 /// Lines of context to include after each match.
65 pub context_after: Option<u32>,
66 /// Include hidden files (default: `true`).
67 pub hidden: Option<bool>,
68 /// Respect `.gitignore` rules (default: `true`).
69 pub gitignore: Option<bool>,
70 /// Abort the search after this many milliseconds.
71 pub timeout_ms: Option<u32>,
72}
73
74/// Configuration for the [`glob`](super::glob::glob) tool.
75///
76/// Fields are optional — use `Glob::default()` for sensible defaults.
77#[derive(Default)]
78pub struct Glob {
79 /// Restrict results to a filesystem kind: `"file"`, `"dir"`, or `"symlink"`.
80 pub file_type: Option<String>,
81 /// Search subdirectories recursively (default: `true`).
82 pub recursive: Option<bool>,
83 /// Include hidden files and directories whose names start with `.`.
84 /// Left `None`, the free function falls through to the SDK default
85 /// (`false`); the `Find` wrapper defaults this to `true` (reference-tool
86 /// behavior). The walker ALWAYS skips `.git` regardless.
87 pub hidden: Option<bool>,
88 /// Maximum number of entries to return.
89 pub max_results: Option<u32>,
90 /// Respect `.gitignore` rules (default: `true`).
91 pub gitignore: Option<bool>,
92 /// Sort results by modification time, most recent first (default: `true`).
93 pub sort_by_mtime: Option<bool>,
94 /// Output layout (`"flat"`, `"grouped"`, `"tree"`).
95 pub format: Option<String>,
96 /// Abort the search after this many milliseconds.
97 pub timeout_ms: Option<u32>,
98}
99
100/// Schema-driven call options bundled for the `find_glob` entry point,
101/// keeping `glob_full` argument count low as the schema grows.
102///
103/// `file_type` filters results to one filesystem kind (`"file"`, `"dir"`,
104/// `"symlink"`) and is an extension over the reference tool (oh-my-pi).
105/// `hidden` and `gitignore` default to `true` (reference behavior) when
106/// omitted; `max_results` defaults to 200 with a hard ceiling of 200.
107#[derive(Clone, Debug, Default)]
108pub struct GlobCallOptions {
109 /// Restrict results to a filesystem kind: `"file"`, `"dir"`, or `"symlink"`.
110 pub file_type: Option<String>,
111 /// Whether to include hidden entries. Omitted defaults to `true`.
112 pub hidden: Option<bool>,
113 /// Whether to respect `.gitignore`. Omitted defaults to `true`.
114 pub gitignore: Option<bool>,
115 /// Maximum number of entries to return (clamped to 200).
116 pub max_results: Option<u32>,
117 /// Output layout: `"flat"`, `"grouped"`, or `"tree"`.
118 pub format: Option<String>,
119 /// Sort results by modification time, most recent first. Omitted defaults
120 /// to `true` (the most recently edited files surface first).
121 pub sort_by_mtime: Option<bool>,
122 /// Abort the search after this many milliseconds.
123 pub timeout_ms: Option<u32>,
124}
125
126/// A single filesystem entry matched by a glob search.
127#[derive(Serialize)]
128pub struct GlobEntry {
129 /// Path relative to the search root, using forward slashes.
130 pub path: String,
131 /// Filesystem kind: `"file"`, `"dir"`, or `"symlink"`.
132 pub file_type: String,
133 /// Modification time in milliseconds since the Unix epoch.
134 pub mtime_ms: Option<f64>,
135 /// File size in bytes (`None` for directories and symlinks).
136 pub size_bytes: Option<f64>,
137}
138
139/// Result returned by the [`glob`](super::glob::glob) tool.
140#[derive(Serialize)]
141pub struct GlobOutput {
142 /// Matched filesystem entries.
143 pub matches: Vec<GlobEntry>,
144 /// Total number of entries returned.
145 pub total: u32,
146 /// `true` when the search hit `max_results` and more entries may exist.
147 pub limit_reached: Option<bool>,
148 /// `true` when the scan was cut short by the timeout; `matches` holds the
149 /// partial results found up to that point. An empty `matches` with this
150 /// flag is an INCOMPLETE scan, not proof of absence.
151 pub timed_out: Option<bool>,
152 /// Human-readable hint for the caller (timeout guidance, no-match notice,
153 /// pagination/limit notes).
154 pub note: Option<String>,
155 /// `true` when no matches were selected. Such a result carries no new
156 /// information — adjust the pattern or scope instead of blindly retrying.
157 pub useless: Option<bool>,
158 /// User-supplied targets whose directory was missing on disk. These were
159 /// skipped; the surviving targets' results are still returned.
160 pub missing_paths: Option<Vec<String>>,
161 /// Matched paths rendered in the requested `format` (`flat`/`grouped`/
162 /// `tree`), OR the plain newline-joined paths when no format was
163 /// requested. Always present (even for empty results) so the model never
164 /// needs to reconstruct grouping from the structured entries.
165 pub formatted: String,
166 /// The directory this search was scoped to, in the same relative form as
167 /// the match paths (`.`, `src`, `crates/…`).
168 pub scope: String,
169 /// Working directory the paths are relative to. Lets the TUI renderer
170 /// resolve match paths to absolute paths for OSC 8 file hyperlinks.
171 pub cwd: Option<String>,
172}
173
174/// Configuration for the [`grep`](super::grep::grep) tool.
175///
176/// Fields are optional — use `Grep::default()` for sensible defaults.
177#[derive(Default)]
178pub struct Grep {
179 /// Restrict the search to files whose names match this glob (e.g., `"*.rs"`).
180 pub glob: Option<String>,
181 /// Restrict the search to files of a given language type (e.g., `"rust"`, `"py"`, `"js"`).
182 pub file_type: Option<String>,
183 /// Case-insensitive matching (default: `false`).
184 pub ignore_case: Option<bool>,
185 /// Maximum total number of matches to return across all files.
186 pub max_count: Option<u32>,
187 /// Files to skip before collecting results — use to paginate when the
188 /// prior call hit the file window limit.
189 pub skip: Option<u32>,
190 /// Restrict matches to a 1-based inclusive line range `"start-end"`.
191 /// Requires every target to be a single file.
192 pub line_range: Option<String>,
193 /// Lines of context to include before each match.
194 pub context_before: Option<u32>,
195 /// Lines of context to include after each match.
196 pub context_after: Option<u32>,
197 /// Include hidden files (default: `true`).
198 pub hidden: Option<bool>,
199 /// Respect `.gitignore` rules (default: `true`).
200 pub gitignore: Option<bool>,
201 /// Abort the search after this many milliseconds.
202 pub timeout_ms: Option<u32>,
203}
204
205/// A context line adjacent to a grep match.
206#[derive(Clone, Serialize)]
207pub struct ContextEntry {
208 /// 1-indexed line number in the source file.
209 pub line_number: u32,
210 /// Raw line content.
211 pub line: String,
212}
213
214/// A single match found by the grep tool.
215#[derive(Clone, Serialize)]
216pub struct GrepMatchEntry {
217 /// File path where the match was found.
218 pub path: String,
219 /// 1-indexed line number of the matched line.
220 pub line_number: u32,
221 /// Content of the matched line.
222 pub line: String,
223 /// `true` when the line was truncated to the column limit (see the tool
224 /// description) — read the file for the full line.
225 pub truncated: Option<bool>,
226 /// Context lines immediately before the match.
227 pub context_before: Vec<ContextEntry>,
228 /// Context lines immediately after the match.
229 pub context_after: Vec<ContextEntry>,
230}
231
232/// Hashline anchor for a single file surfaced by the [`grep`](super::grep::grep) tool.
233///
234/// Lets the agent edit a file directly from grep output: pair a match's `path`
235/// with the matching `file_hash`/`header` and pass them to `fs_edit` without
236/// re-reading the file to obtain the current hashline tag.
237#[derive(Serialize)]
238pub struct GrepFileEntry {
239 /// File path, in the same form as the match paths in this result.
240 pub path: String,
241 /// 4-hex content hash of the whole file — the `file_hash` for `fs_edit`.
242 pub file_hash: String,
243 /// Hashline header `¶path#TAG` anchoring edits to this file. The path is
244 /// absolute so it round-trips through `fs_edit`'s path validation.
245 pub header: String,
246}
247
248/// Result returned by the [`grep`](super::grep::grep) tool.
249#[derive(Serialize)]
250pub struct GrepOutput {
251 /// All matches found, ordered by file path. Bounded by the file window and
252 /// per-file caps described on the tool; page further files with `skip`.
253 pub matches: Vec<GrepMatchEntry>,
254 /// Total number of matches across all files (a lower bound when caps
255 /// trimmed the fetched matches).
256 pub total_matches: u32,
257 /// Number of files that contained at least one match.
258 pub files_with_matches: u32,
259 /// Number of files searched.
260 pub files_searched: u32,
261 /// `true` when more files matched than the shown window — page with `skip`.
262 pub file_limit_reached: bool,
263 /// `true` when at least one file had more matches than shown (capped to
264 /// keep a single hot file from crowding out diverse hits).
265 pub per_file_limit_reached: bool,
266 /// Human-readable hint: a pagination instruction when the file window was
267 /// hit, or a no-match notice (`"No matches found"` / `"No more results …"`).
268 pub note: Option<String>,
269 /// `true` when no matches were selected. Such a result carries no new
270 /// information — adjust the pattern or scope instead of blindly retrying.
271 pub useless: Option<bool>,
272 /// Hashline anchors for shown files, in encounter order. Bounded to a
273 /// small window of files (whole-file tags require reading each file);
274 /// files beyond the window surface plain, headerless output.
275 pub files: Vec<GrepFileEntry>,
276 /// `true` when the search was cut short by the timeout; `matches` holds
277 /// the partial results found up to that point. An empty `matches` with
278 /// this flag is an INCOMPLETE search, not proof of absence.
279 pub timed_out: Option<bool>,
280}