Better FS Tools
Comparisons

Groq Code CLI

Read, edit, shell, and search tools in Groq Code CLI at 1.0.2, with every model-facing description in full

Groq Code CLI is an open-source terminal coding agent published by Groq as a small codebase for developers to modify. It ships seven filesystem and shell tools as OpenAI-style function schemas, read_file, create_file, edit_file, delete_file, list_files, search_files, and execute_command, plus two task-list tools, and sends all nine on every request. Every description is one line under 380 characters with a worked JSON example. Tool results travel to the model as one JSON object with success, content, message, and error fields, and edit_file is refused until the same session has read the file.

Source

ItemValue
Repositorybuild-with-groq/groq-code-cli
Commita303eb4be01a53aaf3fbf319636e2b608e80aeca on main, committed 2025-09-05
Last updated2026-09-19

Tool inventory

ToolPurposeDefaultNotes
read_fileRead a whole text file or a one-based line rangeYesNo prompt
create_fileCreate a file or directory; replace a file with overwriteYesPrompt, unless session auto-approve is on
edit_fileReplace one exact string, or all occurrencesYesPrompt, unless session auto-approve is on; refused until the file was read
delete_fileDelete a file or directory inside the working directoryYesAlways prompts
list_filesOne-level tree of a directoryYesNo prompt
search_filesRegex, substring, exact, or fuzzy search over text filesYesNo prompt
execute_commandRun one command through child_process.execYesAlways prompts

ALL_TOOL_SCHEMAS in src/tools/tool-schemas.ts goes whole into every chat completion with tool_choice: 'auto'. The same file defines three classes: SAFE_TOOLS run without a prompt, APPROVAL_REQUIRED_TOOLS prompt unless the user chose "don't ask again this session", and DANGEROUS_TOOLS prompt every time. create_tasks and update_tasks are also in the set; they manage an in-memory task list and are outside this page. Tool calls in one assistant turn run one after another; a rejection stops the rest and ends the turn.

Read

Signature

read_file({ file_path: string, start_line?: number, end_line?: number })

start_line and end_line are one-based, inclusive, with minimum: 1 in the schema. There is no limit and no default window: a call without start_line returns the whole file.

Description

read_file: 218 characters, 218 bytes, 1 line, approximately 52 tokens.

read_file (218 characters)

Read file contents with optional line range. REQUIRED before edit_file. Use to check if files exist and examine current code before making changes. Example: {"file_path": "src/app.js", "start_line": 10, "end_line": 20}

Path handling

path.resolve(file_path) against the process working directory. The parameter description says relative paths only and no leading slash, but the code accepts an absolute path and reads it. A leading slash on a relative path resolves to the filesystem root and fails with Error: File not found. There is no ~ expansion, no URL handling, no unicode normalisation, no containment check, and no near-miss suggestion: None. Symlinks are followed; the tool uses access, stat, and readFile with no lstat.

Permissions

CheckBehaviour
Path rulesNone. No allow or deny list, no ignore rules on read
ApprovalNone. read_file is in SAFE_TOOLS
Device and special filesNone. stats.isFile() must be true, which excludes directories and FIFOs but not symlinks to regular files
Denial textNot applicable

Binary and conversion

AspectBehaviour
DetectionNone on read. search_files skips a binary extension list; read_file does not use it
RefusalNone. Every file decodes as UTF-8 with replacement characters
NotebooksNone
ImagesNone
PDFNone
Audio and videoNone
EncodingUTF-8 only; no BOM handling

Limits

LimitValueMessage
Default windowNone. Whole file
CeilingNone on lines or bytes returned
Long lineNone
Whole-file size50 MiB, checked with stat before the readError: File too large (max 50MB)
StreamingNone. Whole file into memory, then split('\n')
start_line past endErrorError: Start line exceeds file length
end_line past endClamped to the last line, no notice

split('\n') on a file with a trailing newline yields one extra empty element. A whole read of a three-line file reports Read 4 lines, and start_line equal to the line count plus one returns empty content with Read lines 4-4 instead of an error.

Output

The tool returns an object and agent.ts pushes JSON.stringify(result) as the tool message. The model sees a JSON string.

