Claude Code Plugins

Community-maintained marketplace

Feedback

hammerspoon

@whilp/dotfiles
8
1

Configure and manage Hammerspoon automation, window management, key remapping, and app launching. Use when working with ~/.config/hammerspoon config files, adding keybindings, implementing window layouts, or troubleshooting Hammerspoon functionality.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name hammerspoon
description Configure and manage Hammerspoon automation, app launching, and window switching. Use when working with ~/.config/hammerspoon config files, adding keybindings, modifying the leader modal, or troubleshooting Hammerspoon functionality. Window management is handled by AeroSpace.

Hammerspoon configuration skill

Overview

Manage Hammerspoon configuration for macOS automation including app launching, window switching, emoji/symbol pickers, and leader modal. Window management is handled by AeroSpace.

Configuration locations

  • Config directory: ~/.config/hammerspoon/
  • Symlink: ~/.hammerspoon~/.config/hammerspoon/
  • CLI tool: hs (installed via hs.ipc.cliInstall() in init.lua)

Current modules

Core infrastructure

hyper-key.lua - Inline hyper key implementation

  • No spoon dependencies
  • Provides HyperKey.new(mods) constructor
  • Binding methods: bind(key):toFunction(name, fn) and bind(key):toApplication(app)
  • Tracks bindings in self.bindings table

init.lua - Main entry point

  • Calls hs.ipc.cliInstall() to enable CLI access
  • Defines hyper modifier: {cmd, ctrl, alt, shift}
  • Loads window-switcher and sets up cmd+space and cmd+tab bindings
  • Loads notch-clock with 4-minute offset
  • Loads leader modal with emoji and symbol pickers

Leader modal system

leader-modal.lua - Modal keybinding system

  • Timeout-based modal (default 3000ms)
  • Shows on-screen hints for available bindings
  • Supports nested leader keys
  • Sticky mode for repeated actions

leader-dsl.lua - DSL for defining leader bindings

  • Leader(key, description, bindings) - Define leader menu
  • Bind(key, description, action, options) - Define action
  • Actions can be functions, shell commands, or mode transitions
  • Automatically registers clues from ~/.config/hammerspoon/clues/ directory

