Skip to main content

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}