Automating screen capture and Claude Code.
Shave off a few seconds from your workflow, it all adds up!

Taking screenshots can be seamless in Claude Code with this simple automation.

The problem

I take a lot of screenshots to show Claude just how far off the rails it has run.

It is slow and annoying to drag and drop images into my terminals and clutter up my desktop with hundreds of screenshots.

There is a simple way to automate taking screenshots and pasting them directly into my terminal.


The solution

Watch it in action:


Press Cmd+Shift+6 hotkey to open the macOS screen selection tool.

Select a region; it pastes into the terminal. No file saved, no desktop clutter.

Saves time and keeps the workflow smooth.

Try it out!

macOS only (not Windows or Linux).

I wired this up using a tool called Hammerspoon.

It’s a handy macOS automation utility.

Hammerspoon uses Lua as a scripting language.

1. Install Hammerspoon

To install it:

brew install --cask hammerspoon

And then open it with:

open -a Hammerspoon

Accessibility permissions need to be granted by Hammerspoon, so when the dialog pops up, turn on the switch.

Or do this manually in System SettingsPrivacy & SecurityAccessibilityenable Hammerspoon


2. Add the Lua script

I am using Ghostty as my terminal emulator, you should swap that out with your own.

In ~/.hammerspoon/init.lua add the following:

-- https://quobix.com/articles/screenshots-with-claude-code/
-- ============================================================================
-- Screenshot to Claude Code
-- ============================================================================
-- Captures a screen selection and automatically pastes it into Claude Code
-- running in Ghostty terminal. Press Cmd+Shift+6 from any application.
--
-- State: prevents multiple screenshots from running simultaneously
local screenshotInProgress = false

-- State: tracks the most recently focused Ghostty window so we can paste
-- to the correct window even when triggering from another application
local lastGhosttyWindow = nil

-- Resets the in-progress flag. Centralized to ensure all code paths
-- properly release the lock, preventing the hotkey from getting stuck.
local function resetFlag()
    screenshotInProgress = false
end

-- Finds the Ghostty application by name or bundle ID.
-- Tries common name first, falls back to bundle ID for reliability.
local function findGhostty()
    return hs.application.find("Ghostty") or
           hs.application.find("com.mitchellh.ghostty")
end

-- Validates that a window reference is still valid and usable.
-- Windows can be closed between when we capture a reference and when
-- we try to use it, so we must validate before each operation.
local function isValidWindow(window)
    return window and window:application() and window:isStandard()
end

-- Window Tracking
local windowFilter = hs.window.filter.new("Ghostty")

-- Track when a Ghostty window gains focus
windowFilter:subscribe(hs.window.filter.windowFocused, function(window)
    if isValidWindow(window) then
        lastGhosttyWindow = window
    end
end)

-- Clean up stale references when windows are closed.
-- This prevents memory leaks from holding references to destroyed windows.
windowFilter:subscribe(hs.window.filter.windowDestroyed, function(window)
    if lastGhosttyWindow == window then
        lastGhosttyWindow = nil
    end
end)

-- Pastes the clipboard contents into the target Ghostty window.
-- Called after screenshot capture completes successfully.
local function pasteToGhostty()
    local targetWindow = lastGhosttyWindow

    -- If tracked window is no longer valid, find any valid Ghostty window
    if not isValidWindow(targetWindow) then
        local ghostty = findGhostty()
        if ghostty then
            local windows = ghostty:allWindows()
            for _, win in ipairs(windows) do
                if isValidWindow(win) then
                    targetWindow = win
                    break
                end
            end
        end
    end

    -- Bail out if no valid Ghostty window exists
    if not isValidWindow(targetWindow) then
        resetFlag()
        return
    end

    -- Focus the window; bail if focus fails
    local focusSuccess = targetWindow:focus()
    if not focusSuccess then
        resetFlag()
        return
    end

    -- Wait for window to fully activate, then send Ctrl+V to paste.
    -- The delay is necessary because macOS window focus is asynchronous.
    hs.timer.doAfter(0.2, function()
        -- Re-validate window hasn't been closed during the delay
        if isValidWindow(targetWindow) then
            hs.eventtap.keyStroke({"ctrl"}, "v")
        end
        resetFlag()
    end)
end

-- Callback invoked when screencapture command completes.
-- Exit code 0 = success, non-zero = user cancelled or error.
local function onScreenshotComplete(exitCode)
    if exitCode ~= 0 then
        resetFlag()
        return
    end

    -- Small delay to ensure clipboard is populated before pasting
    hs.timer.doAfter(0.1, pasteToGhostty)
end

-- ----------------------------------------------------------------------------
-- Hotkey Binding
-- ----------------------------------------------------------------------------
-- Cmd+Shift+6: Trigger screenshot selection, auto-paste to Ghostty
--
-- We use screencapture with these flags:
--   -c : Send capture to clipboard (not file)
--   -s : Interactive selection mode (like Cmd+Shift+4)
--   -x : Disable capture sound

hs.hotkey.bind({"cmd", "shift"}, "6", function()
    -- Prevent concurrent captures
    if screenshotInProgress then return end
    screenshotInProgress = true

    -- Launch macOS screencapture tool
    local task = hs.task.new("/usr/sbin/screencapture", onScreenshotComplete, {"-c", "-s", "-x"})
    if not task:start() then
        resetFlag()
    end
end)

3. Reload the configuration

Click the Hammerspoon menu bar icon and select Reload Config (it looks like a hammer and a spoon)


You can change the hotkey combo to whatever you want. The default is Command + Shift + 6