← Keyhac crftwr/keyhac on GitHub · craftware

Packaging, launchers, distribution

Languages: the complete answer

Layer Language Size Why
Application (engine, UI, platform bindings) Python 3.14 ~everything ctypes covers Win32 (PuiKit’s Windows backend proves it incl. COM/Direct2D); PyObjC covers Quartz/AX/AppKit
Embedding launcher, Windows (Keyhac.exe) C ~150 lines PEP 587 PyConfig isolated init, module search paths. Also: exe icon, DPI manifest.
Embedding launcher, macOS (Keyhac.app main) Objective-C ~150 lines Same PEP 587 pattern. A real bundle executable is required so the Accessibility permission attaches to a stable identity — running under a generic python3 would grant the permission to that interpreter, not to Keyhac.
Build scripts PowerShell / shell / Python   windows_app/build.ps1, macos_app/build.sh, tools/

No ckit, no pyauto, no compiled extension of our own. Runtime dependencies: puikit, pyobjc-framework-{Cocoa,Quartz,ApplicationServices} (macOS only), pillow (via puikit). The pipeline is a port of XeFM’s Makefile/bundle system — the author’s shipped PuiKit app, the standard to follow for build infra.

Windows: windows_app/

Microsoft Store (MSIX)

Ported from XeFM’s pipeline (../xefm/windows_app/build_msix.ps1, doc/dev/WINDOWS_STORE_MSIX_PLAN.md there has the full background). The Store route exists because it makes Windows code signing free: the package is uploaded unsigned and Microsoft re-signs it during certification — no certificate to buy, no SmartScreen warning, and a winget install --source msstore line for free.

macOS: macos_app/

Shared tooling

Release pipeline

make tagmake release-githubmake release-macos-dmg (on macOS) / make release-windows-zip (on Windows) → make release-whlmake release-skill; make release-status shows where a version stands. make release-windows-msix (on Windows) is the odd one out: it submits to the Microsoft Store, not the GitHub Release. Supporting scripts: tools/{release_preflight,bump_version,_version_source}.py. The version lives in keyhac/__init__.py (__version__), one number for both OSes.

Release artifacts per version: Keyhac-<ver>-macos.dmg, Keyhac-<ver>-win64.zip, the skill bundles - one zip per folder in keyhac/skills/, all attached to the GitHub Release - and the keyhac wheel on PyPI. PuiKit is versioned/released independently on PyPI; Keyhac2 pins a minimum (puikit>=1.0.10).

release-skill is not optional the way it looks. The bundles are the only way a user can obtain the skills — make skill-bundle needs the Makefile and tools/, and neither ships — so a release without it leaves doc/ai-integration.md pointing at an asset that is not there, and leaves anyone who connects the MCP endpoint in the half-installed state that fails quietly: the tools work and the actions come back full of sleep and screen coordinates.

Data & config paths (unified)

Item Path (both OSes)
Config ~/.keyhac/config.py (template copied on first run)
Extensions ~/.keyhac/extensions/ (on sys.path)
Clipboard history ~/.keyhac/clipboard.json
App settings ~/.keyhac/settings.json

With --config PATH, clipboard.json and settings.json live beside the config (sandbox isolation). Windows portable mode (everything next to Keyhac.exe) is not implemented yet — issue tracker.