{"success":true,"content":"import { x } from \"./x\"\nexport const y = x\n","message":"Read 3 lines from src/x.ts"}
AspectBehaviour
Prefix before contentNone inside content. The message field carries Read <n> lines from <file_path> or Read lines <a>-<b> from <file_path>
Line prefixNone. Lines are bare
End markerNone
Truncation noticeNone. Nothing is truncated below 50 MiB
Error transportsuccess: false and error: "<text>" in the same JSON object; no error flag at the API level

The example is reconstructed from the format code. A ranged read joins the selected lines with \n.

Notable

  • Every successful read, whole or ranged, adds the resolved path to a session Set that gates edit_file. The set is not cleared by /clear.
  • Empty file: success: true, content: "", message: "Read 1 lines from <file_path>".
  • No repeat-read guard, no content injection after the read, no redaction.
  • Cancellation: Agent.interrupt() aborts the HTTP request and sets a flag checked between tool calls. A running tool is not interrupted.
  • /init writes .groq/context.md; the agent loads it as a second system message at startup, capped at 20000 characters by GROQ_CONTEXT_LIMIT.

Edit

Signature

create_file({ file_path: string, content: string, file_type?: "file" | "directory", overwrite?: boolean })
edit_file({ file_path: string, old_text: string, new_text: string, replace_all?: boolean })
delete_file({ file_path: string, recursive?: boolean })

file_type defaults to file, overwrite, replace_all, and recursive to false. One replacement per edit_file call. There is no array of edits and no patch grammar.

Description

create_file: 376 characters, 376 bytes, 1 line, approximately 83 tokens. edit_file: 299 characters, 299 bytes, 1 line, approximately 73 tokens. delete_file: 138 characters, 138 bytes, 1 line, approximately 36 tokens.

create_file (376 characters)

Create NEW files or directories that DO NOT EXIST. CRITICAL: Always check if file exists first using list_files or read_file before creating. If file exists, use edit_file instead. Set overwrite=true only if you explicitly need to replace existing content. Example: {"file_path": "src/utils/new-helper.js", "content": "function helper() { return true; }", "file_type": "file"}

edit_file (299 characters)

Modify EXISTING files by exact text replacement. Use this for files that already exist. MANDATORY: Always read_file first to see current content before editing. Text must match exactly including whitespace. Example: {"file_path": "src/app.js", "old_text": "const x = 1;", "new_text": "const x = 2;"}

delete_file (138 characters)

Remove files or directories. Use with caution. Example: {"file_path": "temp/old_file.txt"} or {"file_path": "temp_dir", "recursive": true}

Whole-file write

create_file with file_type: "file" checks existence first and refuses with Error: File already exists, use overwrite=true unless overwrite is set. writeFile() in src/utils/file-ops.ts creates parent directories recursively and calls fs.promises.writeFile with UTF-8. The destination is overwritten in place: no temporary file, no rename, no backup (a backup argument exists and is unused), no BOM or line-ending handling, no post-write verification. Mode preservation: None; a replaced file gets the default mode of a fresh write. file_type: "directory" calls mkdir recursive. create_file with overwrite: true is not gated by the read-before-edit check.

Matching

editFile() reads the file and applies String.prototype.replace(old_text, new_text), or split(old_text).join(new_text) with replace_all. Exact string match only. There is no whitespace or unicode normalisation, no fuzzy fallback, and no uniqueness rule: the first occurrence is replaced when several exist. A comment in tools.ts says a validation system has already confirmed old_text exists and is unambiguous; no such check exists in agent.ts or validators.ts. An absent old_text writes the file unchanged and reports success. Because the single-replacement path uses replace with a string pattern, $&, $1, and similar sequences in new_text are interpreted as replacement patterns.

Multi-file patch

None. Each call touches one path.

Verification and mutation state

ConcernBehaviour
Read-before-writeedit_file only. validateReadBeforeEdit() looks up path.resolve(file_path) in the session read set before the approval prompt. Failure returns File must be read before editing. Use read_file tool first: <file_path>. create_file with overwrite and delete_file are not gated
Stale-file checkNone. No mtime, hash, or content comparison against the earlier read
LockNone
Post-write checksNone
RollbackNone

Output

ToolModel text
create_fileFile created: <file_path>; for a directory Directory created: <file_path> plus a content object with the absolute path and type: "directory"
edit_fileReplaced <n> occurrence(s) in <file_path>. Without replace_all, n is always 1; with it, n is the split count
delete_fileDeleted file: <file_path> or Deleted directory: <file_path>

