import { tool } from "@lmstudio/sdk";
import { z } from "zod";
import { readFile, writeFileSync, getFileStats, listDirectory, ensureDirectory, deleteFileSync, zipCreate, zipExtract, zipList, diffFiles } from "../fileUtilities";
import { sanitizeFilePath, validateWritePath } from "../securityEnhanced";
import { logger } from "../logger";
export async function registerFileTools(): Promise<any[]> {
const tools: any[] = [];
tools.push(tool({
name: "file_write",
description: "Write content to a file.",
parameters: {
path: z.string().describe("File path to write to"),
content: z.string().describe("Content to write"),
overwrite: z.boolean().optional().describe("Overwrite if file exists (default: true)"),
create_dirs: z.boolean().optional().describe("Create parent directories (default: true)"),
},
implementation: async ({ path: filePath, content, overwrite, create_dirs }) => {
try {
const validation = validateWritePath(filePath);
if (!validation.valid) {
return { success: false, path: filePath, error: validation.error };
}
const safePath = validation.sanitized!;
const fs = require("fs");
const pathMod = require("path");
if (create_dirs !== false) {
const dir = pathMod.dirname(safePath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}
if (overwrite === false && fs.existsSync(safePath)) {
return { success: false, path: safePath, error: "File already exists and overwrite is false" };
}
writeFileSync(safePath, content);
const stats = getFileStats(safePath);
return { success: true, path: safePath, size: stats.size };
} catch (err: any) {
logger.error('file_write failed', { error: err.message });
return { success: false, path: filePath, error: err.message };
}
},
}));
tools.push(tool({
name: "read_file",
description: "Read the contents of a file.",
parameters: { path: z.string().describe("File path to read") },
implementation: async ({ path: filePath }) => {
try {
const validation = sanitizeFilePath(filePath);
if (!validation.valid) {
return { success: false, content: "", error: validation.error };
}
const safePath = validation.sanitized!;
const content = readFile(safePath);
return { success: true, content };
} catch (err: any) {
logger.error('read_file failed', { error: err.message });
return { success: false, content: "", error: err.message };
}
},
}));
tools.push(tool({
name: "file_diff",
description: "Compare two files.",
parameters: { file1: z.string().describe("First file"), file2: z.string().describe("Second file") },
implementation: async ({ file1, file2 }) => {
try {
const v1 = sanitizeFilePath(file1);
if (!v1.valid) return { success: false, diff: "", error: `file1: ${v1.error}` };
const v2 = sanitizeFilePath(file2);
if (!v2.valid) return { success: false, diff: "", error: `file2: ${v2.error}` };
const diff = diffFiles(v1.sanitized!, v2.sanitized!);
const lines = diff.split("\n");
return { success: true, diff, diff_lines: lines.length, truncated: diff.length > 10000 };
} catch (err: any) {
logger.error('file_diff failed', { error: err.message });
return { success: false, diff: "", error: err.message };
}
},
}));
tools.push(tool({
name: "file_checksum",
description: "Calculate file hash.",
parameters: { path: z.string().describe("File path"), algorithm: z.enum(["sha256", "md5"]).optional() },
implementation: async ({ path: filePath, algorithm }) => {
try {
const validation = sanitizeFilePath(filePath);
if (!validation.valid) {
return { success: false, checksum: "", error: validation.error };
}
const safePath = validation.sanitized!;
const result = await require("../fileUtilities").fileChecksum(safePath, algorithm ?? "sha256");
return { success: true, checksum: result };
} catch (err: any) {
logger.error('file_checksum failed', { error: err.message });
return { success: false, checksum: "", error: err.message };
}
},
}));
tools.push(tool({
name: "file_stats",
description: "Get file metadata.",
parameters: { path: z.string().describe("File path") },
implementation: async ({ path: filePath }) => {
try {
const validation = sanitizeFilePath(filePath);
if (!validation.valid) {
return { success: false, stats: null, error: validation.error };
}
const safePath = validation.sanitized!;
const stats = getFileStats(safePath);
return { success: true, stats };
} catch (err: any) {
logger.error('file_stats failed', { error: err.message });
return { success: false, stats: null, error: err.message };
}
},
}));
tools.push(tool({
name: "list_directory",
description: "List directory contents.",
parameters: {
path: z.string().describe("Directory path"),
recursive: z.boolean().optional().describe("Recursive"),
show_hidden: z.boolean().optional().describe("Show hidden"),
max_entries: z.number().optional().describe("Max entries"),
},
implementation: async ({ path: dirPath, recursive, show_hidden, max_entries }) => {
try {
const validation = sanitizeFilePath(dirPath);
if (!validation.valid) {
return { success: false, path: dirPath, entries: [], error: validation.error };
}
const safePath = validation.sanitized!;
const entries = listDirectory(safePath, { recursive: recursive ?? false })
.filter(e => show_hidden ? true : !e.name.startsWith('.'));
const total = entries.length;
const max = max_entries ?? 100;
if (max === 0) {
return {
success: true,
path: dirPath,
summary: true,
total_entries: total,
directories: entries.filter(e => e.isDirectory).length,
files: entries.filter(e => !e.isDirectory).length,
entries: []
};
}
const limited = entries.slice(0, max);
return {
success: true,
path: dirPath,
total_entries: total,
shown: limited.length,
truncated: total > max,
entries: limited
};
} catch (err: any) {
logger.error('list_directory failed', { error: err.message });
return { success: false, path: dirPath, entries: [], error: err.message };
}
},
}));
tools.push(tool({
name: "create_directory",
description: "Create a directory.",
parameters: { path: z.string().describe("Path"), recursive: z.boolean().optional() },
implementation: async ({ path: dirPath, recursive }) => {
try {
const validation = validateWritePath(dirPath);
if (!validation.valid) {
return { success: false, path: dirPath, error: validation.error };
}
const safePath = validation.sanitized!;
if (recursive === false) {
const fs = require("fs");
if (fs.existsSync(safePath) && !fs.statSync(safePath).isDirectory()) {
return { success: false, path: safePath, error: "Path exists and is not a directory" };
}
fs.mkdirSync(safePath);
} else {
ensureDirectory(safePath);
}
return { success: true, path: safePath };
} catch (err: any) {
logger.error('create_directory failed', { error: err.message });
return { success: false, path: dirPath, error: err.message };
}
},
}));
tools.push(tool({
name: "delete_file",
description: "Delete file or directory.",
parameters: { path: z.string().describe("Path"), recursive: z.boolean().optional(), missing_ok: z.boolean().optional() },
implementation: async ({ path: filePath, recursive, missing_ok }) => {
try {
const validation = validateWritePath(filePath);
if (!validation.valid) {
return { success: false, path: filePath, error: validation.error };
}
const safePath = validation.sanitized!;
const fs = require("fs");
if (!fs.existsSync(safePath)) {
return missing_ok === true
? { success: true, path: safePath, deleted: false, message: "File not found" }
: { success: false, path: safePath, deleted: false, error: "File not found" };
}
const stat = fs.statSync(safePath);
if (stat.isDirectory()) {
fs.rmSync(safePath, { recursive: recursive ?? true, force: missing_ok === true });
} else {
fs.unlinkSync(safePath);
}
return { success: true, path: safePath, deleted: true };
} catch (err: any) {
logger.error('delete_file failed', { error: err.message });
return { success: false, path: filePath, error: err.message };
}
},
}));
tools.push(tool({
name: "delete_directory",
description: "Delete a directory.",
parameters: { path: z.string().describe("Path"), recursive: z.boolean().optional(), missing_ok: z.boolean().optional() },
implementation: async ({ path: dirPath, recursive, missing_ok }) => {
try {
const validation = validateWritePath(dirPath);
if (!validation.valid) {
return { success: false, path: dirPath, error: validation.error };
}
const safePath = validation.sanitized!;
const fs = require("fs");
if (!fs.existsSync(safePath)) {
return { success: true, path: safePath, deleted: false, message: "Directory not found" };
}
fs.rmSync(safePath, { recursive: recursive ?? true, force: missing_ok === true });
return { success: true, path: safePath, deleted: true };
} catch (err: any) {
logger.error('delete_directory failed', { error: err.message });
return { success: false, path: dirPath, error: err.message };
}
},
}));
return tools;
}