Multi-monitor jumps (`petctl jump`/`monitors`), pull-only screen OCR
(`petctl read`), generated sprite art with a distance-stepped walk cycle,
plus the filectl file ops and server file delivery merged back in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>