The approval overlay renders a diff preview and the history view renders a synthetic diff. Neither reaches the model.

Notable

  • No notebook editor, undo, or checkpoint.
  • delete_file refuses the working directory itself and any resolved path that does not start with the working directory string. The other mutation tools have no containment check.
  • The default system prompt tells the model to use create_file with overwrite=true to replace a file completely, which bypasses the read gate.

Shell

Signature

execute_command({ command: string, command_type: "bash" | "python" | "setup" | "run", working_directory?: string, timeout?: number })

The schema describes timeout as seconds in the range 1 to 300. The implementation passes the value to child_process.exec unchanged, where it is milliseconds, and defaults to 30000 when absent. command_type: "python" wraps the command as python -c "<command>" with double quotes escaped. The other three values pass the command through unchanged; the setup (auto-run) and run (needs approval) labels in the parameter description have no matching code path.

Description

execute_command: 367 characters, 367 bytes, 1 line, approximately 80 tokens.

execute_command (367 characters)

Run shell commands, scripts, or code. SAFETY WARNING: Only use for commands that COMPLETE and EXIT (test scripts, build commands, short-running scripts). NEVER use for commands that run indefinitely (flask server, node app starting, python -m http.server, etc.). Always prefer short-running commands that exit. Example: {"command": "npm test", "command_type": "bash"}

Working directory and environment

The command runs in the CLI process working directory. working_directory is resolved, checked with access, then applied with process.chdir() for the call and restored in a finally; a missing directory returns Error: Working directory not found. The environment is the CLI process environment. Shell selection is the Node exec default, /bin/sh -c on POSIX and cmd.exe /d /s /c on Windows. Login shell: None. Windows: Not surveyed.

Process lifecycle

AspectBehaviour
ModelOne exec per call, awaited to exit
BackgroundNone
stdinNone
CancellationNone from the agent loop. Timeout sends SIGTERM; a kill on SIGTERM maps to Error: Command timed out
GrandchildrenUnverified. exec without detached kills the shell only

Output limits

AspectBehaviour
Collectionexec buffers stdout and stderr in memory with the Node default maxBuffer of 1 MiB per stream
Model-visible budgetNone. The whole buffer is returned as content: "stdout: <stdout>\nstderr: <stderr>"
Truncation textNone. Exceeding maxBuffer rejects the promise and returns Error: Failed to execute command
Spill fileNone
Empty outputstdout: \nstderr:

Sandbox

None. The child runs as the CLI process account with the same filesystem and unrestricted network.

Approval

execute_command is in DANGEROUS_TOOLS. Every call prompts with two options, Yes and No, tell Groq what to do differently (esc); the session auto-approve option is not shown. Without an approval callback the agent rejects by default. A rejection returns Tool execution canceled by user with userRejected: true, and the loop appends a system message telling the model the response has been terminated. There is no allow list, deny list, or per-command rule.

Notable

  • A non-zero exit rejects the exec promise. The catch returns Error: Failed to execute command and drops stdout, stderr, and the exit code.
  • No PTY, no streaming to the model, no interactive input.
  • The approval prompt shows command: "<command>" truncated to 47 characters plus ... when longer.

Search and listing

Tools

ToolArgumentsDefault capsIgnore rules
list_filesdirectory?: string (.), pattern?: string (*), recursive?: boolean, show_hidden?: booleanNoneIGNORE_PATTERNS from src/utils/constants.ts: node_modules, .git, __pycache__, venv, .venv, build, dist, .idea, .vscode, .DS_Store, *.pyc, *.log, *.tmp, matched as substrings of the absolute path or against the basename. Dotfiles hidden unless show_hidden; even then only .env, .gitignore, and .dockerfile pass the ignore check
search_filespattern: string, file_pattern? (*), directory? (.), case_sensitive? (false), pattern_type? (substring, regex, exact, fuzzy), file_types?, exclude_dirs?, exclude_files?, max_results? (100, max 1000), context_lines? (0, max 10), group_by_file?100 matching linesDirectories .git, node_modules, .next, dist, build, .cache plus exclude_dirs; files *.log, *.tmp, *.cache, *.lock plus exclude_files; hidden directories skipped except .config and .env; hidden files searched; files with a listed binary extension skipped

