litecode/schema/mod.rs
1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
5#[serde(deny_unknown_fields)]
6pub struct ReadInput {
7 /// The absolute path to the file to read
8 pub file_path: String,
9 /// The line number to start reading from. Only provide if the file is too large to read at once
10 pub offset: Option<usize>,
11 /// The number of lines to read. Only provide if the file is too large to read at once.
12 pub limit: Option<usize>,
13 /// Page range for PDF files (e.g., "1-5", "3", "10-20"). Only applicable to PDF files. Maximum 20 pages per request.
14 pub pages: Option<String>,
15}
16
17#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
18#[serde(deny_unknown_fields)]
19pub struct WriteInput {
20 /// The absolute path to the file to write (must be absolute, not relative)
21 pub file_path: String,
22 /// The content to write to the file
23 pub content: String,
24 /// Decode Unicode escape sequences like \u4F60 before writing (default false)
25 #[serde(default)]
26 pub decode_unicode_escapes: bool,
27}
28
29#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
30#[serde(deny_unknown_fields)]
31pub struct EditInput {
32 /// The absolute path to the file to modify
33 pub file_path: String,
34 /// The text to replace
35 pub old_string: String,
36 /// The text to replace it with (must be different from old_string)
37 pub new_string: String,
38 /// Replace all occurrences of old_string (default false)
39 #[serde(default)]
40 pub replace_all: bool,
41 /// Decode Unicode escape sequences like \u4F60 in old_string and new_string (default false)
42 #[serde(default)]
43 pub decode_unicode_escapes: bool,
44}
45
46#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
47#[serde(deny_unknown_fields)]
48pub struct GlobInput {
49 /// The glob pattern to match files against
50 pub pattern: String,
51 /// The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.
52 pub path: Option<String>,
53}
54
55#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
56#[serde(rename_all = "snake_case")]
57pub enum GrepOutputMode {
58 Content,
59 FilesWithMatches,
60 Count,
61}
62
63impl Default for GrepOutputMode {
64 fn default() -> Self {
65 Self::FilesWithMatches
66 }
67}
68
69#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
70#[serde(deny_unknown_fields)]
71pub struct GrepInput {
72 /// The regular expression pattern to search for in file contents
73 pub pattern: String,
74 /// File or directory to search in (rg PATH). Defaults to current working directory.
75 pub path: Option<String>,
76 /// Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob
77 pub glob: Option<String>,
78 /// Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "files_with_matches".
79 #[serde(default)]
80 pub output_mode: GrepOutputMode,
81 /// Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.
82 #[serde(default, rename = "-B")]
83 pub before: Option<usize>,
84 /// Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.
85 #[serde(default, rename = "-A")]
86 pub after: Option<usize>,
87 /// Alias for context.
88 #[serde(default, rename = "-C")]
89 pub context_alias: Option<usize>,
90 /// Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.
91 pub context: Option<usize>,
92 /// Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.
93 #[serde(default, rename = "-n")]
94 pub line_numbers: Option<bool>,
95 /// Case insensitive search (rg -i)
96 #[serde(default, rename = "-i")]
97 pub case_insensitive: Option<bool>,
98 /// File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types.
99 #[serde(rename = "type")]
100 pub file_type: Option<String>,
101 /// Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 0 (unlimited).
102 pub head_limit: Option<usize>,
103 /// Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.
104 pub offset: Option<usize>,
105 /// Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.
106 #[serde(default)]
107 pub multiline: bool,
108}
109
110#[derive(Debug, Clone, Serialize, JsonSchema)]
111#[serde(deny_unknown_fields)]
112pub struct WriteOutput {
113 /// Whether the write operation succeeded
114 pub success: bool,
115 /// Summary of what was written
116 pub message: String,
117}
118
119#[derive(Debug, Clone, Serialize, JsonSchema)]
120#[serde(deny_unknown_fields)]
121pub struct EditOutput {
122 /// Whether the edit operation succeeded
123 pub success: bool,
124 /// Summary of what was edited
125 pub message: String,
126}
127
128#[derive(Debug, Clone, Serialize, JsonSchema)]
129#[serde(deny_unknown_fields)]
130pub struct GlobOutput {
131 /// Total number of files found
132 #[serde(rename = "numFiles")]
133 pub num_files: usize,
134 /// Array of file paths that match the pattern
135 pub filenames: Vec<String>,
136}
137
138#[derive(Debug, Clone, Serialize, JsonSchema)]
139#[serde(deny_unknown_fields)]
140pub struct GrepOutput {
141 /// Total number of files found
142 #[serde(rename = "numFiles")]
143 pub num_files: usize,
144 /// Array of file paths that match the pattern
145 pub filenames: Vec<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub content: Option<String>,
148 /// Total number of matches found
149 #[serde(rename = "numMatches", skip_serializing_if = "Option::is_none")]
150 pub num_matches: Option<usize>,
151}
152
153#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
154#[serde(deny_unknown_fields)]
155pub struct BashInput {
156 /// The command to execute
157 pub command: String,
158 /// Optional timeout in milliseconds (max 600000)
159 pub timeout: Option<u64>,
160 /// Clear, concise description of what this command does in active voice. Never use words like "complex" or "risk" in the description - just describe what it does.
161 ///
162 /// For simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):
163 /// - ls → "List files in current directory"
164 /// - git status → "Show working tree status"
165 /// - npm install → "Install package dependencies"
166 ///
167 /// For commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:
168 /// - find . -name "*.tmp" -exec rm {} \; → "Find and delete all .tmp files recursively"
169 /// - git reset --hard origin/main → "Discard all local changes and match remote main"
170 /// - curl -s url | jq '.data[]' → "Fetch JSON from URL and extract data array elements"
171 pub description: Option<String>,
172 /// Set to true to run this command in the background. Use TaskOutput to read the output later.
173 pub run_in_background: Option<bool>,
174}
175
176#[derive(Debug, Clone, Serialize, JsonSchema)]
177#[serde(deny_unknown_fields)]
178pub struct BashOutput {
179 /// The standard output of the command
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub stdout: Option<String>,
182 /// The standard error output of the command
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub stderr: Option<String>,
185 /// Whether the command was interrupted
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub interrupted: Option<bool>,
188 /// ID of the background task if command is running in background
189 #[serde(rename = "backgroundTaskId", skip_serializing_if = "Option::is_none")]
190 pub background_task_id: Option<String>,
191}
192
193#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
194#[serde(deny_unknown_fields)]
195pub struct TaskOutputInput {
196 /// The task ID to get output from
197 pub task_id: String,
198 /// Whether to wait for completion
199 #[serde(default = "default_true")]
200 #[schemars(required)]
201 pub block: bool,
202 /// Max wait time in ms
203 #[serde(default = "default_task_timeout")]
204 #[schemars(range(min = 0, max = 600000), required)]
205 pub timeout: u64,
206}
207
208#[derive(Debug, Clone, Serialize, JsonSchema)]
209#[serde(deny_unknown_fields)]
210pub struct TaskOutputResponse {
211 pub status: String,
212 pub stdout: String,
213 pub stderr: String,
214}
215
216#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
217#[serde(deny_unknown_fields)]
218pub struct TaskStopInput {
219 /// The ID of the background task to stop
220 pub task_id: Option<String>,
221 /// Deprecated: use task_id instead
222 pub shell_id: Option<String>,
223}
224
225#[derive(Debug, Clone, Serialize, JsonSchema)]
226#[serde(deny_unknown_fields)]
227pub struct TaskStopOutput {
228 /// Status message about the operation
229 pub message: String,
230}
231
232#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
233#[serde(rename_all = "snake_case")]
234pub enum NotebookCellType {
235 Code,
236 Markdown,
237}
238
239#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
240#[serde(rename_all = "snake_case")]
241pub enum NotebookEditMode {
242 Replace,
243 Insert,
244 Delete,
245}
246
247impl Default for NotebookEditMode {
248 fn default() -> Self {
249 Self::Replace
250 }
251}
252
253#[derive(Debug, Clone, Default, Deserialize, JsonSchema)]
254#[serde(deny_unknown_fields)]
255pub struct NotebookEditInput {
256 /// The absolute path to the Jupyter notebook file to edit (must be absolute, not relative)
257 pub notebook_path: String,
258 /// The ID of the target cell. Use this or cell_number. If both are provided, they must refer to the same cell, or the same insertion point in insert mode.
259 pub cell_id: Option<String>,
260 /// The 0-indexed number of the target cell. Use this or cell_id. In insert mode, the new cell is inserted at this index.
261 pub cell_number: Option<usize>,
262 /// The new source for the cell
263 pub new_source: String,
264 /// The type of the cell (code or markdown). If not specified, it defaults to the current cell type. If using edit_mode=insert, this is required.
265 pub cell_type: Option<NotebookCellType>,
266 /// The type of edit to make (replace, insert, delete). Defaults to replace.
267 #[serde(default)]
268 pub edit_mode: NotebookEditMode,
269}
270
271#[derive(Debug, Clone, Serialize, JsonSchema)]
272#[serde(deny_unknown_fields)]
273pub struct NotebookEditOutput {
274 /// Whether the notebook edit operation succeeded
275 pub success: bool,
276 /// The ID of a newly inserted cell when one is created
277 #[serde(rename = "cellId", skip_serializing_if = "Option::is_none")]
278 pub cell_id: Option<String>,
279}
280
281const fn default_true() -> bool {
282 true
283}
284
285const fn default_task_timeout() -> u64 {
286 30_000
287}