Skip to content

Releases: eliBenven/freemicro

FreeMicro 0.5.0

Choose a tag to compare

@github-actions github-actions released this 27 Jul 21:27

[0.5.0] - 2026-07-27

Added

  • freemicro update upgrades to the latest release (detects pipx vs pip),
    and --check reports whether one exists without installing. Offline-safe.

Changed

  • The Codex coexistence split is now conditional. With agent_keys.keys set
    to a subset, FreeMicro honors it only while the ChatGPT app is running; when
    Codex is not running there is no one to share with, so FreeMicro owns all six
    keys. It expands and contracts within a few seconds as Codex quits or launches,
    with lighting and presses always agreeing on which keys are FreeMicro's.

FreeMicro 0.4.1

Choose a tag to compare

@github-actions github-actions released this 27 Jul 20:06

[0.4.1] - 2026-07-27

Fixed

  • Coexistence split now covers key presses, not just lighting. With
    agent_keys.keys set to a subset, pressing a key FreeMicro did not own (a
    Codex key) still ran its binding and could open a new terminal on top of
    Codex. Un-owned Agent Keys are now inert on FreeMicro's side entirely, so the
    three-and-three split with Codex behaves on press too. (Note: the pad's key
    presses are read by both apps at once, so this stops FreeMicro acting on
    Codex's keys; it cannot stop Codex reacting to FreeMicro's keys - that needs a
    setting on the Codex side.)

FreeMicro 0.4.0

Choose a tag to compare

@github-actions github-actions released this 27 Jul 16:37

[0.4.0] - 2026-07-27

Added

  • Coexist with Codex (agent_keys.keys). FreeMicro can own a subset of the
    six Agent Keys and leave the rest for the ChatGPT app, so Codex and Claude
    Code can share the pad - three keys each, for example. FreeMicro sends a
    partial thstatus for only its keys (leaving the others as Codex set them)
    and auto-reasserts them so the split holds. Default is all six, unchanged.
    "agent_keys": {"keys": [3, 4, 5]}.

FreeMicro 0.3.0

Choose a tag to compare

@github-actions github-actions released this 26 Jul 23:46

[0.3.0] - 2026-07-26

Configurability: the pad adapts to context, and every key can carry more.

Added

  • Per-app profiles (profiles): one key does different things depending on
    which app is frontmost. Overrides only the keys it names; matched by app name
    (exact, then longest substring). The frontmost app is read off a 300ms cache,
    so a keypress is a dict lookup and no profiles means no OS lookup at all. A
    hold pressed in one app still releases cleanly if you switch mid-hold.
  • Layers (layer action + layers): hold a key for a second binding set,
    Fn-style. Precedence is layer over profile over base. This is what gives the
    pointer its click back and puts /effort on a held layer without either
    stealing the other. A stuck layer cannot latch on (reuses the hold safety).
  • Tap-to-click on the joystick: a quick stick tap is a left click, making
    the pointer a full trackpad. A tap must return to centre within 0.2s and move
    the cursor under 30px, so a fast move never clicks. On by default in pointer
    mode.
  • New terminal on an empty Agent Key: pressing a dark key (no project) opens
    a new terminal window instead of doing nothing. On by default; disable with
    terminal_app: false.

FreeMicro 0.2.0

Choose a tag to compare

@github-actions github-actions released this 26 Jul 22:49

[0.2.0] - 2026-07-26

A batch of lighting and awareness features, plus a joystick fix.

Added

  • Software blink effect. The firmware has no hard on/off flash (only the
    smooth breath), so FreeMicro blinks in software from the render tick. One
    Agent Key can blink error while the others stay solid. It is also the
    accessibility answer: a distinct effect per state is a redundant channel that
    does not depend on colour vision. freemicro lights --effect blink works too.
  • Attention flash on entry (lighting.flash_on, default waiting/error): one
    brief pulse in the state's own colour when an element enters that state.
  • Named themes (lighting.theme): factory, nord, solarized, and a
    high-contrast colourblind-safe palette that leans on distinct effects.
    Explicit per-state colours still win. Picker in the web UI.
  • Battery cue (off by default): a configurable low-battery look composited
    over a zone. Reads the cached status file, never the vendor channel.
  • Alerts (off by default): a system sound and a Notification Center banner
    on agent state transitions, so you are not blind when looking away.
    freemicro alerts --test.