list_files calls displayTree() in src/utils/file-ops.ts, which accepts pattern and recursive and uses neither. It lists the direct children of one directory, directories first, then case-insensitive alphabetical, as a tree under a <basename>/ header with ├── and └── prefixes and a trailing / on directories. shouldIgnore() tests pathStr.includes(pattern) for patterns without *, so .gitignore never appears because its absolute path contains .git, and a project whose path contains build or dist lists as empty.

search_files walks the tree in TypeScript, reads each file as UTF-8, and tests each line with a global regex built from pattern_type. file_pattern, exclude_dirs, and exclude_files are matched against the basename only, with * and ? converted to regex and case folded; a file_pattern with a directory segment matches nothing. max_results counts matching lines across files. content is an array of objects with filePath relative to the working directory, lineNumber, lineContent, contextLines when requested, and matchPositions; with group_by_file it is one object per file. Zero files returns content: [] with No files found matching criteria; zero matches in searched files returns content: [] with Found 0 match(es) in 0 file(s).

Description

search_files: 196 characters, 196 bytes, 1 line, approximately 45 tokens. list_files: 218 characters, 218 bytes, 1 line, approximately 46 tokens.

search_files (196 characters)

Find text patterns in files across the codebase. Perfect for locating functions, classes, or specific code. Example: {"pattern": "function handleClick", "file_pattern": "*.js", "context_lines": 3}

list_files (218 characters)

Browse directory contents and file structure. Use to explore project layout and CHECK IF FILES EXIST before deciding between create_file vs edit_file. Example: {"directory": "src", "pattern": "*.js", "recursive": true}

Errors

Every failure is success: false with an error string in the JSON tool message. One row per distinct string in src/tools/tools.ts, src/tools/validators.ts, and src/core/agent.ts.

ConditionMessage shapeTransport
Missing file on readError: File not foundJSON error field
Directory or special file on readError: Path is not a fileJSON error field
File too largeError: File too large (max 50MB)JSON error field
start_line past endError: Start line exceeds file lengthJSON error field
Other read failureError: Failed to read fileJSON error field
Create target existsError: File already exists, use overwrite=trueJSON error field
Create failedError: Failed to create file; Error: Failed to create directory; Error: Failed to create file or directoryJSON error field
Create bad file_typeError: Invalid file_type, must be 'file' or 'directory'JSON error field
Edit before readFile must be read before editing. Use read_file tool first: <file_path>JSON error field
Edit match failedNone. The file is rewritten unchanged and Replaced 1 occurrence(s) in <file_path> is returnedSuccessful result
Edit not uniqueNone. The first occurrence is replacedSuccessful result
Edit write failedError: Failed to write changes to file; Error: Failed to edit file - <error>JSON error field
Stale fileNone. Not detected
Delete refusedError: Cannot delete the root project directory; Error: Cannot delete files outside the project directory; Error: Directory not empty, use recursive=trueJSON error field
Delete missing or failedError: Path not found; Error: Failed to deleteJSON error field
List or search directory missingError: Directory not foundJSON error field
List or search path not a directoryError: Path is not a directoryJSON error field
List or search failedError: Failed to list files; Error: Failed to search filesJSON error field
Search bad regexError: Invalid regex patternJSON error field
Command bad command_typeError: Invalid command_typeJSON error field
Command missing working_directoryError: Working directory not foundJSON error field
Command timeoutError: Command timed outJSON error field
Command non-zero exit, buffer overflow, or other failureError: Failed to execute commandJSON error field
Command deniedTool execution canceled by user with userRejected: true; then a system message The user rejected the <tool> tool execution. The response has been terminated. Please wait for the user's next instruction.JSON error field, then a system message
Interrupted during approvalTool execution interrupted by user with userRejected: trueJSON error field
Arguments not valid JSONTool arguments truncated: <error>. Please break this into smaller pieces or use shorter content.JSON error field
Dispatch failuresError: Unknown tool; Error: Tool not implemented; Error: Invalid tool arguments; Error: Unexpected tool error; Tool execution error: <error>JSON error field

Twenty-seven rows, above the outline's fifteen. Each row is a distinct string or a distinct absence.

On this page