Skip to main content

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}