| name | building-spoons |
| description | Creates Hammerspoon Spoon plugins following official conventions. Use when building, scaffolding, or packaging a Hammerspoon Spoon. |
Building Hammerspoon Spoons
Spoons are pure-Lua plugins for Hammerspoon with standardized APIs.
Spoon Structure
Minimal structure:
MySpoon.spoon/
└── init.lua
Full structure:
MySpoon.spoon/
├── init.lua # Main entry point (required)
├── docs.json # Generated documentation
└── images/ # Optional assets
└── icon.png
Install location: ~/.hammerspoon/Spoons/
Naming Conventions
| Element | Convention | Example |
|---|
| Spoon name | TitleCase | WindowManager, USBObserver |
| Methods/variables | camelCase | bindHotkeys, showAlert |
| Directory | Name.spoon/ | WindowManager.spoon/ |
Required Metadata
local obj = {}
obj.__index = obj
obj.name = "MySpoon"
obj.version = "1.0"
obj.author = "Your Name <email@example.com>"
obj.license = "MIT - https://opensource.org/licenses/MIT"
obj.homepage = "https://github.com/user/MySpoon"
return obj
Lifecycle Methods
| Method | Called | Purpose |
|---|
init() | Automatically by hs.loadSpoon() | Setup resources, no background work |
start() | Manually by user | Start timers, watchers, background tasks |
stop() | Manually by user | Stop all background activity |
function obj:init()
self.watcher = nil
return self
end
function obj:start()
self.watcher = hs.application.watcher.new(function(...)
self:handleEvent(...)
end):start()
return self
end
function obj:stop()
if self.watcher then
self.watcher:stop()
self.watcher = nil
end
return self
end
Hotkey Binding
Use hs.spoons.bindHotkeysToSpec() for standard hotkey binding:
function obj:bindHotkeys(mapping)
local spec = {
show = hs.fnutils.partial(self.show, self),
hide = hs.fnutils.partial(self.hide, self),
toggle = hs.fnutils.partial(self.toggle, self),
}
hs.spoons.bindHotkeysToSpec(spec, mapping)
return self
end
User calls with:
spoon.MySpoon:bindHotkeys({
show = {{"cmd", "alt"}, "s"},
hide = {{"cmd", "alt"}, "h", message = "Hidden"},
})
Loading Resources
obj.spoonPath = hs.spoons.scriptPath()
local imagePath = hs.spoons.resourcePath("images/icon.png")
obj.icon = hs.image.imageFromPath(imagePath)
dofile(hs.spoons.resourcePath("helpers.lua"))
Docstring Format
Document all public API using Hammerspoon's docstring format:
Docstring Types
| Type | Header Format |
|---|
| Method | --- Spoon:method() then --- Method |
| Variable | --- Spoon.variable then --- Variable |
| Constant | --- Spoon.CONSTANT then --- Constant |
Complete Template
local obj = {}
obj.__index = obj
obj.name = "MySpoon"
obj.version = "1.0"
obj.author = "Your Name <email@example.com>"
obj.license = "MIT - https://opensource.org/licenses/MIT"
obj.homepage = "https://github.com/user/MySpoon"
obj.logger = hs.logger.new("MySpoon")
function obj:init()
return self
end
function obj:start()
return self
end
function obj:stop()
return self
end
function obj:bindHotkeys(mapping)
local spec = {
}
hs.spoons.bindHotkeysToSpec(spec, mapping)
return self
end
return obj
Generating Documentation
cd /path/to/MySpoon.spoon
hs -c "hs.doc.builder.genJSON(\"$(pwd)\")" | grep -v "^--" > docs.json
Distribution
- Ensure Spoon directory ends with
.spoon
- Include
docs.json for documentation
- Zip the
.spoon directory for distribution
- Users double-click to install to
~/.hammerspoon/Spoons/
References