Fixed

  • The joystick no longer stops mid-hold. The pad falls silent while a
    direction is held steady (a 2.1s gap was measured with the stick deflected),
    and the 250ms stale-stop read that as a disconnect and froze the cursor. The
    window is now 4s, clear of the real gap; release is still instant.

FreeMicro 0.1.2

Choose a tag to compare

@github-actions github-actions released this 26 Jul 22:05

[0.1.2] - 2026-07-26

Added

  • Launch on connect (freemicro daemon install --on-connect). A LaunchAgent
    whose LaunchEvents IOKit matcher starts FreeMicro the moment the Codex Micro
    appears (matched by VID/PID over both BLE and USB), so pressing a button wakes
    the connection and the pad comes alive with nothing running while it is away.
    Mutually exclusive with the login daemon, swept by uninstall, and it refuses
    from a ~/Desktop checkout (a LaunchAgent cannot read that folder). The
    install prints the exact launchctl steps to confirm it fires, since IOKit
    matching on BLE HID is less documented than on USB.
  • HID report map in docs/PROTOCOL.md. The full 216-byte report descriptor
    read from a real unit over BLE, decoded: boot keyboard on Report ID 1,
    consumer on 2, mouse on 3, and the vendor 0xFF00 channel on Report ID 6 with
    63-byte In/Out/Feature reports. It confirms, from the silicon, the channel
    this project was built on. Captured for a firmware author building a
    ZMK-compatible board (issue #1).

Also documentation from real use and an interop request.

Changed

  • Effort dial documented correctly. Claude Code's /effort slider is driven
    by LEFT/RIGHT arrows, not up/down, and /effort alone opens it on submit. The
    shipped default keeps the dial as a pointer companion; the exact rebind for an
    effort dial (turn sends left/right, press opens /effort) is now in the
    default's notes. Corrects an earlier wrong assumption.

FreeMicro 0.1.1

Choose a tag to compare

@github-actions github-actions released this 24 Jul 19:27

[0.1.1] - 2026-07-24

Fixes from the first day of real use.

Fixed

  • Stuck modifier after push-to-talk. A modifier key-up was posted with the
    event's default flags (which for a modifier keycode include its own flag), so
    macOS believed e.g. control was still held after the mic released. Enter then
    became ctrl+Return and clicks became right-clicks. key_event now always sets
    the flags it is given, and a chord releases with decreasing flags. Two
    backstops added for a genuinely dropped pad event: a repeated key-down
    releases the stale hold first, and a hold held past 120s auto-releases.
  • A long submit prompt dropped a newline instead of sending. The text is
    typed as one fast Unicode burst that a TUI reads as a paste; a Return arriving
    inside it became a literal newline. Typed text now settles before the Return.
    A per-binding submit_delay tunes it.

Added

  • double_tap on a hold binding fires a second, different shortcut on a
    double-tap while keeping the physical push-to-talk hold, so the mic key can
    drive a hold-mode app on hold and a toggle-mode app on double-tap.

Changed

  • The shipped default's REJ cap is now a plain Escape (interrupt Claude,
    clear the input, decline a prompt) rather than the gated answer_permission reject. Approve stays session-targeted.

FreeMicro 0.1.0

Choose a tag to compare

@github-actions github-actions released this 24 Jul 17:16

[0.1.0] - 2026-07-24

First public release.

The arc of this release, honestly: FreeMicro began as a state engine and a
renderer registry with no way to drive the actual pad. The Codex Micro's
LED protocol was unknown, so it shipped four fallback renderers while the
investigation ran. It ends with that protocol reverse engineered, published, and
verified on hardware over both USB and Bluetooth - every key, the dial, the
thumbstick and all three LED zones - plus a loop that runs at login without a
terminal open. The fallbacks are gone with it: they insured a risk that no
longer exists, and one of them could abort the process.

