Ask for the Fix

@rebind / ask-claude-for-the-fixv0.1.3Public

Copy an error or a sentence, press F8, and Claude's answer is pasted where you are. Uses your own Anthropic API key.

Files

Older release. Installs get 0.1.3.

main.luau2.5 KB

-- Ask for the Fix: send what you copied to Claude with a short instruction
-- and paste the answer where you are. Uses your own Anthropic API key.

local cfg = UI.Schema({
  key = UI.Keybind("F8", { label = "Ask and paste the answer" }),
  apiKey = UI.Text("", { label = "Anthropic API key" }),
  prompt = UI.Text(
    "Fix or improve this. Reply with only the corrected text:",
    {
      label = "Instruction sent before your text",
    }
  ),
})

local busy = false

local function answerText(body)
  local ok, data = pcall(JSON.Parse, body)
  if not ok or type(data) ~= "table" then
    return nil
  end
  for _, block in data.content or {} do
    if block.type == "text" then
      return block.text
    end
  end
  return nil
end

local function errorMessage(body)
  local ok, data = pcall(JSON.Parse, body)
  if ok and type(data) == "table" and data.error and data.error.message then
    return data.error.message
  end
  return "no answer"
end

Bind(
  cfg.key,
  Async(function()
    if busy then
      return
    end
    if cfg.apiKey == "" then
      UI.Notify("Add your Anthropic API key in the package options", "warning")
      return
    end
    local text = Clipboard.Get() or ""
    if text == "" then
      UI.Notify("Copy something first", "warning")
      return
    end
    busy = true
    UI.Notify("Asking Claude", "info")
    local ok, resp = pcall(
      Net.Post,
      "https://api.anthropic.com/v1/messages",
      JSON.Stringify({
        model = "claude-sonnet-5-5",
        max_tokens = 4096,
        messages = { { role = "user", content = cfg.prompt .. "\n\n" .. text } },
      }),
      {
        ["x-api-key"] = cfg.apiKey,
        ["anthropic-version"] = "2023-06-01",
        ["content-type"] = "application/json",
      },
      { timeout = 60000 }
    )
    busy = false
    if not ok then
      UI.Notify("Could not reach Claude: " .. tostring(resp), "error")
      return
    end
    if
      resp.status == 200 and resp.body:find('"stop_reason"%s*:%s*"max_tokens"')
    then
      UI.Notify(
        "Claude's answer was too long and was cut off, so nothing was pasted",
        "warning"
      )
      return
    end
    local answer = if resp.status == 200 then answerText(resp.body) else nil
    if not answer then
      UI.Notify(
        `Claude did not answer ({resp.status}): {errorMessage(resp.body)}`,
        "error"
      )
      return
    end
    Clipboard.Set(answer)
    HID.Press(if _REBIND.platform == "macos" then "LWin+V" else "LCtrl+V")
  end)
)

3 files, 4.6 KB.