home / skills / johnlindquist / claude / karabiner
npx playbooks add skill johnlindquist/claude --skill karabinerReview the files below or copy the command above to add this skill to your agents.
---
name: karabiner
description: Configure Karabiner-Elements keyboard remapping using Goku EDN syntax. Use when creating keybindings, layers, simlayers, app-specific shortcuts, or modifying karabiner.edn.
---
# Karabiner (via Goku)
Configure macOS keyboard remapping with [GokuRakuJoudo](https://github.com/yqrashawn/GokuRakuJoudo) - an EDN-based DSL that compiles to Karabiner-Elements JSON.
## Why Goku?
Karabiner's native JSON is verbose (20,000+ lines). Goku's EDN format is 10-50x more concise:
```clojure
;; Goku: 1 line
[:caps_lock :escape]
;; Karabiner JSON: ~30 lines
```
## Prerequisites
```bash
# Install Goku
brew install yqrashawn/goku/goku
# Start as service (watches ~/.config/karabiner.edn)
brew services start goku
# Or run once manually
goku
```
Config location: `~/.config/karabiner.edn`
Logs: `~/Library/Logs/goku.log`
## EDN Syntax Quick Reference
### Basic Structure
```clojure
{:main [{:des "Rule description"
:rules [[:from :to]
[:from2 :to2]]}]}
```
### Keycodes
All keys use keyword syntax: `:a`, `:1`, `:f19`, `:spacebar`, `:return_or_enter`
Find keycodes:
- Run Karabiner-EventViewer.app
- See [keys_info.clj](https://github.com/yqrashawn/GokuRakuJoudo/blob/master/src/karabiner_configurator/keys_info.clj)
### Modifier Syntax
| Symbol | Modifier | Example |
|--------|----------|---------|
| `!C` | Left Command | `:!Ca` = Cmd+A |
| `!T` | Left Control | `:!Ta` = Ctrl+A |
| `!O` | Left Option | `:!Oa` = Opt+A |
| `!S` | Left Shift | `:!Sa` = Shift+A |
| `!Q` | Right Command | `:!Qa` |
| `!W` | Right Control | `:!Wa` |
| `!E` | Right Option | `:!Ea` |
| `!R` | Right Shift | `:!Ra` |
| `!F` | Fn | `:!Fa` |
| `!P` | Caps Lock | `:!Pa` |
| `!!` | Hyper (Cmd+Ctrl+Opt+Shift) | `:!!a` |
| `##` | Optional any modifier | `:##a` |
Combine modifiers: `:!CTSa` = Cmd+Ctrl+Shift+A
### Rule Format
```clojure
[:from :to] ;; Basic
[:from :to :condition] ;; With condition
[:from :to :condition {:alone :x}] ;; With options
```
### Multiple Keys
```clojure
;; Press sequence
[:a [:1 :2 :3]] ;; a -> types 1, 2, 3
;; Simultaneous press (from)
[[:j :k] :escape] ;; j+k together -> escape
```
### Shell Commands
```clojure
[:!!1 "open -a Safari"] ;; Hyper+1 runs shell command
```
---
## Conditions
### Application Conditions
```clojure
{:applications {:chrome ["^com\\.google\\.Chrome$"]
:code ["com.microsoft.VSCode"]}
:main [{:des "Chrome only"
:rules [[:a :b :chrome]]}]}
```
### Device Conditions
```clojure
{:devices {:hhkb [{:vendor_id 1278 :product_id 51966}]}
:main [{:rules [[:a :b :hhkb]]}]}
```
### Input Source Conditions
```clojure
{:input-sources {:us {:input_source_id "com.apple.keylayout.US"}}
:main [{:rules [[:a :b :us]]}]}
```
### Combining & Negating Conditions
```clojure
[:a :b [:chrome :hhkb]] ;; Both conditions
[:a :b [:!chrome]] ;; NOT in Chrome
```
---
## Layers
### Simlayers (Recommended)
Fast, simultaneous-key based layers - best for typing speed:
```clojure
{:simlayers {:w-mode {:key :w}} ;; Hold W activates layer
:main [{:des "w-mode shortcuts"
:rules [:w-mode ;; Apply to this layer
[:e "open -a Finder"] ;; W+E opens Finder
[:r "open -a Safari"] ;; W+R opens Safari
]}]}
```
**Simlayer options:**
```clojure
{:simlayers {:w-mode {:key :w
:modi {:mandatory [:left_control]}}}} ;; Ctrl+W activates
```
### Standard Layers (Tap/Hold)
Different behavior on tap vs hold:
```clojure
{:layers {:caps-mode {:key :caps_lock
:alone {:key :escape}}}} ;; Tap=Esc, Hold=layer
```
### Manual Layer Variables
```clojure
;; Set variable on keydown, clear on keyup
[:w ["w-mode" 1] nil {:afterup ["w-mode" 0] :alone :w}]
;; Use the variable as condition
[:e :!Ce ["w-mode" 1]] ;; W+E -> Cmd+E (only when w-mode=1)
```
---
## Templates
Reusable shell command patterns:
```clojure
{:templates {:open "open -a '%s'"
:launch "/path/to/script.sh %s"
:alfred "osascript -e 'tell application \"Alfred\" to run trigger \"%s\"'"}
:main [{:rules [[:!!1 [:open "Safari"]] ;; %s replaced with Safari
[:!!2 [:launch "arg1"]]]}]}
```
---
## Predefined Aliases
### :froms (Input Keys)
```clojure
{:froms {:delete {:key :delete_or_backspace}
:return {:key :return_or_enter}
:mouse1 {:pkey :button1}}}
```
### :tos (Output Actions)
```clojure
{:tos {:spotlight {:key :spacebar :modi :command}
:paste {:key :v :modi :command}
:shift-click {:pkey :button1 :modi :left_shift}}}
```
### Custom Modifier Sets
```clojure
{:modifiers {:hyper [:command :shift :control :option]
:meh [:shift :control :option]}}
```
---
## Advanced Options
Fourth position in rules:
```clojure
[:from :to :condition {
:alone :key ;; to_if_alone
:held :key ;; to_if_held_down
:afterup :key ;; to_after_key_up
:delayed {:invoked :x :canceled :y}
:params {:alone_timeout 200}
}]
```
---
## Profile Settings
```clojure
{:profiles {:Default {:default true
:sim 250 ;; Simultaneous threshold (ms)
:delay 500 ;; Delayed action time (ms)
:alone 1000 ;; to_if_alone timeout (ms)
:held 500}}} ;; Held threshold (ms)
```
---
## Common Workflows
### Add a Simple Remap
```clojure
{:main [{:des "Caps to Escape"
:rules [[:caps_lock :escape]]}]}
```
### Add App-Specific Shortcut
```clojure
{:applications {:chrome ["com.google.Chrome"]}
:main [{:des "Chrome shortcuts"
:rules [[:!Cl :!Ct :chrome]]}]} ;; Cmd+L -> Cmd+T in Chrome
```
### Create a Layer
```clojure
{:simlayers {:nav {:key :spacebar}}
:main [{:des "Navigation layer"
:rules [:nav
[:h :left_arrow]
[:j :down_arrow]
[:k :up_arrow]
[:l :right_arrow]]}]}
```
### Hyper Key Setup
```clojure
{:main [{:des "Caps Lock to Hyper"
:rules [[:caps_lock :!CTOSleft_shift nil {:alone :escape}]]}]}
```
---
## CLI Commands
```bash
goku # Compile karabiner.edn once
gokuw # Watch mode (compile on change)
goku -h # Help
# Service management
brew services start goku # Start watch service
brew services stop goku # Stop service
brew services restart goku # Restart after changes
# Check logs
tail -f ~/Library/Logs/goku.log
```
---
## Validation & Debugging
1. **Check syntax**: Goku reports EDN parse errors
2. **View output**: `~/.config/karabiner/karabiner.json`
3. **Use EventViewer**: Karabiner-EventViewer.app shows key events
4. **Check logs**: `tail ~/Library/Logs/goku.log`
---
## Best Practices
1. **Use simlayers** for frequently-accessed shortcuts (faster than hold-layers)
2. **Group rules by purpose** with descriptive `:des` tags
3. **Use templates** for repeated shell command patterns
4. **Define aliases** in `:froms`/`:tos` for complex key definitions
5. **Test incrementally** - add one rule, verify, repeat
6. **Back up your config** before major changes
---
## Resources
- [Tutorial](https://github.com/yqrashawn/GokuRakuJoudo/blob/master/tutorial.md)
- [Examples](https://github.com/yqrashawn/GokuRakuJoudo/blob/master/examples.org)
- [Configs in the wild](https://github.com/yqrashawn/GokuRakuJoudo/blob/master/in-the-wild.md)
- [Keycodes reference](https://github.com/yqrashawn/GokuRakuJoudo/blob/master/src/karabiner_configurator/keys_info.clj)