Added - talking to the pad

  • freemicro.device - the pad's 0xFF00 vendor JSON-RPC channel, over
    USB and Bluetooth. Transport-aware write framing (63 bytes with no
    report-id prefix on USB, 64 bytes with 0x06 prefixed on BLE), a pure and
    therefore testable framer and decoder, a single shared device handle per
    process, and a reconnect loop that treats disconnects as normal rather than
    fatal.
  • docs/PROTOCOL.md - the wire protocol, written up as interface facts and
    believed to be its first public documentation: methods, key ids, the lighting
    objects, the effect enum, and the per-transport framing trap. Plus
    docs/CUSTOMIZING.md and docs/FACTORY-DEFAULTS.md.

Added - input

  • Input bridge (freemicro.input): every key, the dial press, the dial's
    rotation ticks and the analogue thumbstick, routed to user-configured actions.
    The pad emits no scancodes, so this is what makes it do anything at all.
  • CGEvent keystroke backend with AppleScript fallback: adds fn support,
    true hold-to-talk, mouse control, and no subprocess per keystroke.
  • Eight action kinds (text, key, hold, shell, applescript, app,
    mouse, none) in a registry where adding a ninth is one decorated function,
    validated at config-load time.

Added - lighting

  • micro-leds renderer: drives the backlight, underglow and the six Agent
    Keys from agent state via lights.preview + v.oai.thstatus. Opt-in - freemicro lights --enable - because macOS shares this device and FreeMicro
    should not seize LEDs somebody else may be driving.
  • Factory-matching default colours, and a decay on done (factory green means
    unread, not completed).

Added - the loop is proven, and it runs without you

  • freemicro selftest: fires a whole synthetic Claude Code session through
    the exact command recorded in your settings, as a subprocess with the event
    JSON on stdin, against a throwaway state directory - then asserts the resolved
    state for each event and the LED protocol messages for each state. This
    closes the project's oldest gap: every piece was unit-tested and the assembly
    never was. --json for CI, --in-process when iterating on the classifier.
  • freemicro start: guided setup end to end - both macOS permissions
    (detected, explained, and the right System Settings pane opened on request),
    the pad, vendor-app contention, the config, the LED opt-in (default still
    no), the hooks (installed and verified), the daemon, and a closing light
    show. Idempotent, and every prompt has a default so --yes never hangs.
  • freemicro daemon install|uninstall|status|logs: a launchd LaunchAgent
    that starts at login, restarts if it dies, and logs to a size-capped file. The
    pad emits no scancodes, so without something listening the hardware is dead - this is what stops that meaning "keep a terminal open forever".
  • Pad lock (~/.freemicro/pad.lock): only one process can usefully hold this
    device, so run, keys and the daemon take an flock and name whoever has
    it
    instead of fighting. Being an flock, a killed process can never leave a
    stale lock behind. --take-pad overrides.
  • freemicro.permissions: Input Monitoring via IOHIDCheckAccess and
    Accessibility via AXIsProcessTrusted - read-only, prompt-free, and they work
    with no pad attached, so "did you grant it?" is no longer inferred from a
    failure three layers down. doctor reports "macOS hasn't decided yet" as the
    distinct third state it really is.
  • freemicro doctor: preflight for both macOS permissions, transport,
    battery, and a real device.status round trip - the only honest write test on
    a device where success return codes mean nothing.
  • freemicro run: keys in and lights out in one process, one device handle.
  • User-owned pad config (~/.freemicro/keymap.json, freemicro keys --init):
    every input and every LED colour/effect in one annotated JSON file. JSON
    rather than TOML so the core stays dependency-free on Python 3.9.

