diff --git a/lua/99/providers.lua b/lua/99/providers.lua
index 2fa2f83..f9527db 100644
--- a/lua/99/providers.lua
+++ b/lua/99/providers.lua
@@ -231,23 +231,105 @@ end
--- @class CursorAgentProvider : _99.Providers.BaseProvider
local CursorAgentProvider = setmetatable({}, { __index = BaseProvider })
+--- @param context _99.Prompt
+--- @return string
+local function cursor_agent_workspace(_context)
+ return vim.fn.getcwd()
+end
+
+--- @param relative string
+--- @param ws string
+--- @return string[]
+local function cursor_agent_tmp_paths(relative, ws)
+ local seen, out = {}, {}
+
+ local function add(path)
+ path = vim.fs.normalize(vim.fn.fnamemodify(path, ":p"))
+ if not seen[path] then
+ seen[path] = true
+ out[#out + 1] = path
+ end
+ end
+
+ if relative:match("^[%a]:[/\\]") or relative:match("^/") then
+ add(relative)
+ return out
+ end
+
+ local rel = relative:gsub("^%.[\\/]", "")
+ for _, base in ipairs({ ws, vim.fn.getcwd() }) do
+ add(vim.fs.joinpath(base, rel))
+ end
+ return out
+end
+
+--- @param paths string[]
+--- @return string, string|nil
+local function cursor_agent_read_first(paths)
+ for _, path in ipairs(paths) do
+ local ok, lines = pcall(vim.fn.readfile, path)
+ if ok and #lines > 0 then
+ return table.concat(lines, "\n"), path
+ end
+ end
+ return "", nil
+end
+
+--- @param context _99.Prompt
+--- @param ws string|nil
+--- @return string
+local function cursor_agent_prompt_file(context, ws)
+ ws = ws or cursor_agent_workspace(context)
+ for _, path in ipairs(cursor_agent_tmp_paths(context.tmp_file .. "-prompt", ws)) do
+ if vim.fn.filereadable(path) == 1 then
+ return path
+ end
+ end
+ return cursor_agent_tmp_paths(context.tmp_file .. "-prompt", ws)[1]
+end
+
+--- @param context _99.Prompt
+--- @param ws string|nil
+--- @return string
+local function cursor_agent_print_prompt(context, ws)
+ local prompt_file = cursor_agent_prompt_file(context, ws)
+ return string.format(
+ "Read and follow every instruction in @%s using your file tools, then complete the task exactly as specified in that file.",
+ prompt_file
+ )
+end
+
--- @param query string
--- @param context _99.Prompt
--- @return string[]
-function CursorAgentProvider._build_command(_, query, context)
+function CursorAgentProvider._build_command(_, _query, context)
-- TODO: trust is sort of a hack and should probably be removed in favor of having a
-- trust flag from the setup call
+ local ws = cursor_agent_workspace(context)
return {
"cursor-agent",
+ "--workspace",
+ ws,
"--trust", -- directories are always trusted and can be ran in
"--force", -- allows for commands to run
"--model",
context.model,
"--print",
- query,
+ cursor_agent_print_prompt(context, ws),
}
end
+--- @param context _99.Prompt
+--- @return boolean, string
+function CursorAgentProvider:_retrieve_response(context)
+ local ws = cursor_agent_workspace(context)
+ local tmp = cursor_agent_read_first(cursor_agent_tmp_paths(context.tmp_file, ws))
+ if tmp:match("%S") then
+ return true, tmp
+ end
+ return BaseProvider._retrieve_response(self, context)
+end
+
--- @return string
function CursorAgentProvider._get_provider_name()
return "CursorAgentProvider"
diff --git a/lua/99/test/providers_spec.lua b/lua/99/test/providers_spec.lua
index 803cd05..5022c84 100644
--- a/lua/99/test/providers_spec.lua
+++ b/lua/99/test/providers_spec.lua
@@ -48,17 +48,26 @@ describe("providers", function()
end)
describe("CursorAgentProvider", function()
- it("builds correct command with model", function()
- local request = { model = "anthropic/claude-sonnet-4-5" }
- local cmd =
- Providers.CursorAgentProvider._build_command(nil, "test query", request)
- eq({
- "cursor-agent",
- "--model",
- "anthropic/claude-sonnet-4-5",
- "--print",
- "test query",
- }, cmd)
+ it("uses @ prompt file instead of XML on argv", function()
+ local request = { model = "sonnet-4.5", tmp_file = "tmp/99-1234" }
+ local cmd = Providers.CursorAgentProvider._build_command(
+ nil,
+ "ignored",
+ request
+ )
+ local print_arg = cmd[#cmd]
+ eq("cursor-agent", cmd[1])
+ eq("--workspace", cmd[2])
+ eq(vim.fn.getcwd(), cmd[3])
+ eq("--trust", cmd[4])
+ eq("--force", cmd[5])
+ eq("--model", cmd[6])
+ eq("sonnet-4.5", cmd[7])
+ eq("--print", cmd[8])
+ assert(print_arg:match("@"))
+ assert(print_arg:match("99%-1234%-prompt"))
+ assert(not print_arg:match(""))
+ assert(not print_arg:match("\n"))
end)
it("has correct default model", function()