read_file
Read the contents of a file from the workspace.
Parameters
Path to the file, relative to workspace root or absolute. Accepts alternative keys: file, filepath, file_path, filename.
Returns
File contents as UTF-8 string, or an error message prefixed with "Error: ".
Behavior
- Resolves relative paths against the workspace root
- Validates path stays within workspace boundaries
- Rejects files larger than 1 MB
- Returns canonical error messages for missing files
Examples
{
"path": "src/config.ts"
}
Implementation Details
From voice-tools.ts:236-272:
async function readFile(
args: Record<string, unknown>,
workspaceRoot: string
): Promise<string> {
const path = extractStringArg(args, [
"path",
"file",
"filepath",
"file_path",
"filename",
]);
if (!path) {
return "Error: Missing required parameter 'path'";
}
const resolved = await resolvePath(path, workspaceRoot);
if (!resolved) {
return "Error: Path escapes workspace root";
}
if (!existsSync(resolved)) {
return `Error: File not found at '${path}'`;
}
try {
const s = statSync(resolved);
if (s.size > MAX_FILE_BYTES) {
return `Error: File exceeds 1MB limit (${s.size} bytes)`;
}
return readFileSync(resolved, "utf8");
} catch (err) {
return `Error: Failed to read file: ${String(err)}`;
}
}
write_file
Create a new file or overwrite an existing file with the provided content.
This tool is disabled in safe mode. Attempting to use it will return an error.
Parameters
Path to the file, relative to workspace root or absolute. Accepts alternative keys: file, filepath, file_path, filename.
UTF-8 content to write to the file.
Returns
Success message with byte count, or an error message prefixed with "Error: ".
Behavior
- Creates parent directories automatically using
mkdir -p semantics
- Overwrites existing files without warning
- Validates path stays within workspace boundaries
- Returns error if safe mode is enabled
Examples
{
"path": "src/config.ts",
"content": "export const API_KEY = 'sk-test';\n"
}
Implementation Details
From voice-tools.ts:274-316:
async function writeFile(
args: Record<string, unknown>,
workspaceRoot: string,
safeMode: boolean
): Promise<string> {
if (safeMode) {
return "Error: write_file is disabled in safe mode";
}
const path = extractStringArg(args, [
"path",
"file",
"filepath",
"file_path",
"filename",
]);
if (!path) {
return "Error: Missing required parameter 'path'";
}
const content = args.content;
if (typeof content !== "string") {
return "Error: Missing required parameter 'content'";
}
const resolved = await resolvePath(path, workspaceRoot);
if (!resolved) {
return "Error: Path escapes workspace root";
}
try {
mkdirSync(dirname(resolved), { recursive: true });
const data = Buffer.from(content, "utf8");
writeFileSync(resolved, data);
return `Successfully wrote ${data.byteLength} bytes to ${path}`;
} catch (err) {
return `Error: Failed to write file: ${String(err)}`;
}
}
edit_file
Make a targeted edit to an existing file using find-and-replace.
This tool is disabled in safe mode. Attempting to use it will return an error.
Parameters
Path to the file, relative to workspace root or absolute. Accepts alternative keys: file, filepath, file_path, filename.
Exact text to find in the file. Accepts alternative keys: oldText, find. Must match exactly once.
Replacement text. Accepts alternative keys: newText, replace.
Returns
Success message, or an error message prefixed with "Error: ".
Behavior
- Requires exact match of
old_text (case-sensitive)
- Rejects ambiguous edits if
old_text appears multiple times
- Returns error if
old_text is not found
- Atomic operation: writes only if validation passes
Examples
{
"path": "src/config.ts",
"old_text": "timeout: 5000",
"new_text": "timeout: 10000"
}
Implementation Details
From voice-tools.ts:318-373:
async function editFile(
args: Record<string, unknown>,
workspaceRoot: string,
safeMode: boolean
): Promise<string> {
if (safeMode) {
return "Error: edit_file is disabled in safe mode";
}
const path = extractStringArg(args, [
"path",
"file",
"filepath",
"file_path",
"filename",
]);
if (!path) {
return "Error: Missing required parameter 'path'";
}
const oldText = extractStringArg(args, ["old_text", "oldText", "find"]);
if (!oldText) {
return "Error: Missing required parameter 'old_text'";
}
const newText = extractStringArg(args, ["new_text", "newText", "replace"]);
if (!newText) {
return "Error: Missing required parameter 'new_text'";
}
const resolved = await resolvePath(path, workspaceRoot);
if (!resolved) {
return "Error: Path escapes workspace root";
}
try {
const content = readFileSync(resolved, "utf8");
const occurrences = content.split(oldText).length - 1;
if (occurrences === 0) {
return "Error: old_text not found in file";
}
if (occurrences > 1) {
return `Error: old_text found ${occurrences} times (ambiguous edit)`;
}
const updated = content.replace(oldText, newText);
writeFileSync(resolved, updated, "utf8");
return `Successfully edited ${path}`;
} catch (err) {
return `Error: Failed to edit file: ${String(err)}`;
}
}
Best Practices
Use edit_file for targeted changes
Prefer edit_file over write_file when making small modifications. This preserves file history and reduces risk of data loss.// Good: Targeted edit
{
"path": "package.json",
"old_text": "\"version\": \"1.0.0\"",
"new_text": "\"version\": \"1.1.0\""
}
Include enough context in old_text
Make old_text unique to avoid ambiguity errors. Include surrounding context if needed.// Bad: Ambiguous
{
"old_text": "const x = 1;"
}
// Good: Unique context
{
"old_text": "// Config\nconst x = 1;"
}
Always check for "Error: " prefix in results:const result = await executeVoiceTool("read_file", args, workspace);
if (result.startsWith("Error: ")) {
console.error("Tool failed:", result);
return;
}
// Use result...
Bash
Execute shell commands for complex file operations
Search
Find files before reading or editing them