- Refactor `run_local_command` to manage subprocesses more effectively, ensuring child processes are terminated on timeout.
- Introduce `_terminate` function to handle process group termination and capture output.
- Implement `_command_output` to format command results with a character limit.
- Add local intent recognition in `intents.py` to handle commands like "stop", "go to sleep", and "come here" without server interaction.
- Normalize user input to match local intents while stripping filler words.
- Update tests to cover new local intent functionality and ensure proper command handling.
- Enhance speech processing to handle abbreviations and improve spoken output clarity.
petctl gains screen verbs: `jump` (1-based number, name, next/prev/
primary/other, or a direction resolved from real geometry), `monitors`,
and `read` for OCR of a monitor's contents.
- monitors.py: pure layout model + jump-target resolution. The monitor
list is published by PetWindow from QGuiApplication.screens() over a
queued signal, so the controller and window agree on what "monitor 2"
means; xrandr and Qt order screens differently on the same machine.
- screen_text.py: pull-only OCR (mss capture + Tesseract/RapidOCR).
Nothing captures unless the server asks, and the text rides back up
the tool-result relay so Bolt can read a screen mid-turn. Both deps
optional, soft-failing with a reason. SCREEN_TEXT=false removes it.
- Query verbs are answered in controller._handle_command rather than
pet_actions.describe(), because their output is the point.
- scripts/generate_bolt_sprites.py draws every frame; walk/ is a
side-view cycle stepped by distance travelled, not by the animation
timer, so the planted paw tracks the window exactly. sprite.py loads
it via EXTRA_ANIMATIONS keyed by name, with has() so callers can
decline a placeholder blob.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>