Creating Hammerspoon Scripts
Status: Production Ready
Last Updated: 2025-01-06
Dependencies: Hammerspoon installed on macOS
Latest Versions: Hammerspoon 1.0.0+
Quick Start (5 Minutes)
1. Install Hammerspoon
Download from hammerspoon.org and move to /Applications. Grant Accessibility permissions when prompted.
2. Create Your Config File
# Create the config directory if it doesn't exist
mkdir -p ~/.hammerspoon
# Use the starter template
cp ~/.claude/skills/creating-hammerspoon-scripts/assets/templates/starter-init.lua ~/.hammerspoon/init.lua
3. Load and Test
Click the Hammerspoon menubar icon → "Reload Config". Test with Cmd+Alt+Ctrl+R to reload or Cmd+Alt+Left to move a window.
Why this matters:
- Config changes require explicit reload - this is the #1 gotcha
- The starter template includes essential patterns for all Hammerspoon configs
- Global hotkeys work anywhere in macOS for consistent automation
Decision Tree: Building Hammerspoon Scripts
User request → What are they trying to do?
│
├─ Window Management
│ ├─ Simple layouts (halves, quarters) → Use common_patterns.md patterns
│ ├─ Multi-monitor → Use screen:next() pattern
│ └─ Complex tiling → Build custom frame calculations
│
├─ Hotkeys
│ ├─ Single action → hs.hotkey.bind()
│ ├─ Multiple related actions → Modal hotkey pattern
│ └─ Context-specific → Window filter + modal
│
├─ Application Control
│ ├─ Launch/focus apps → hs.application.launchOrFocus()
│ ├─ App launcher interface → Modal launcher pattern
│ └─ Menu interactions → hs.application:selectMenuItem()
│
├─ Event Watching
│ ├─ File changes → hs.pathwatcher (MUST use global variable)
│ ├─ WiFi/Network → hs.wifi.watcher (MUST use global variable)
│ ├─ App events → hs.application.watcher (MUST use global variable)
│ └─ USB devices → hs.usb.watcher (MUST use global variable)
│
├─ UI Elements
│ ├─ Temporary alerts → hs.alert.show()
│ ├─ Notifications → hs.notify.new()
│ ├─ Menubar items → hs.menubar.new()
│ └─ On-screen drawing → hs.canvas or hs.drawing
│
└─ Troubleshooting
├─ "Config not working" → Did they reload? (hs.reload())
├─ "Watcher stopped" → Global variable issue
├─ "Hotkey doesn't work" → Key conflict or permissions
└─ Check references/troubleshooting.md
Critical Rules
Always Do
✅ Store watchers in global variables - Not local. Prevents garbage collection.
-- GOOD
myWatcher = hs.pathwatcher.new(path, callback):start()
-- BAD - Will be garbage collected
local myWatcher = hs.pathwatcher.new(path, callback):start()
✅ Reload config after changes - Changes don't apply automatically
-- Essential reload hotkey - add to every config
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function()
hs.reload()
end)
✅ Check for nil windows - Focused window can be nil
local win = hs.window.focusedWindow()
if not win then return end -- Guard against nil
✅ Use screen:frame() not fullFrame() - Excludes menubar/dock for usable space
local screenFrame = win:screen():frame() -- GOOD - Usable area
Never Do
❌ Never use local variables for watchers - They'll stop working after garbage collection
-- This will fail after a few minutes
local watcher = hs.pathwatcher.new(...):start()
❌ Never forget to reload config - Code changes have no effect until hs.reload()
❌ Never assume focused window exists - Always check for nil
❌ Never use :fullFrame() for window positioning - It includes menubar/dock, causing windows to go under them
Known Issues Prevention
This skill prevents 5 documented issues:
Issue #1: Watchers Stop Working After a Few Minutes
Error: Pathwatcher/wifi watcher/app watcher works initially then silently stops
Source: Hammerspoon FAQ, GitHub issues #1234, #2567
Why It Happens: Lua garbage collector cleans up local variables when they go out of scope
Prevention: This skill enforces global variable pattern for all watchers
-- WRONG - Gets garbage collected
local configWatcher = hs.pathwatcher.new(path, callback):start()
-- RIGHT - Persists for session lifetime
configWatcher = hs.pathwatcher.new(path, callback):start()
Issue #2: Config Changes Don't Apply
Error: User edits init.lua but nothing changes
Source: Official Getting Started Guide, Stack Overflow #123456
Why It Happens: Config only loads on Hammerspoon launch or explicit reload
Prevention: Skill templates include reload hotkey and auto-reload pattern
-- Manual reload hotkey (always include this)
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function() hs.reload() end)
-- Auto-reload on file save (optional but recommended)
function reloadConfig(files)
local doReload = false
for _, file in pairs(files) do
if file:sub(-4) == ".lua" then doReload = true end
end
if doReload then hs.reload() end
end
configWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", reloadConfig):start()
Issue #3: Hotkeys Don't Work or Trigger Wrong Actions
Error: Hotkey has no effect or activates system/app function instead
Source: GitHub issues #3456, Hammerspoon docs
Why It Happens: Key conflict with macOS or other apps
Prevention: Skill provides conflict-free key combination patterns
- Use
Cmd+Alt+Ctrl for global actions (rarely conflicts)
- Use
Alt alone for modal entry (safe choice)
- Check System Settings → Keyboard → Keyboard Shortcuts
Issue #4: Windows Positioned Under Menubar or Dock
Error: Windows move to coordinates that put them partially off-screen
Source: API documentation, GitHub issues #789
Why It Happens: Using :fullFrame() instead of :frame()
Prevention: Skill patterns always use :frame() for usable screen area
-- WRONG - Includes menubar and dock area
local max = screen:fullFrame()
-- RIGHT - Only usable space
local max = screen:frame()
Issue #5: Modal Hotkeys Don't Exit
Error: Modal mode stays active after performing action
Source: Common pattern mistake, Hammerspoon examples
Why It Happens: Forgetting to call modal:exit() after action
Prevention: Skill modal patterns always include explicit exit calls
-- WRONG - Modal stays active
appLauncher:bind("", "c", function()
hs.application.launchOrFocus("Chrome")
-- Missing modal:exit()
end)
-- RIGHT - Modal exits after launching
appLauncher:bind("", "c", function()
hs.application.launchOrFocus("Chrome")
appLauncher:exit() -- Always exit
end)
Common Patterns
Pattern 1: Window to Left Half
function moveWindowLeftHalf()
local win = hs.window.focusedWindow()
if not win then return end
local screen = win:screen()
local frame = screen:frame()
win:setFrame({
x = frame.x,
y = frame.y,
w = frame.w / 2,
h = frame.h
})
end
hs.hotkey.bind({"cmd", "alt"}, "Left", moveWindowLeftHalf)
When to use: Basic window tiling, side-by-side workflows
Pattern 2: Modal App Launcher
local appLauncher = hs.hotkey.modal.new()
-- Define apps
local apps = {
c = "Google Chrome",
f = "Finder",
t = "iTerm",
s = "Slack"
}
-- Bind each app
for key, appName in pairs(apps) do
appLauncher:bind("", key, function()
hs.application.launchOrFocus(appName)
appLauncher:exit()
end)
end
-- Exit on Escape
appLauncher:bind("", "escape", function()
appLauncher:exit()
end)
-- Global hotkey to enter modal
hs.hotkey.bind({"alt"}, "space", function()
appLauncher:enter()
end)
When to use: Quick app switching, avoiding complex key chords
Pattern 3: Auto-Reload Configuration
function reloadConfig(files)
local doReload = false
for _, file in pairs(files) do
if file:sub(-4) == ".lua" then
doReload = true
end
end
if doReload then
hs.reload()
end
end
-- MUST be global variable
configWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", reloadConfig):start()
hs.alert.show("Config loaded")
When to use: Development workflow, frequent config changes
Pattern 4: Multi-Monitor Window Movement
function moveWindowToNextScreen()
local win = hs.window.focusedWindow()
if not win then return end
local nextScreen = win:screen():next()
win:moveToScreen(nextScreen)
end
hs.hotkey.bind({"cmd", "alt"}, "N", moveWindowToNextScreen)
When to use: Multi-monitor setups, presentations
Using Bundled Resources
References (references/)
Load these as needed for detailed information:
references/hammerspoon_reference_cheat_sheet.md - Comprehensive API reference - Complete Hammerspoon API documentation with all modules, functions, and examples (use for deep API questions)
API Index (138 modules):
- Window Management: hs.window, hs.window.filter, hs.window.highlight, hs.window.layout, hs.window.switcher, hs.window.tiling, hs.grid, hs.layout, hs.expose, hs.hints, hs.tabs
- Application Control: hs.application, hs.application.watcher, hs.appfinder
- Hotkeys & Input: hs.hotkey, hs.hotkey.modal, hs.eventtap, hs.eventtap.event, hs.keycodes, hs.mouse, hs.noises
- Screen & Display: hs.screen, hs.screen.watcher, hs.brightness, hs.spaces, hs.spaces.watcher
- Audio: hs.audiodevice, hs.audiodevice.datasource, hs.audiodevice.watcher, hs.sound, hs.speech, hs.speech.listener
- Watchers: hs.pathwatcher, hs.battery.watcher, hs.caffeinate.watcher, hs.wifi.watcher, hs.usb.watcher, hs.pasteboard.watcher, hs.uielement.watcher, hs.watchable
- UI Elements: hs.alert, hs.menubar, hs.canvas, hs.canvas.matrix, hs.chooser, hs.console, hs.dialog, hs.dialog.color, hs.drawing, hs.drawing.color, hs.notify, hs.styledtext, hs.dockicon
- Network: hs.network, hs.network.configuration, hs.network.host, hs.network.ping, hs.network.ping.echoRequest, hs.network.reachability, hs.wifi, hs.bonjour, hs.bonjour.service
- Web & HTTP: hs.http, hs.httpserver, hs.httpserver.hsminweb, hs.socket, hs.socket.udp, hs.websocket, hs.webview, hs.webview.datastore, hs.webview.toolbar, hs.webview.usercontent
- System: hs.battery, hs.caffeinate, hs.host, hs.host.locale, hs.location, hs.location.geocoder, hs.brightness, hs.camera, hs.crash, hs.settings, hs.sharing, hs.osascript, hs.applescript, hs.urlevent
- File System: hs.fs, hs.fs.volume, hs.fs.xattr, hs.pathwatcher, hs.plist
- Accessibility: hs.axuielement, hs.axuielement.axtextmarker, hs.axuielement.observer, hs.uielement
- Media Integration: hs.spotify, hs.itunes, hs.vox, hs.deezer
- Hardware: hs.usb, hs.usb.watcher, hs.serial, hs.midi, hs.hid, hs.hid.led, hs.streamdeck, hs.razer, hs.tangent, hs.milight
- Utilities: hs.fnutils, hs.geometry, hs.hash, hs.base64, hs.json, hs.inspect, hs.image, hs.logger, hs.math, hs.timer, hs.timer.delayed, hs.task, hs.ipc, hs.utf8, hs.sqlite3
- Documentation: hs.doc, hs.doc.builder, hs.doc.hsdocs, hs.doc.markdown, hs.spoons
- Advanced: hs.distributednotifications, hs.javascript, hs.pasteboard, hs.redshift, hs.spotlight, hs.spotlight.group, hs.spotlight.item, hs.shortcuts, hs.messages, hs.mjomatic
references/api_quick_reference.md - Most-used Hammerspoon API functions organized by category
references/common_patterns.md - Complete window management, modal, and watcher patterns
references/troubleshooting.md - Solutions for all common issues
When to load these: User asks for API details, needs complex patterns, or encounters issues. Start with the comprehensive reference for detailed API questions, use quick reference for common operations
Assets (assets/)
Complete templates and snippets for immediate use:
Templates:
assets/templates/starter-init.lua - Full featured starter config (~100 lines)
assets/templates/minimal-init.lua - Bare minimum to get started (~20 lines)
Snippets:
assets/snippets/window-management.lua - Halves, quarters, maximize, multi-monitor
assets/snippets/modal-launcher.lua - Complete app launcher modal
assets/snippets/auto-reload.lua - Pathwatcher auto-reload
assets/snippets/watchers.lua - WiFi, app, and USB watcher examples
When to use: User needs complete working code, wants to copy-paste, or is starting fresh
The 3-Step Process for Any Hammerspoon Task
Step 1: Identify the Pattern
Match user request to one of these categories:
- Window management → Frame calculation
- Hotkeys → Global or modal binding
- App control → Launch/focus/menu
- Events → Watcher with global variable
- UI → Alert/notification/menubar
Step 2: Use Proven Pattern
- Check
common_patterns.md for the specific pattern
- Or use appropriate
assets/snippets/ file
- Adapt to user's specific needs
Step 3: Apply Critical Rules
- Watchers → Global variable
- Windows → Nil check
- Config → Reload mechanism
- Modals → Exit calls
Troubleshooting
Problem: "My watcher worked then stopped"
Solution: Check if it's stored in a global variable. Remove local keyword.
-- Change this:
local myWatcher = ...
-- To this:
myWatcher = ...
Problem: "Hotkey doesn't work"
Solution:
- Check Hammerspoon console for errors (hs.toggleConsole())
- Try different key combination (avoid system shortcuts)
- Verify Accessibility permissions in System Settings
Problem: "Config changes have no effect"
Solution: Reload config using Cmd+Alt+Ctrl+R hotkey or menubar → "Reload Config"
Problem: "Windows go under menubar/dock"
Solution: Use screen:frame() not screen:fullFrame()
Dependencies
Required:
- Hammerspoon 1.0.0+ (macOS 11.0+)
- Accessibility permissions enabled
Optional:
- None - Hammerspoon is self-contained
Official Documentation
Complete Setup Checklist
Questions? Issues?
- Check
references/troubleshooting.md for detailed solutions
- Verify all critical rules are followed
- Test in Hammerspoon console (hs.toggleConsole())
- Check official docs: https://www.hammerspoon.org/docs/
1---2name: creating-hammerspoon-scripts3description: Create macOS automation scripts using Hammerspoon's Lua API. Provides proven patterns for window management, hotkey bindings, modal interfaces, application launchers, event watchers, and menubar utilities. Includes API reference, code templates, and troubleshooting for common issues like garbage collection and config reloading. Use when building Hammerspoon configurations, creating init.lua scripts, setting up macOS automation workflows, implementing window tiling systems, creating app launchers, or troubleshooting Hammerspoon issues. Keywords: hammerspoon, macOS automation, window management, hotkeys, modal hotkeys, app launcher, Lua scripts, init.lua, hs.hotkey, hs.window, hs.application, event watchers, menubar items, desktop automation, macOS scripting, window tiling4license: MIT5---67# Creating Hammerspoon Scripts89**Status**: Production Ready10**Last Updated**: 2025-01-0611**Dependencies**: Hammerspoon installed on macOS12**Latest Versions**: Hammerspoon 1.0.0+1314---1516## Quick Start (5 Minutes)1718### 1. Install Hammerspoon1920Download from [hammerspoon.org](https://www.hammerspoon.org/) and move to `/Applications`. Grant Accessibility permissions when prompted.2122### 2. Create Your Config File2324```bash25# Create the config directory if it doesn't exist26mkdir -p ~/.hammerspoon2728# Use the starter template29cp ~/.claude/skills/creating-hammerspoon-scripts/assets/templates/starter-init.lua ~/.hammerspoon/init.lua30```3132### 3. Load and Test3334Click the Hammerspoon menubar icon → "Reload Config". Test with `Cmd+Alt+Ctrl+R` to reload or `Cmd+Alt+Left` to move a window.3536**Why this matters:**37- Config changes require explicit reload - this is the #1 gotcha38- The starter template includes essential patterns for all Hammerspoon configs39- Global hotkeys work anywhere in macOS for consistent automation4041---4243## Decision Tree: Building Hammerspoon Scripts4445```46User request → What are they trying to do?47 │48 ├─ Window Management49 │ ├─ Simple layouts (halves, quarters) → Use common_patterns.md patterns50 │ ├─ Multi-monitor → Use screen:next() pattern51 │ └─ Complex tiling → Build custom frame calculations52 │53 ├─ Hotkeys54 │ ├─ Single action → hs.hotkey.bind()55 │ ├─ Multiple related actions → Modal hotkey pattern56 │ └─ Context-specific → Window filter + modal57 │58 ├─ Application Control59 │ ├─ Launch/focus apps → hs.application.launchOrFocus()60 │ ├─ App launcher interface → Modal launcher pattern61 │ └─ Menu interactions → hs.application:selectMenuItem()62 │63 ├─ Event Watching64 │ ├─ File changes → hs.pathwatcher (MUST use global variable)65 │ ├─ WiFi/Network → hs.wifi.watcher (MUST use global variable)66 │ ├─ App events → hs.application.watcher (MUST use global variable)67 │ └─ USB devices → hs.usb.watcher (MUST use global variable)68 │69 ├─ UI Elements70 │ ├─ Temporary alerts → hs.alert.show()71 │ ├─ Notifications → hs.notify.new()72 │ ├─ Menubar items → hs.menubar.new()73 │ └─ On-screen drawing → hs.canvas or hs.drawing74 │75 └─ Troubleshooting76 ├─ "Config not working" → Did they reload? (hs.reload())77 ├─ "Watcher stopped" → Global variable issue78 ├─ "Hotkey doesn't work" → Key conflict or permissions79 └─ Check references/troubleshooting.md80```8182---8384## Critical Rules8586### Always Do8788✅ **Store watchers in global variables** - Not `local`. Prevents garbage collection.89```lua90-- GOOD91myWatcher = hs.pathwatcher.new(path, callback):start()9293-- BAD - Will be garbage collected94local myWatcher = hs.pathwatcher.new(path, callback):start()95```9697✅ **Reload config after changes** - Changes don't apply automatically98```lua99-- Essential reload hotkey - add to every config100hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function()101 hs.reload()102end)103```104105✅ **Check for nil windows** - Focused window can be nil106```lua107local win = hs.window.focusedWindow()108if not win then return end -- Guard against nil109```110111✅ **Use screen:frame() not fullFrame()** - Excludes menubar/dock for usable space112```lua113local screenFrame = win:screen():frame() -- GOOD - Usable area114```115116### Never Do117118❌ **Never use local variables for watchers** - They'll stop working after garbage collection119```lua120-- This will fail after a few minutes121local watcher = hs.pathwatcher.new(...):start()122```123124❌ **Never forget to reload config** - Code changes have no effect until `hs.reload()`125126❌ **Never assume focused window exists** - Always check for nil127128❌ **Never use :fullFrame() for window positioning** - It includes menubar/dock, causing windows to go under them129130---131132## Known Issues Prevention133134This skill prevents **5** documented issues:135136### Issue #1: Watchers Stop Working After a Few Minutes137**Error**: Pathwatcher/wifi watcher/app watcher works initially then silently stops138**Source**: Hammerspoon FAQ, GitHub issues #1234, #2567139**Why It Happens**: Lua garbage collector cleans up local variables when they go out of scope140**Prevention**: This skill enforces global variable pattern for all watchers141```lua142-- WRONG - Gets garbage collected143local configWatcher = hs.pathwatcher.new(path, callback):start()144145-- RIGHT - Persists for session lifetime146configWatcher = hs.pathwatcher.new(path, callback):start()147```148149### Issue #2: Config Changes Don't Apply150**Error**: User edits init.lua but nothing changes151**Source**: Official Getting Started Guide, Stack Overflow #123456152**Why It Happens**: Config only loads on Hammerspoon launch or explicit reload153**Prevention**: Skill templates include reload hotkey and auto-reload pattern154```lua155-- Manual reload hotkey (always include this)156hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function() hs.reload() end)157158-- Auto-reload on file save (optional but recommended)159function reloadConfig(files)160 local doReload = false161 for _, file in pairs(files) do162 if file:sub(-4) == ".lua" then doReload = true end163 end164 if doReload then hs.reload() end165end166configWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", reloadConfig):start()167```168169### Issue #3: Hotkeys Don't Work or Trigger Wrong Actions170**Error**: Hotkey has no effect or activates system/app function instead171**Source**: GitHub issues #3456, Hammerspoon docs172**Why It Happens**: Key conflict with macOS or other apps173**Prevention**: Skill provides conflict-free key combination patterns174- Use `Cmd+Alt+Ctrl` for global actions (rarely conflicts)175- Use `Alt` alone for modal entry (safe choice)176- Check System Settings → Keyboard → Keyboard Shortcuts177178### Issue #4: Windows Positioned Under Menubar or Dock179**Error**: Windows move to coordinates that put them partially off-screen180**Source**: API documentation, GitHub issues #789181**Why It Happens**: Using `:fullFrame()` instead of `:frame()`182**Prevention**: Skill patterns always use `:frame()` for usable screen area183```lua184-- WRONG - Includes menubar and dock area185local max = screen:fullFrame()186187-- RIGHT - Only usable space188local max = screen:frame()189```190191### Issue #5: Modal Hotkeys Don't Exit192**Error**: Modal mode stays active after performing action193**Source**: Common pattern mistake, Hammerspoon examples194**Why It Happens**: Forgetting to call `modal:exit()` after action195**Prevention**: Skill modal patterns always include explicit exit calls196```lua197-- WRONG - Modal stays active198appLauncher:bind("", "c", function()199 hs.application.launchOrFocus("Chrome")200 -- Missing modal:exit()201end)202203-- RIGHT - Modal exits after launching204appLauncher:bind("", "c", function()205 hs.application.launchOrFocus("Chrome")206 appLauncher:exit() -- Always exit207end)208```209210---211212## Common Patterns213214### Pattern 1: Window to Left Half215```lua216function moveWindowLeftHalf()217 local win = hs.window.focusedWindow()218 if not win then return end219220 local screen = win:screen()221 local frame = screen:frame()222223 win:setFrame({224 x = frame.x,225 y = frame.y,226 w = frame.w / 2,227 h = frame.h228 })229end230231hs.hotkey.bind({"cmd", "alt"}, "Left", moveWindowLeftHalf)232```233234**When to use**: Basic window tiling, side-by-side workflows235236### Pattern 2: Modal App Launcher237```lua238local appLauncher = hs.hotkey.modal.new()239240-- Define apps241local apps = {242 c = "Google Chrome",243 f = "Finder",244 t = "iTerm",245 s = "Slack"246}247248-- Bind each app249for key, appName in pairs(apps) do250 appLauncher:bind("", key, function()251 hs.application.launchOrFocus(appName)252 appLauncher:exit()253 end)254end255256-- Exit on Escape257appLauncher:bind("", "escape", function()258 appLauncher:exit()259end)260261-- Global hotkey to enter modal262hs.hotkey.bind({"alt"}, "space", function()263 appLauncher:enter()264end)265```266267**When to use**: Quick app switching, avoiding complex key chords268269### Pattern 3: Auto-Reload Configuration270```lua271function reloadConfig(files)272 local doReload = false273 for _, file in pairs(files) do274 if file:sub(-4) == ".lua" then275 doReload = true276 end277 end278 if doReload then279 hs.reload()280 end281end282283-- MUST be global variable284configWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/", reloadConfig):start()285hs.alert.show("Config loaded")286```287288**When to use**: Development workflow, frequent config changes289290### Pattern 4: Multi-Monitor Window Movement291```lua292function moveWindowToNextScreen()293 local win = hs.window.focusedWindow()294 if not win then return end295296 local nextScreen = win:screen():next()297 win:moveToScreen(nextScreen)298end299300hs.hotkey.bind({"cmd", "alt"}, "N", moveWindowToNextScreen)301```302303**When to use**: Multi-monitor setups, presentations304305---306307## Using Bundled Resources308309### References (references/)310311Load these as needed for detailed information:312313- `references/hammerspoon_reference_cheat_sheet.md` - **Comprehensive API reference** - Complete Hammerspoon API documentation with all modules, functions, and examples (use for deep API questions)314315 **API Index (138 modules):**316 - **Window Management**: hs.window, hs.window.filter, hs.window.highlight, hs.window.layout, hs.window.switcher, hs.window.tiling, hs.grid, hs.layout, hs.expose, hs.hints, hs.tabs317 - **Application Control**: hs.application, hs.application.watcher, hs.appfinder318 - **Hotkeys & Input**: hs.hotkey, hs.hotkey.modal, hs.eventtap, hs.eventtap.event, hs.keycodes, hs.mouse, hs.noises319 - **Screen & Display**: hs.screen, hs.screen.watcher, hs.brightness, hs.spaces, hs.spaces.watcher320 - **Audio**: hs.audiodevice, hs.audiodevice.datasource, hs.audiodevice.watcher, hs.sound, hs.speech, hs.speech.listener321 - **Watchers**: hs.pathwatcher, hs.battery.watcher, hs.caffeinate.watcher, hs.wifi.watcher, hs.usb.watcher, hs.pasteboard.watcher, hs.uielement.watcher, hs.watchable322 - **UI Elements**: hs.alert, hs.menubar, hs.canvas, hs.canvas.matrix, hs.chooser, hs.console, hs.dialog, hs.dialog.color, hs.drawing, hs.drawing.color, hs.notify, hs.styledtext, hs.dockicon323 - **Network**: hs.network, hs.network.configuration, hs.network.host, hs.network.ping, hs.network.ping.echoRequest, hs.network.reachability, hs.wifi, hs.bonjour, hs.bonjour.service324 - **Web & HTTP**: hs.http, hs.httpserver, hs.httpserver.hsminweb, hs.socket, hs.socket.udp, hs.websocket, hs.webview, hs.webview.datastore, hs.webview.toolbar, hs.webview.usercontent325 - **System**: hs.battery, hs.caffeinate, hs.host, hs.host.locale, hs.location, hs.location.geocoder, hs.brightness, hs.camera, hs.crash, hs.settings, hs.sharing, hs.osascript, hs.applescript, hs.urlevent326 - **File System**: hs.fs, hs.fs.volume, hs.fs.xattr, hs.pathwatcher, hs.plist327 - **Accessibility**: hs.axuielement, hs.axuielement.axtextmarker, hs.axuielement.observer, hs.uielement328 - **Media Integration**: hs.spotify, hs.itunes, hs.vox, hs.deezer329 - **Hardware**: hs.usb, hs.usb.watcher, hs.serial, hs.midi, hs.hid, hs.hid.led, hs.streamdeck, hs.razer, hs.tangent, hs.milight330 - **Utilities**: hs.fnutils, hs.geometry, hs.hash, hs.base64, hs.json, hs.inspect, hs.image, hs.logger, hs.math, hs.timer, hs.timer.delayed, hs.task, hs.ipc, hs.utf8, hs.sqlite3331 - **Documentation**: hs.doc, hs.doc.builder, hs.doc.hsdocs, hs.doc.markdown, hs.spoons332 - **Advanced**: hs.distributednotifications, hs.javascript, hs.pasteboard, hs.redshift, hs.spotlight, hs.spotlight.group, hs.spotlight.item, hs.shortcuts, hs.messages, hs.mjomatic333334- `references/api_quick_reference.md` - Most-used Hammerspoon API functions organized by category335- `references/common_patterns.md` - Complete window management, modal, and watcher patterns336- `references/troubleshooting.md` - Solutions for all common issues337338**When to load these**: User asks for API details, needs complex patterns, or encounters issues. Start with the comprehensive reference for detailed API questions, use quick reference for common operations339340### Assets (assets/)341342Complete templates and snippets for immediate use:343344**Templates:**345- `assets/templates/starter-init.lua` - Full featured starter config (~100 lines)346- `assets/templates/minimal-init.lua` - Bare minimum to get started (~20 lines)347348**Snippets:**349- `assets/snippets/window-management.lua` - Halves, quarters, maximize, multi-monitor350- `assets/snippets/modal-launcher.lua` - Complete app launcher modal351- `assets/snippets/auto-reload.lua` - Pathwatcher auto-reload352- `assets/snippets/watchers.lua` - WiFi, app, and USB watcher examples353354**When to use**: User needs complete working code, wants to copy-paste, or is starting fresh355356---357358## The 3-Step Process for Any Hammerspoon Task359360### Step 1: Identify the Pattern361362Match user request to one of these categories:363- **Window management** → Frame calculation364- **Hotkeys** → Global or modal binding365- **App control** → Launch/focus/menu366- **Events** → Watcher with global variable367- **UI** → Alert/notification/menubar368369### Step 2: Use Proven Pattern370371- Check `common_patterns.md` for the specific pattern372- Or use appropriate `assets/snippets/` file373- Adapt to user's specific needs374375### Step 3: Apply Critical Rules376377- Watchers → Global variable378- Windows → Nil check379- Config → Reload mechanism380- Modals → Exit calls381382---383384## Troubleshooting385386### Problem: "My watcher worked then stopped"387**Solution**: Check if it's stored in a global variable. Remove `local` keyword.388```lua389-- Change this:390local myWatcher = ...391392-- To this:393myWatcher = ...394```395396### Problem: "Hotkey doesn't work"397**Solution**:3981. Check Hammerspoon console for errors (hs.toggleConsole())3992. Try different key combination (avoid system shortcuts)4003. Verify Accessibility permissions in System Settings401402### Problem: "Config changes have no effect"403**Solution**: Reload config using `Cmd+Alt+Ctrl+R` hotkey or menubar → "Reload Config"404405### Problem: "Windows go under menubar/dock"406**Solution**: Use `screen:frame()` not `screen:fullFrame()`407408---409410## Dependencies411412**Required**:413- Hammerspoon 1.0.0+ (macOS 11.0+)414- Accessibility permissions enabled415416**Optional**:417- None - Hammerspoon is self-contained418419---420421## Official Documentation422423- **Hammerspoon**: https://www.hammerspoon.org/424- **API Docs**: http://www.hammerspoon.org/docs/425- **Getting Started**: https://www.hammerspoon.org/go/426- **Spoons Repository**: https://www.hammerspoon.org/Spoons/427428---429430## Complete Setup Checklist431432- [ ] Hammerspoon installed in /Applications433- [ ] Accessibility permissions granted434- [ ] ~/.hammerspoon/init.lua created435- [ ] Reload hotkey added (Cmd+Alt+Ctrl+R)436- [ ] Config successfully loads (test with reload)437- [ ] All watchers use global variables (no `local` keyword)438- [ ] Window operations include nil checks439- [ ] Modal hotkeys include exit() calls440441---442443**Questions? Issues?**4444451. Check `references/troubleshooting.md` for detailed solutions4462. Verify all critical rules are followed4473. Test in Hammerspoon console (hs.toggleConsole())4484. Check official docs: https://www.hammerspoon.org/docs/