Skip to content

[Windows] External voice command router pattern using history.json + Python #1765

Open

Description

@GhostDragon420

Hey team — wanted to share a clean Windows workaround I built for triggering local commands/scripts from Hex without fighting the hex.config.ts sandbox.

Instead of running processes inside QuickJS, a lightweight background Python watcher (watcher.pyw) tails %APPDATA%\hex\history.json (timestamp_ms + raw_text) and matches spoken phrases against an external commands.json registry to launch local executables. (Note: since this watches history.json externally rather than intercepting inside hex.config.ts, Hex will still paste the spoken text if a text-accepting window is focused—so it works best when dictating from the desktop/taskbar or with no text field focused.)

Why this works well on Windows

  • Zero changes to Hex — no hex.config.ts sandbox friction or permission prompts.
  • Resilient to deleted history entries — tracks timestamp_ms rather than assuming contiguous id sequences.
  • Dynamic hot-reloading — adding new commands or script triggers to commands.json takes effect immediately without restarting Hex or the watcher.

commands.json

Change the the watcher.py to watcher.pyw if you dont want the terminal open, but with terminal open you do get to see the script work and catch the commands and everything.
watcher.py

Activity

  1. thdxr commented on Nov 13, 2026

    @thdxr
    Member

    you can do this inside hex instead, with nothing pasted.

    • spoken commands were removed in 3.0, so hex.config.ts only holds transformations now
    • a transformation can check the dictated text, launch a program with execFile from node:child_process, and return an empty string so nothing pastes
    • click allow on the trust card in the output pane
    • it runs after every dictation in the mode it's in, so return the text unchanged when no phrase matches
    • use execFile, never build a shell command from dictated text
    • https://anoma.ly/hex/docs/transforms.md

    the history file format isn't promised and may change. hex history --json is the stable output for outside tools.

    would a transform cover what you need, or do you want hex to recognize phrases itself, and what do you launch most?

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

  2. added
    needs-designNeeds a closer look or a product decision before we build it
    on Nov 13, 2026
  3. GhostDragon420 commented on Nov 13, 2026

    @GhostDragon420
    Author

    Thanks for the breakdown on how hex.config.ts transforms and execFile work in 3.0—that's really helpful context, especially knowing hex history --json is the stable interface going forward. Tailing history.json was just a simple starting point for me, so if that format changes, swapping the input over to hex history --json or whatever else is easy enough.

    Being honest, TypeScript isn't really my specialty compared to Python and JSON—and keeping the command router external in Python + JSON is actually intentional for my workflow. The projects I work on range anywhere from game mods to full-blown custom programs, so having a flexible external setup fits best:

    • Cross-OS & tool-agnostic: Python and a plain JSON config run identically across Windows, Linux, and macOS without tying my command logic to TypeScript or a single app's internal runtime.
    • Live debugging & hot-swapping: Running watcher.py in a terminal gives me instant stdout/debugging feedback while I test triggers, and editing commands.json on the fly lets me wire up new actions without touching Hex config files.
    • What I'm launching most: Mostly local automation scripts, OBS/streaming controls (clips, scene switches, overlays), custom program hooks, and external game/mod bridges (like sending local triggers or RCON/IPC actions while gaming without alt-tabbing).

    As far as design direction goes: a transform works well for power users who want to suppress text pasting, while native phrase-to-command mapping (backed by a simple JSON registry or CLI hook) would make it super accessible for gamers, streamers, and anyone relying on voice controls for accessibility without needing to write TypeScript. Happy to help test or share more feedback as you guys explore it!

  4. GhostDragon420 commented on Nov 13, 2026

    @GhostDragon420
    Author

    Just wanted to follow up with a working pattern I put together on Windows in case it’s helpful for folks who aren't comfortable writing TypeScript—while keeping your transforms.ts flow completely untouched for power users.
    How the Flow Works

    Hex → Clipboard Only: Hex transcribes and copies to the clipboard without firing a paste event.
    
    Python Gatekeeper (watcher.py): A lightweight Python script monitors clipboard updates and checks the text against a simple commands.json file:
    
        If it matches a command: It clears the clipboard immediately and launches the mapped app/script—nothing ever leaks into an active window or game chat.
    
        If it’s normal dictation: It triggers a native OS paste (Ctrl+V) so standard voice-to-text drops right wherever your cursor is.
    

    Why This Works Well Alongside 3.0

    Two Tiers for Different Skill Levels: Devs and power users keep the full synchronous control of transforms.ts, while everyday users, gamers, or admins get a zero-compile Python + JSON setup they can tweak in seconds.
    
    Cross-OS Potential: On Windows, I replicated your native SendInput scan-code paste via built-in ctypes so games and terminals accept it reliably. The exact same Python/JSON pattern could easily map to CGEvent/osascript on macOS and xdotool/ydotool on Linux so all three platforms get parity without forcing everyone into a JS/TS runtime.
    

    Happy to share the working Windows snippet if you guys ever want to look at it or adapt the paste layer for Linux and Mac!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-designNeeds a closer look or a product decision before we build it

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions