Hammerspoon Spoon for viewing Claude Code tasks in a floating window.
- Floating task viewer with WebView UI
- Vim-like keyboard navigation (j/k or ↑/↓ to move, Space to view, Enter to launch)
- Customizable key bindings - configure all keyboard shortcuts
- Task deletion with keyboard shortcut (⌘⌫)
- Task search/filtering with real-time results
- Auto-refresh on file changes via pathwatcher
- Session selector with datalist autocomplete
- Quick TaskUpdate via dialog (⌘E)
- Launch Claude session in terminal (▶ button)
- Task detail view with metadata display
- Keyboard shortcuts help popup (?)
git clone https://github.com/jongwony/ClaudePanel.spoon.git
ln -sf $(pwd)/ClaudePanel.spoon ~/.hammerspoon/Spoons/ClaudePanel.spoonDownload and extract to ~/.hammerspoon/Spoons/ClaudePanel.spoon/
Add to your ~/.hammerspoon/init.lua:
hs.loadSpoon("ClaudePanel")
spoon.ClaudePanel:bindHotkeys(spoon.ClaudePanel.defaultHotkeys)
spoon.ClaudePanel:start()| Hotkey | Action |
|---|---|
opt+. |
Toggle task viewer |
cmd+alt+T |
Show task status summary |
| Key | Action |
|---|---|
j / k / ↓ / ↑ |
Navigate tasks |
Space |
View task detail |
Enter |
Launch Claude session |
⌘⌫ |
Delete selected task |
/ |
Search mode |
= |
Session input mode |
Escape |
Return to navigation |
? |
Show shortcuts help |
⌘E |
Quick Task dialog |
⌘Enter |
Create task |
All keyboard shortcuts can be customized via bindShortcuts().
hs.loadSpoon("ClaudePanel")
spoon.ClaudePanel:configure({
width = 500,
height = 700,
margin = 30,
debugMode = true,
-- Specify paths explicitly if auto-discovery fails
claudePath = "/usr/local/bin/claude",
terminalApp = "/Applications/iTerm.app/Contents/MacOS/iTerm2",
shell = "/bin/bash",
})
spoon.ClaudePanel:bindHotkeys({
toggle = {{"cmd", "shift"}, "t"},
status = {{"cmd", "alt"}, "t"}
})
-- You can customize all in-app keyboard shortcuts
spoon.ClaudePanel:bindShortcuts({
navigateDown = {modifiers = {}, keys = {'j', 'ㅓ', 'ArrowDown'}},
navigateUp = {modifiers = {}, keys = {'k', 'ㅏ', 'ArrowUp'}},
deleteTask = {modifiers = {'cmd'}, keys = {'Backspace'}},
openTask = {modifiers = {}, keys = {' '}},
launchTask = {modifiers = {}, keys = {'Enter'}},
})
spoon.ClaudePanel:start()obj:init()- Initialize the Spoon (called automatically)obj:start()- Start file watching and load saved stateobj:stop()- Stop file watching and cleanupobj:show()- Show the task viewerobj:hide()- Hide the task viewerobj:toggle()- Toggle visibilityobj:refresh()- Manually refresh the task listobj:setTaskListId(id)- Set the session ID filterobj:createTask(subject)- Create a new task via Claude CLIobj:quickTaskUpdate(prompt)- Run quick TaskUpdate via haiku modelobj:launchClaudeWithTaskList()- Launch Claude in terminal with current sessionobj:launchClaudeWithCwd(sessionId, cwd)- Launch Claude with specific working directoryobj:launchClaudeWithSession(sessionId)- Launch Claude with session env varobj:showTaskDetailWindow(subject, description, metadata)- Show task detail popupobj:deleteTask(taskId, sessionId)- Delete a task by IDobj:status()- Get current status infoobj:configure(options)- Update configurationobj:bindHotkeys(mapping)- Bind hotkeys
| Option | Default | Description |
|---|---|---|
width |
420 | Window width |
height |
580 | Window height |
margin |
20 | Screen edge margin |
refreshDebounce |
0.2 | Debounce delay for file changes (seconds) |
debugMode |
false | Enable debug logging |
taskListId |
$CLAUDE_CODE_TASK_LIST_ID |
Session ID filter |
claudePath |
nil | Path to claude CLI (auto-discovered if nil) |
terminalApp |
nil | Path to terminal app (auto-discovered if nil) |
shell |
nil | Shell to use (defaults to $SHELL or /bin/zsh) |
keyBindings |
(see below) | Customizable keyboard shortcuts (use bindShortcuts()) |
- Hammerspoon
- Claude Code CLI (
claudecommand) - A supported terminal app (Ghostty, iTerm2, or Terminal.app)
MIT License - see LICENSE