clues/*.lua - Leader binding definitions

  • windows.lua - Window management (moved to AeroSpace)
  • hammerspoon.lua - Reload config, console, update apps, switcher
  • apps.lua - App launching
  • system.lua - System operations
  • cleanshot.lua - CleanShot integration
  • emoji.lua - Emoji picker
  • superwhisper.lua - SuperWhisper integration

Pickers

emoji-picker.lua - Emoji chooser

  • Fuzzy-searchable emoji picker
  • Categories and descriptions
  • Inserts selected emoji via keystroke

symbol-picker.lua - Symbol chooser

  • Special characters and symbols
  • Categories: arrows, math, punctuation, currency, etc.
  • Inserts selected symbol via keystroke

chooser-style.lua - Shared chooser styling

  • Dark theme
  • Consistent width and row count
  • Applied to all choosers

Window switcher

window-switcher.lua - Unified launcher/dispatcher modal

  • Uses hs.chooser API with fuzzy matching
  • Shows windows, apps, and commands
  • Keybindings: cmd+space and cmd+tab
  • Dark theme with 15 visible rows

fuzzy.lua - Dynamic programming fuzzy matching

  • Subsequence matching with scoring bonuses
  • Type priority: window (3), app (2), command (1)

notch-clock.lua - Clock in notch area

  • Shows time in MacBook notch
  • 4-minute offset configuration

Common operations

Using the hs CLI

The hs command-line tool provides direct interaction with Hammerspoon. It requires hs.ipc.cliInstall() to be called in init.lua (already configured).

Check for errors and module status:

echo "
local status = {modules_loaded = {}, errors = {}}
local modules = {'hyper-key', 'config-watch', 'window-hotkeys', 'quick-switch', 'window-switcher'}
for _, mod in ipairs(modules) do
  local ok, result = pcall(require, mod)
  if ok then
    table.insert(status.modules_loaded, mod)
  else
    table.insert(status.errors, mod .. ': ' .. tostring(result))
  end
end
return hs.json.encode(status, true)
" | hs -c ''

View live console output:

hs -C

Execute Hammerspoon commands:

echo "hs.alert.show('test')" | hs -c ''
echo 'hs.reload()' | hs -c ''

Interactive Lua REPL:

hs

Mirror prints to console:

hs -P

Run a script:

hs /path/to/script.lua

Common flags:

  • -A - Auto-launch Hammerspoon if not running
  • -C - Clone console prints to this terminal (best for checking errors)
  • -P - Mirror prints to Hammerspoon console
  • -c - Execute command and return result
  • -i - Force interactive mode
  • -n - Disable colorized output
  • -N - Force colorized output
  • -q - Quiet mode (errors and results only)

Other operations

Reload Hammerspoon:

echo 'hs.reload()' | hs -c ''
# or via URL:
open -g hammerspoon://reload

Check if running:

ps aux | rg -i hammerspoon

Open console window:

open "hammerspoon://consoleWindow"

Test configuration: Edit any .lua file in ~/.config/hammerspoon/ to trigger auto-reload

Development patterns

Adding new leader bindings

Create a file in ~/.config/hammerspoon/clues/:

return Leader("key", "Description", {
  Bind("a", "Action 1", { fn = function() ... end }),
  Bind("b", "Action 2", { shell = "/path/to/script" }),
  Bind("c", "Action 3", { mode = "mode_name" }),
})

Creating new modules

  1. Create ~/.config/hammerspoon/module-name.lua
  2. Return module table or functionality
  3. Require in init.lua: local module = require("module-name")

App replacement status

Current Hammerspoon setup replaces:

  • AltTab: Window switching via window-switcher.lua (cmd+space, cmd+tab)

Other macOS automation tools:

  • Karabiner-Elements: Key remapping (caps lock to ctrl, etc.)
  • AeroSpace: Window management (tiling, workspaces, monitor assignment)
  • CleanShot: Screenshots (integrated via leader modal)

Troubleshooting

Check for errors

Use the hs CLI to check for module loading errors:

echo "
local status = {modules_loaded = {}, errors = {}}
local modules = {'hyper-key', 'config-watch', 'window-hotkeys', 'quick-switch', 'window-switcher'}
for _, mod in ipairs(modules) do
  local ok, result = pcall(require, mod)
  if ok then
    table.insert(status.modules_loaded, mod)
  else
    table.insert(status.errors, mod .. ': ' .. tostring(result))
  end
end
return hs.json.encode(status, true)
" | hs -c ''

Or view live console output: hs -C

Config not loading

  • Check symlink: ls -la ~/.hammerspoon
  • Verify Hammerspoon is running: ps aux | rg Hammerspoon
  • Reload manually: echo 'hs.reload()' | hs -c ''
  • Check for errors: hs -C or open "hammerspoon://consoleWindow"

Keybindings not working

  • Check for conflicts with app shortcuts
  • Verify modifier keys are correct
  • Test with: echo "hs.alert.show('test')" | hs -c ''

Auto-reload not triggering

  • Verify config-watch.lua is loaded in init.lua
  • Check file extension is .lua
  • Restart Hammerspoon app

CLI not working

If hs command not found, ensure hs.ipc.cliInstall() is called in init.lua

Philosophy

Zero spoons approach

  • Inline functionality instead of external dependencies
  • Only use spoons if absolutely necessary
  • Keep implementation simple and maintainable

Decision criteria for using spoons:

  • Must provide significant value that's hard to inline
  • Must be actively maintained
  • Must be worth the workflow complexity

Future spoon management (not yet implemented): If spoons are needed, they will be managed via GitHub workflow:

  1. Add spoon commit hash to .github/workflows/versions.lua
  2. Create .github/workflows/hammerspoon.yml build workflow
  3. Workflow clones spoons at specified revisions, bundles into tarball
  4. Release as hammerspoon-YYYY.MM.DD-darwin-arm64.tar.gz
  5. Only add spoons to config after workflow builds successfully

This ensures pinned, reproducible spoon dependencies similar to other tools in the repo.

File organization

  • One module per file
  • Clear, descriptive names
  • Require modules in init.lua
  • Use XDG-style config directory

Resources