I Replaced 10 Years of Keyboard Maestro with One Rebind Script | Rebind
All posts<br>I've been a Keyboard Maestro user for ten years. Paid for every major upgrade. Built up a Global Macro Group with seventeen macros that my hands depend on: browser history on ⌘J/⌘L, tab switching on ⌘S/⌘D, hard refresh, dev tools, window sizing, a terminal toggle on backtick, forward delete, a text expansion. The kind of setup you stop thinking about because it's part of how you type.
Last week I replaced all of it with one Rebind script. I didn't even write the script — RebindGPT did.
The before state
If you've maintained a serious Keyboard Maestro setup, you know what it looks like. Every remap is a macro. Every macro is a stack of click-configured conditions: If any of the following are true: Google Chrome is at the front, Chromium is at the front... execute: Type the ⌘] Keystroke. Otherwise: No Action. Multiply that by every browser you use, then by every shortcut, then again for the apps that need a different keystroke for the same action.
My "browser history forward" macro alone was two condition blocks deep — one for Chrome-family browsers (⌘]) and one for Firefox-family (⌘→), each enumerating applications one dropdown at a time. Seventeen macros of that. It works, and Keyboard Maestro is genuinely good software — but the setup is opaque, it lives in a proprietary editor, you can't diff it, you can't grep it, and you rebuild it dropdown by dropdown on every new machine.
The old setup: one of seventeen macros — two condition blocks and four app dropdowns just to remap ⌘L per browser family.
The after state
One Luau file. My entire Global Macro Group, expressed as code I can read:
remap("J", "LeftBrace", function() return cmd() and CHROMISH[front()] end) -- Chrome → ⌘[<br>remap("J", "Left", function() return cmd() and FIREFOXISH[front()] end) -- Firefox → ⌘←<br>remap("L", "RightBrace", function() return cmd() and CHROMISH[front()] end) -- Chrome → ⌘]<br>remap("L", "Right", function() return cmd() and FIREFOXISH[front()] end) -- Firefox → ⌘→
That's the entire "browser history" pair — the thing that took four condition blocks across two macros in the old setup. App families are defined once as plain tables and reused everywhere:
BROWSERS = set { "Google Chrome", "Chromium", "Brave Browser", "Firefox", "LibreWolf", "Safari" }<br>ARROW_TABS = set { "Google Chrome", "Chromium", "Brave Browser", "Firefox", "LibreWolf", "Safari", "Code", "Visual Studio Code", "Cursor" }<br>BRACKET_TABS = set { "Ghostty", "Terminal", "Hyper", "iTerm2", "iTerm", "MacVim", term }
Add a browser to the table, every remap picks it up. Try doing that across seventeen macros of dropdowns.
I didn't write this script
Here's the part that actually surprised me. I described my Keyboard Maestro setup to RebindGPT — the shortcuts, which apps needed which variants, the terminal toggle behavior — and it wrote the whole thing. The app-family tables, the modifier helpers, even the subtle stuff I would have gotten wrong on the first pass, like waiting for physical modifiers to clear before injecting a chord that conflicts with them:
-- Emit `chord` after physical modifiers clear — used when the chord conflicts<br>-- with held ⌘/⇧ (macOS applies held modifiers to any injected event).<br>local function injectAfterMods(chord)<br>Run(function()<br>while cmd() or shift() do Sleep(5) end<br>HID.Combo(chord)<br>end)<br>end
That's a real macOS injection pitfall — held modifiers merge into synthetic events — and the generated script handled it without being asked.
It didn't stop at writing the script, either. When an early version polled System.Window() too aggressively in a timer, I pasted the runtime's warning into the same chat and RebindGPT diagnosed the tick-budget problem and rewrote the hot path — slower timer, expensive check moved behind the keypress that actually needs it.
The replacement: taky-km.luau running in the Rebind editor at 0µs/tick, with RebindGPT explaining its own performance fix.
The full script
Everything below is what runs on my machine today. The two configurable values (terminal app and the ⌘P text expansion) surface as UI controls in the Rebind panel, so the script itself stays generic:
--[[<br>rebind: name=taky-km<br>rebind: min_sdk=3.2.1<br>rebind: description=Keyboard Maestro parity — global, app-conditional chord remaps, app control, and configurable terminal/hash.<br>--]]
-- ═══════════════════════════════════════════════════════════════════════════════<br>-- Config<br>-- ═══════════════════════════════════════════════════════════════════════════════
local cfg = UI.Schema({<br>terminal = UI.Text("Terminal", {<br>label = "Terminal App",<br>tooltip = "Process name of your terminal (e.g. Terminal, Ghostty, iTerm2, Kitty).",<br>maxLength = 40,<br>}),<br>hash = UI.Text("your-snippet-here", {<br>label = "Hash String",<br>tooltip = "Text inserted by ⌘P (skipped in Cursor / MacVim).",<br>maxLength = 128,<br>}),<br>})
-- ═══════════════════════════════════════════════════════════════════════════════<br>--...