Claude-skill-registry hammerspoon
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.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/hammerspoon" ~/.claude/skills/majiayu000-claude-skill-registry-hammerspoon && rm -rf "$T"
skills/data/hammerspoon/SKILL.mdHammerspoon 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:
(installed viahs
in init.lua)hs.ipc.cliInstall()
Current modules
Core infrastructure
hyper-key.lua - Inline hyper key implementation
- No spoon dependencies
- Provides
constructorHyperKey.new(mods) - Binding methods:
andbind(key):toFunction(name, fn)bind(key):toApplication(app) - Tracks bindings in
tableself.bindings
init.lua - Main entry point
- Calls
to enable CLI accesshs.ipc.cliInstall() - Defines
modifier:hyper{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
- Define leader menuLeader(key, description, bindings)
- Define actionBind(key, description, action, options)- Actions can be functions, shell commands, or mode transitions
- Automatically registers clues from
directory~/.config/hammerspoon/clues/
clues/*.lua - Leader binding definitions
- Window management (moved to AeroSpace)windows.lua
- Reload config, console, update apps, switcherhammerspoon.lua
- App launchingapps.lua
- System operationssystem.lua
- CleanShot integrationcleanshot.lua
- Emoji pickeremoji.lua
- SuperWhisper integrationsuperwhisper.lua
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
API with fuzzy matchinghs.chooser - Shows windows, apps, and commands
- Keybindings:
andcmd+spacecmd+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:
- Auto-launch Hammerspoon if not running-A
- Clone console prints to this terminal (best for checking errors)-C
- Mirror prints to Hammerspoon console-P
- Execute command and return result-c
- Force interactive mode-i
- Disable colorized output-n
- Force colorized output-N
- Quiet mode (errors and results only)-q
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
- Create
~/.config/hammerspoon/module-name.lua - Return module table or functionality
- Require in
:init.lualocal 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:
orhs -Copen "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
is loaded inconfig-watch.luainit.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:
- Add spoon commit hash to
.github/workflows/versions.lua - Create
build workflow.github/workflows/hammerspoon.yml - Workflow clones spoons at specified revisions, bundles into tarball
- Release as
hammerspoon-YYYY.MM.DD-darwin-arm64.tar.gz - 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
- Hammerspoon API: https://www.hammerspoon.org/docs/
- Plan document:
~/.claude/plans/hammerspoon-consolidation.md - dbalatero dotfiles: https://github.com/dbalatero/dotfiles/tree/main/hammerspoon (reference only)