Hold Key / Auto-Repeat
type_keyboard is an instant down+up and input_macro.run_sequence can
hand-roll a press / wait / release, but there was no primitive for “hold this key
for N seconds” (game movement, hold-to-scroll) or “send it at R presses per
second” (auto-repeat).
plan_key_hold() builds the deterministic op-plan (pure, unit-testable);
hold_key() dispatches it through an injectable sink and sleep so it
is tested without real input or real waiting. Imports no PySide6.
Headless API
from je_auto_control import hold_key, plan_key_hold
hold_key("key_d", duration_s=1.5) # press, hold 1.5s, release
hold_key("key_down", duration_s=2.0, rate_hz=20) # 40 key events @ 50ms
plan_key_hold("space", 1.0)
# [{'op': 'press', 'key': 'space'},
# {'op': 'wait', 'seconds': 1.0},
# {'op': 'release', 'key': 'space'}]
With rate_hz unset the key is pressed, held for duration_s, then released.
With rate_hz set it is sent as round(duration_s * rate_hz) discrete key
events spaced 1 / rate_hz apart — simulated auto-repeat for movement / scroll
loops. A non-positive duration or rate raises ValueError. hold_key routes
the wait steps to sleep and the key steps to sink, both injectable.
Executor commands
AC_hold_key takes key plus duration_s and an optional rate_hz and
returns {ops, plan}. It is exposed as the MCP tool ac_hold_key and as a
Script Builder command under Keyboard.