Python-scriptable keyboard customization for Windows and macOS.
Keyhac installs a system-wide keyboard hook and lets you script what your keys do in
Python. You write one file, ~/.keyhac/config.py, and it runs unchanged on both OSes —
remapping keys, binding keys to Python functions, and driving windows, applications and
the clipboard from the keyboard.
It can also write the harder scripts for you. Connect an AI assistant and it reads the screen in front of you, generates a plain-Python action against the real UI, and tests it — then the action runs on its own, with no AI in the loop.
Keyhac 2 replaces the two previous-generation products, Keyhac for Windows (1.x) and Keyhac for macOS (1.x), rebuilt as one shared codebase. Those older versions are no longer developed — if you are coming from one of them, see the migration guides in Documentation below. Keyhac 2’s UI is built on PuiKit.
Keyhac can be driven by an AI assistant while you write your automation, and by nothing but Python once you run it.
Connect Claude Desktop — or any MCP client — to Keyhac’s local endpoint, and it can read
the accessibility tree of the application in front of you, write an Action against the
real element names it finds there, run it, read its own traceback, and fix it. No
copy-pasting between a chat window and an editor. What you are left with is an ordinary
Python class on a key:
import translate_clipboard
def configure(keymap):
kt = keymap.define_keytable(focus_path_pattern="*")
kt["Fn-T"] = translate_clipboard.TranslateClipboard()
From then on it runs in milliseconds, offline, with no tokens and no model — the AI was the author, not the runtime. That matters most for applications with no API at all: walking pagination to the end, filling a form from each row of a CSV, reading tables out of an internal web app, capturing every settings screen to JSON.
It is off unless you turn it on — a checkbox in the console window. The endpoint listens on loopback only, behind a token generated at startup, and closes itself an hour after you open it.
AI integration covers setup, which clients have been tried, what it can reach, and the security model.
| Console (macOS) | Console (Windows) |
|---|---|
![]() |
![]() |
| Menu-bar extra (macOS) | Task tray (Windows) |
|---|---|
Keyhac-<version>-macos.dmg from
Releases: drag Keyhac.app into
Applications and launch it. Grant the Accessibility permission when prompted
(required for the keyboard hook).winget install --id 9P8H1PG6PRHH --source msstore) — automatic updates, no
SmartScreen warning. Alternatively, download Keyhac-<version>-win64.zip from
Releases, unzip anywhere and run
Keyhac.exe.Details, data locations and privacy notes: doc/installation.md.
On first run Keyhac creates ~/.keyhac/config.py from a fully commented template.
Open it from the tray / menu-bar icon (“Edit Config”), edit, then “Reload Config”.
A config defines one function:
from keyhac import *
def configure(keymap):
kt = keymap.define_keytable(focus_path_pattern="*") # active everywhere
kt["Fn-J"] = "Left" # key -> key
kt["Fn-A"] = "Home", "Shift-End" # key -> sequence
def hello(): # key -> Python function
print("Hello from config.py")
kt["Fn-H"] = hello
kt["Fn-V"] = ShowClipboardHistory() # clipboard history popup
kt_browser = keymap.define_keytable(app="chrome|Safari") # per-app table
kt_browser["Fn-R"] = "Cmd-R"
The full guide is doc/configuration.md, the per-symbol reference doc/config-api.md; the shipped template (keyhac/_config.py) is a working tour of every feature.
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest # engine + unit tests, no permissions needed
.venv/bin/python -m keyhac # run (macOS: needs Accessibility permission)
python -m keyhac -d enables debug logging, --no-ui runs headless (hook + engine
only), --config PATH uses an alternate config file (its data files live beside it).
MIT — see LICENSE.