Added - release readiness

  • freemicro.compat: records the firmware the protocol was actually
    verified on (v0.4.1) and classifies whatever a pad reports as known-good,
    newer, older or unparseable. It warns and explains; it never gates.
  • freemicro.diagnostics: a redacted diagnostic bundle for bug reports - OS, Python, transport, firmware, battery, permissions, renderer availability
    and config structure, in text and JSON. The contents of shell and
    applescript bindings and every path outside the config directory are
    stripped before anything is printed, so the output is safe to paste into a
    public issue.
  • SECURITY.md and docs/SECURITY-MODEL.md: what a config can actually
    do, what the two macOS permissions really grant and to whom, a threat model
    (malicious preset, local process on the web UI, unattended daemon, config
    tampering), and a concrete design for preset trust that must ship alongside
    shareable presets.
  • docs/RELEASING.md: version bump through PyPI and GitHub release, with
    the exact commands.
  • macOS CI. The product is macOS-only and CI was Linux-only, so every IOKit,
    CGEvent and permissions path was untested. There is now a macos-latest
    job (3.11 - 3.13, plus 3.9/3.10 on the x86_64 runner) that runs headless - no
    hardware, no synthetic keystrokes, and a step that fails the build if anything
    imports tkinter. Linux is kept across 3.9 - 3.13 to prove the pure-logic layers stay
    portable. Ruff runs as its own job, and a packaging job installs the built
    wheel into a clean venv and runs it.
  • Issue templates for bug reports (asking for freemicro doctor --report)
    and hardware reports (VID/PID and firmware version, feeding the crowdsourced
    capability database and compat.KNOWN_GOOD).

Added - the original scaffold

The layers that predate the hardware work, and are still the backbone:

  • State engine (freemicro.state): Claude Code hooks → five normalized
    states, per-session store with priority resolution and TTL expiry.
  • Renderer registry (freemicro.renderers): originally five renderers with
    a guaranteed non-pad fallback. Four were deleted before release; see
    Removed. What survives is the registry and micro-leds.
  • Detector (freemicro.detector): read-only HID capability probe for
    Milestone 0.
  • Sniff & replay (freemicro.capture, freemicro.protocol, freemicro learn, micro-sniffed renderer): capture the ChatGPT app's own LED HID
    reports per Codex state and replay them from Claude Code for an exact
    replication of the Agent-Key behaviour. Parses tshark/Wireshark JSON or plain
    hex captures, maps Codex states (thinking/running→working, awaiting→waiting,
    …) to FreeMicro states, stores literal per-state frames, and optionally infers
    RGB byte offsets from solid-colour captures. Full procedure in
    docs/SNIFF-RUNBOOK.md. (Owner-authorized protocol interop; no firmware is
    extracted or redistributed.)
  • LED write-test (freemicro.verify / freemicro verify-leds): actively
    drives the writable LED renderers through the state sequence (Path A from
    docs/LED-STRATEGY.md), captures a human verdict (did the Agent Keys move?
    per-key or global? app-quit needed?), and writes a report for the capability
    DB. Uses only the documented VIA/QMK path - no firmware, no proprietary
    protocol.
  • CLI (freemicro): detect, install, hook, demo, emit, render,
    renderers, status. Hooks are installed with an absolute interpreter path
    so they resolve under Claude Code's minimal environment.
  • freemicro demo: plays the full state sequence on the real renderer so
    the end-to-end loop is visible with no agent or hardware attached.
  • Crowdsourced hardware DB scaffold (hardware/capabilities.json).
  • Test suite (57 tests) covering the state engine, hook classifier, renderer
    selection, hook installer, detector, the LED write-test harness, capture
    parsing / learning / replay, and a full-session end-to-end pipeline (hook
    events → store → resolved state).

Changed

  • Hook installation is now absolute, quoted, and self-repairing. The command
    is resolved from the binary you actually invoked (then the interpreter's
    bin/, then PATH) and shlex-quoted, so a virtualenv in a path with a space
    no longer produces hooks that silently do nothing. Re-running freemicro install after moving or reinstalling rewrites the stale entry rather than
    leaving it. Entries carry a timeout, and --uninstall removes exactly ours.
  • freemicro install verifies itself by running the selftest, unless you
    pass --no-verify. "Installed" and "working" are not the same claim.
  • freemicro doctor now also checks Input Monitoring directly, whether the
    hooks are installed and still point at this binary, and the daemon.
  • freemicro daemon install refuses to claim success it hasn't seen: it
    waits for a stable pid (launchd reports one for a process already dying) and,
    if the daemon never comes up, reads its log and explains why. It specifically
    detects a binary installed under ~/Desktop/~/Documents/~/Downloads, which
    macOS blocks background agents from reading - an e...
Read more