From 48651e123ef0cf1d02eac781902517c0628b310a Mon Sep 17 00:00:00 2001 From: T Date: Wed, 27 May 2026 17:07:49 -0600 Subject: real coding agent tools --- agent/tools/write.lua | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 agent/tools/write.lua (limited to 'agent/tools/write.lua') diff --git a/agent/tools/write.lua b/agent/tools/write.lua new file mode 100644 index 0000000..70aa5c8 --- /dev/null +++ b/agent/tools/write.lua @@ -0,0 +1,81 @@ +-- Write text to a file, creating parent directories as needed. Pi-style +-- terse description because every byte of every tool description rides +-- in the system prompt on every turn. + +local uv = require("luv") + +-- mkdir -p equivalent: walk the path components and create each. +-- Ignores EEXIST so the call is idempotent. +local function mkdir_p(path) + if path == "" or path == "." or path == "/" then return true end + local parts = {} + for piece in string.gmatch(path, "[^/]+") do + parts[#parts + 1] = piece + end + local cur = (path:sub(1, 1) == "/") and "" or "." + for _, piece in ipairs(parts) do + cur = cur .. "/" .. piece + local ok, err, name = uv.fs_mkdir(cur, tonumber("755", 8)) + if not ok and name ~= "EEXIST" then + return nil, err + end + end + return true +end + +-- Return the dirname of `path`, or nil if there isn't one (basename only). +local function dirname(path) + local slash = path:find("/[^/]*$") + if not slash or slash == 1 then return nil end + return path:sub(1, slash - 1) +end + +return { + name = "write", + description = "Save content to a path. Overwrites existing files; missing parent directories are created automatically.", + schema = { + type = "object", + properties = { + path = { type = "string", description = "Path to the file (relative or absolute)." }, + content = { type = "string", description = "Exact bytes to write. No newline is added." }, + }, + required = { "path", "content" }, + }, + handler = function(input) + local path = input.path + local content = input.content + + if type(path) ~= "string" or path == "" then + return "Error: `path` must be a non-empty string." + end + if type(content) ~= "string" then + return "Error: `content` must be a string." + end + + local parent = dirname(path) + if parent then + local ok, err = mkdir_p(parent) + if not ok then + return "Error: could not create parent directory " .. parent .. ": " .. tostring(err) + end + end + + local f, open_err = io.open(path, "wb") + if not f then + return "Error: " .. (open_err or ("could not open " .. path .. " for writing")) + end + + local ok, write_err = f:write(content) + if not ok then + f:close() + return "Error: write failed: " .. tostring(write_err) + end + + local close_ok, close_err = f:close() + if not close_ok then + return "Error: close failed: " .. tostring(close_err) + end + + return string.format("Wrote %d bytes to %s.", #content, path) + end, +} -- cgit v1.3