Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/nvim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
nvim-version: ["v0.11.0"]
nvim-version: ["v0.11.5"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
Expand All @@ -34,8 +34,9 @@ jobs:
# Try to start nvim, source the config, and quit
timeout 30s nvim --headless -c 'quit' 2>&1 | tee nvim_startup.log

# Check if there were any errors
if grep -i "error" nvim_startup.log; then
# Check for actual Neovim errors (E123: style or "Error detected" messages)
# Excludes false positives from plugin git output containing "error" in commit messages
if grep -P "^E\d+:|^Error|error:" nvim_startup.log; then
echo "Errors found in Neovim startup"
exit 1
fi
Expand Down
120 changes: 120 additions & 0 deletions hammerspoon/Spoons/ClaudeRewriter.spoon/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
local obj = {}
obj.__index = obj

obj.name = "ClaudeRewriter"
obj.version = "1.0"
obj.author = "Daniel Abeles"
obj.homepage = "https://github.com/Den1al/dotfiles"
obj.license = "MIT - https://opensource.org/licenses/MIT"

obj.claudePath = os.getenv("HOME") .. "/.local/bin/claude"
obj.model = "sonnet"
obj.systemPrompt =
"Rewrite the following text for clarity and correct grammar. Preserve the original tone, intent, and meaning. Return ONLY the rewritten text with no preamble or explanation. Don't use emdashes."
obj.alertDuration = 1.5

local hotkeys = {}
local rewriting = false

local function getSelectedText()
local systemElement = hs.axuielement.systemWideElement()
local focusedElement = systemElement:attributeValue("AXFocusedUIElement")
if not focusedElement then
return nil
end
return focusedElement:attributeValue("AXSelectedText")
end

function obj:init()
return self
end

function obj:bindHotkeys(mapping)
for action, bind in pairs(mapping) do
local mods, key = bind[1], bind[2]
local paste = action == "rewrite"
hotkeys[#hotkeys + 1] = hs.hotkey.new(mods, key, function()
self:rewrite(paste)
end)
end
return self
end

function obj:start()
for _, hk in ipairs(hotkeys) do
hk:enable()
end
return self
end

function obj:stop()
for _, hk in ipairs(hotkeys) do
hk:disable()
end
return self
end

function obj:rewrite(pasteInPlace)
if rewriting then
hs.alert.show("Rewrite in progress", self.alertDuration)
return
end

local selected = getSelectedText()
if not selected or selected == "" then
hs.alert.show("No text selected", self.alertDuration)
return
end

if not hs.fs.attributes(self.claudePath) then
hs.alert.show("Claude CLI not found", self.alertDuration)
return
end

rewriting = true
local savedClipboard = hs.pasteboard.readAllData()
local progressAlert = hs.alert.show("Rewriting...", 30)

local args = {
"-p",
selected,
"--model",
self.model,
"--output-format",
"text",
"--system-prompt",
self.systemPrompt,
}

local task = hs.task.new(self.claudePath, function(exitCode, stdout, stderr)
hs.alert.closeSpecific(progressAlert)
rewriting = false

if exitCode ~= 0 then
hs.alert.show("Rewrite failed", self.alertDuration)
return
end

local result = stdout and stdout:gsub("^%s+", ""):gsub("%s+$", "") or ""
if result == "" then
hs.alert.show("Empty response", self.alertDuration)
return
end

hs.pasteboard.setContents(result)

if pasteInPlace then
hs.eventtap.keyStroke({ "cmd" }, "v")
hs.timer.doAfter(0.2, function()
hs.pasteboard.writeAllData(savedClipboard)
hs.alert.show("Rewritten", self.alertDuration)
end)
else
hs.alert.show("Copied to clipboard", self.alertDuration)
end
end, args)

task:start()
end

return obj
7 changes: 7 additions & 0 deletions hammerspoon/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,11 @@ hs.hotkey.bind({ "alt" }, "R", function()
hs.reload()
end)

hs.loadSpoon("ClaudeRewriter")
spoon.ClaudeRewriter.model = "sonnet"
spoon.ClaudeRewriter:bindHotkeys({
rewrite = { { "alt" }, "c" },
clipboard = { { "alt", "shift" }, "c" },
}):start()

hs.alert.show("🔮 Config loaded")
Loading
Loading