parallels-vm
Parallels Desktop
Use this skill for Parallels VM work on Peter's Mac.
Guest OS split:
- macOS guest: use
prl-macos-* - Linux guest: use
prl-linux-* - Windows guest: use
prl-windows-* - unknown guest: probe with
prlctl exec "<vm>" --current-user sh -lc 'uname -a; cat /etc/os-release 2>/dev/null || true'before choosing wrappers
Primary tools:
prlctlfor power, snapshots, guest exec, guest screenshots, VM infopeekaboofor host-side GUI automation of the Parallels app/windowssh/ Screen Sharing / RDP inside the guest for robust control- helper wrappers live in
~/Projects/agent-scripts/skills/parallels-vm/scripts/; they are not copied into each target repo'sscripts/
Fast Path
- Verify
prlctlexists:which prlctl - List VMs:
prlctl list -a - Inspect target VM:
prlctl list -i "<vm>" - Prefer guest-native control first:
prlctl exec "<vm>" --current-user ...ssh user@<guest-ip>
- Use
peekabooonly when GUI interaction is required.
Guest exec pitfalls:
prlctl exec "<vm>" ...without--current-useroften runs asroot; this is wrong for per-user launchd checks on macOS guestsprlctl execcan have a minimal PATH even with--current-user; Homebrew CLIs may fail withcommand not foundorenv: node: No such file or directoryprlctl execalso mangles shell-heavy commands more often than it should on macOS guests; for pipes, redirects, heredocs, or interactive flows, preferprlctl enter "<vm>" --current-user --use-advanced-terminal- prefer absolute guest paths for Homebrew tools on macOS guests, for example
/opt/homebrew/bin/openclaw,/opt/homebrew/bin/node,/usr/bin/grep,/usr/bin/plutil - if you need shell features or user PATH, wrap with a real shell explicitly:
prlctl exec "$VM" --current-user zsh -lc '<cmd>' - if a guest-installed npm CLI fails under
prlctl execbut works interactively, bypass the shebang and run the entrypoint with absolute Node, for example/opt/homebrew/bin/node /opt/homebrew/lib/node_modules/openclaw/openclaw.mjs ... - when validating a same-version npm reinstall inside the guest, use a cache-busted tarball filename and inspect the installed compiled file, not just
--version; npm may leave the old hashed bundle in place if you keep reinstalling the same filename/version pair
Reusable helpers:
scripts/prl-linux-openclaw.sh <vm> [--env KEY=VALUE ...] <openclaw-args...>: run guest OpenClaw on Linux via resolved CLI pathscripts/prl-linux-install-openclaw.sh <vm> [--version latest]: run the website installer on a Linux guestscripts/prl-linux-gateway-status-version.sh <vm> [--profile <name>] [--state-dir <dir>] [--json]: fetch Linux guest gateway status and extractruntimeVersion,rpc.ok, pid, and port datascripts/prl-linux-openclaw-update-verify.sh <vm>: end-to-end published Linux release smoke using manual gateway verificationscripts/prl-macos-enter.sh <vm>: open a real guest shell viaprlctl enterscripts/prl-macos-pnpm.sh <vm> <guest-repo-dir> <pnpm args...>: run guestpnpmthrough absolute Homebrew Node + PATHscripts/prl-macos-vitest.sh <vm> <guest-repo-dir> [--env KEY=VALUE ...] [--] <vitest-args...>: run guest Vitest viapnpm exec vitest --root <repo>; use this instead of hand-rolled guest Vitest commandsscripts/prl-macos-repo-openclaw.sh <vm> <guest-repo-dir|guest-entry.js> [--env KEY=VALUE ...] <openclaw-args...>: run a guest checkout'sopenclaw.mjs/distentry directly; use this for secret-bearing onboard/live commands instead ofpnpm openclaw ...scripts/prl-macos-live-auth-seed.sh <vm> [--agent <agent-id>] [--guest-path <path>] [--provider <provider:ENV_VAR> ...]: build a minimal guestauth-profiles.jsonfrom host env vars and seed it; default providers areopenai:OPENAI_API_KEY+anthropic:ANTHROPIC_API_KEYscripts/prl-macos-download.sh <vm> <url> <guest-path>: download a URL to a guest file first; safer thancurl | bashthroughprlctl execscripts/prl-macos-openclaw.sh <vm> [--env KEY=VALUE ...] <openclaw-args...>: run guest OpenClaw via absolute Node + resolved entrypointscripts/prl-macos-install-openclaw.sh <vm> [--version latest]: run the website installer reliably inside the guestscripts/prl-macos-gateway-status-version.sh <vm> [--profile <name>] [--state-dir <dir>] [--json]: fetch gateway status and extractruntimeVersion,rpc.ok, pid, and port datascripts/prl-macos-openclaw-update-verify.sh <vm>: end-to-end published release smoke; install old version fromopenclaw.ai, verify gateway, update to latest, re-verify, and auto-fallback to manual gateway launch when Tahoe launchd bootstrap is brokenscripts/prl-macos-auth-seed.sh <vm> <local-auth-profiles.json|->: seedauth-profiles.jsoninto the guest with base64 transportscripts/prl-windows-install-openclaw.sh <vm> [--version latest] [--spec <npm-spec-or-url>]: run the Windows website installer with-NoOnboard, or install a host-served/current-checkout npm spec directly; native installer now bootstraps user-local portable Git under%LOCALAPPDATA%\OpenClaw\deps\portable-gitscripts/prl-windows-overlay-openclaw.sh <vm> --spec <npm-spec-or-url>: overlay a host-served/current-checkout tgz onto the existing native Windows global install while keeping itsnode_modules; use this when you need upcoming-build code fast and a raw unpacked tgz is missing runtime depsscripts/prl-windows-openclaw.sh <vm> [--env KEY=VALUE ...] <openclaw-args...>: run guest OpenClaw on native Windows through a PowerShell wrapper that avoidsnpm.ps1/ shell quoting nonsensescripts/prl-windows-gateway-status-version.sh <vm> [--json]: fetch native Windowsgateway status --json; strips CLIXML and trailing guidance text before parsingscripts/prl-windows-openclaw-update-verify.sh <vm>: end-to-end native Windows release smoke; can start from a published version or a host-served/local npm spec, attemptsupdate --json, tolerates trailing post-JSON guidance text, reports known release blockers, waits for the user session first, and skips the reinstall automatically when the VM is already on the requested--from-versionunless you pass--force-reinstallscripts/prl-windows-onboard-verify.sh <vm>: native Windows onboarding smoke; separates "embedded/provider setup works" from "managed gateway install works", captures expected no-daemon health failures, surfaces Scheduled Task access-denied errors cleanly, and can optionally run an embedded OpenAI hatch in the same wrapper via--hatch
Purpose-Built Wrappers
When the task is about OpenClaw install/update verification, prefer the OS-matched wrappers over ad-hoc prlctl exec:
-
Linux guest:
prl-linux-install-openclaw.shprl-linux-openclaw.shprl-linux-gateway-status-version.shprl-linux-openclaw-update-verify.sh
-
macOS guest:
prl-macos-install-openclaw.shprl-macos-openclaw.shprl-macos-gateway-status-version.shprl-macos-openclaw-update-verify.sh
-
Windows guest:
prl-windows-install-openclaw.shprl-windows-overlay-openclaw.shprl-windows-openclaw.shprl-windows-gateway-status-version.shprl-windows-openclaw-update-verify.sh
-
prl-macos-install-openclaw.sh: downloadsinstall.shto the guest first, then runs it with explicit PATH/env -
prl-macos-openclaw.sh: bypasses shebang/PATH issues by calling guest OpenClaw with absolute Node +dist/entry.js -
prl-macos-gateway-status-version.sh: normalizes noisygateway status --jsonoutput into a compact version/probe summary -
prl-macos-openclaw-update-verify.sh: does the Tahoe-style "install old -> verify gateway -> update -> verify gateway" flow and falls back to a detached manualgateway runprobe after forcinggateway.mode=localif LaunchAgent bootstrap fails -
prl-linux-install-openclaw.sh: runs the website installer on Ubuntu/Debian-style guests and then resolves the installedopenclawCLI path -
prl-linux-openclaw.sh: runs guest OpenClaw on Linux via resolvedopenclawbinary path and normal PATH -
prl-linux-gateway-status-version.sh: normalizes Linux guestgateway status --jsonoutput into the same compact summary -
prl-linux-openclaw-update-verify.sh: verifies Linux releases with a detached manualgateway runpath instead of assuming launchd/systemd service setup -
prl-windows-install-openclaw.sh: runs the public PowerShell installer with-NoOnboard, or installs a local/hosted npm spec directly, relying on the installer's user-local MinGit bootstrap instead of adminwingetGit -
prl-windows-overlay-openclaw.sh: overlays a packed build onto the guest's existing global OpenClaw install so the current code can reuse the installed dependency tree; use this when--specreinstall is too slow or when unpacking a tgz directly would miss runtime deps likechalk -
prl-windows-openclaw.sh: runs native Windowsopenclawvia encoded PowerShell, which avoids PATH and execution-policy footguns -
prl-windows-gateway-status-version.sh: returns parsed status JSON even whengateway status --jsonappends human guidance after the JSON block -
prl-windows-openclaw-update-verify.sh: captures native Windows version/install/update behavior even when the published release is partially broken; use--from-specand--update-specto keep the whole smoke on host-served artifacts before npm publish, and lean on the built-in skip-reinstall fast path for repeat runs against the same--from-version -
prl-windows-onboard-verify.sh: use this when the question is specifically "does native Windows onboarding succeed?" because it distinguishes expected local-health failures from real daemon install failures and can optionally prove embedded-provider hatching in one pass -
prl-macos-auth-seed.sh: avoids fragile inline JSON writes when a live test needs stored auth profiles
Core Commands
VM="macOS Tahoe"
prlctl start "$VM"
prlctl status "$VM"
prlctl list -i "$VM"
prlctl exec "$VM" --current-user whoami
prlctl capture "$VM" --file /tmp/parallels-shot.png
prlctl snapshot "$VM" --name pre-change
prlctl snapshot-list "$VM" --tree
prlctl snapshot-switch "$VM" --id <snapshot-id>
prlctl snapshot-delete "$VM" --id <snapshot-id>
Helper examples:
VM="macOS Tahoe"
REPO="/Users/steipete/Projects/openclaw-parallels-gpt54"
scripts/prl-macos-pnpm.sh "$VM" "$REPO" install
scripts/prl-macos-pnpm.sh "$VM" "$REPO" build
scripts/prl-macos-pnpm.sh "$VM" "$REPO" check
scripts/prl-macos-pnpm.sh "$VM" "$REPO" test
scripts/prl-macos-vitest.sh "$VM" "$REPO" run src/plugins/discovery.test.ts
scripts/prl-macos-repo-openclaw.sh "$VM" "$REPO" --version
source ~/.profile >/dev/null 2>&1
scripts/prl-macos-live-auth-seed.sh "$VM"
scripts/prl-macos-vitest.sh "$VM" "$REPO" --env "OPENCLAW_LIVE_MODELS=openai/gpt-5.4,anthropic/claude-opus-4-6" --env "OPENCLAW_LIVE_TEST=1" --env "CLAWDBOT_LIVE_TEST=1" --env "OPENAI_API_KEY=$OPENAI_API_KEY" --env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" -- run --config vitest.live.config.ts src/agents/models.profiles.live.test.ts
scripts/prl-macos-vitest.sh "$VM" "$REPO" --env "OPENCLAW_LIVE_GATEWAY_MODELS=openai/gpt-5.4,anthropic/claude-opus-4-6" --env "OPENCLAW_LIVE_TEST=1" --env "CLAWDBOT_LIVE_TEST=1" --env "OPENAI_API_KEY=$OPENAI_API_KEY" --env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" -- run --config vitest.live.config.ts src/gateway/gateway-models.profiles.live.test.ts
scripts/prl-macos-install-openclaw.sh "$VM" --version 2026.3.7
scripts/prl-macos-gateway-status-version.sh "$VM" --json
scripts/prl-macos-openclaw-update-verify.sh "$VM" --from-version 2026.3.7 --to-tag latest
VM="Ubuntu 24.04.3 ARM64"
scripts/prl-linux-install-openclaw.sh "$VM" --version 2026.3.7
scripts/prl-linux-gateway-status-version.sh "$VM" --json
scripts/prl-linux-openclaw-update-verify.sh "$VM" --from-version 2026.3.7 --to-tag latest
VM="Windows 11"
scripts/prl-windows-install-openclaw.sh "$VM" --version latest
scripts/prl-windows-install-openclaw.sh "$VM" --spec "http://10.211.55.2:8138/openclaw-next.tgz"
scripts/prl-windows-overlay-openclaw.sh "$VM" --spec "http://10.211.55.2:8138/openclaw-next.tgz"
scripts/prl-windows-openclaw.sh "$VM" --version
scripts/prl-windows-gateway-status-version.sh "$VM" --json
scripts/prl-windows-onboard-verify.sh "$VM" --json
source ~/.profile >/dev/null 2>&1
scripts/prl-windows-onboard-verify.sh "$VM" --openai-api-key-env OPENAI_API_KEY --hatch --json
scripts/prl-windows-openclaw-update-verify.sh "$VM" --from-version 2026.3.7 --to-tag latest
scripts/prl-windows-openclaw-update-verify.sh "$VM" --from-version 2026.3.7 --skip-install
scripts/prl-windows-openclaw-update-verify.sh "$VM" --from-spec "http://10.211.55.2:8138/openclaw-a.tgz" --update-spec "http://10.211.55.2:8138/openclaw-b.tgz"
scripts/prl-windows-openclaw-update-verify.sh "$VM" --from-version 2026.3.7 --update-spec "http://10.211.55.2:8138/openclaw-next.tgz"
source ~/.profile >/dev/null 2>&1
scripts/prl-windows-openclaw.sh "$VM" --env "OPENAI_API_KEY=$OPENAI_API_KEY" agent --local --agent main --json --thinking low -m "Reply with exactly WINDOWS-HATCH-OK."
Useful IP extractor:
vmip() { prlctl list -i "$1" | awk -F': ' '/IP Addresses/{print $2}'; }
SSH Bootstrap
Shared networking is usually enough for host-to-guest SSH. Check guest IP from prlctl list -i.
Probe SSH:
nc -G 2 -vz "$(vmip "$VM")" 22
If closed on a macOS guest, enable Remote Login in the guest:
- UI:
System Settings > General > Sharing > Remote Login - CLI in guest when creds allow:
sudo systemsetup -setremotelogin on
Then:
ssh "$USER@$(vmip "$VM")"
If the user wants stable local forwarding like localhost:2222, configure a Parallels NAT port-forward rule instead of relying on changing guest IPs.
macOS Guest Debugging
For launchd-managed services on a macOS guest, use the guest console user, not root:
VM="macOS Tahoe"
prlctl exec "$VM" --current-user 'whoami && echo HOME=$HOME && id -u'
Gateway / launchd checks that worked well:
VM="macOS Tahoe"
prlctl exec "$VM" --current-user 'label=ai.openclaw.gateway; domain=gui/$(id -u); plist=$HOME/Library/LaunchAgents/$label.plist; ls -l "$plist"; launchctl print "$domain/$label"'
prlctl exec "$VM" --current-user 'launchctl print-disabled gui/$(id -u) | /usr/bin/grep -E "ai\\.openclaw|openclaw" || true'
prlctl exec "$VM" --current-user 'lsof -nP -iTCP:18789 -sTCP:LISTEN || true'
prlctl exec "$VM" --current-user 'curl -i --max-time 5 http://127.0.0.1:18789/health || true'
Notes:
launchctl print gui/$(id -u)/<label>is the fastest way to prove “plist exists but launchd lost the service”curl http://127.0.0.1:<port>/healthandlsof -iTCP:<port>are more reliable than app-specific CLIs when PATH/env insideprlctl execis broken- if a service CLI fails under
prlctl exec, first verify whether the binary or its shebang target is missing from PATH before assuming the service is down
OpenClaw/Tahoe notes:
- For OpenAI + Anthropic live model coverage, default to
OPENCLAW_LIVE_MODELS="openai/gpt-5.4,anthropic/claude-opus-4-6"andOPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.4,anthropic/claude-opus-4-6"unless Peter asks for different models - When proving "what version is on main?" from a guest checkout, use
scripts/prl-macos-repo-openclaw.sh "$VM" "$REPO" --version.scripts/prl-macos-openclaw.sh "$VM" --versiononly checks the guest-global install and may lag or be missing after snapshot restore. - After
snapshot-switch/ snapshot restore, assume guest repo checkouts andauth-profiles.jsonmay be stale or gone. Recreate the checkout, pin it to the host commit under test, and reseed live auth before running gateway live tests. - Fresh macOS guests may have Homebrew
nodebut nopnpm; install once with/opt/homebrew/bin/node /opt/homebrew/lib/node_modules/npm/bin/npm-cli.js install -g pnpm scripts/prl-macos-pnpm.shnow auto-installs missing guestpnpmwith the guest Homebrewnode- Some Tahoe guests inherit permissive
umaskvalues (000); temp dirs/files can land as0777/0666and trip OpenClaw's world-writable plugin safety gates. If discovery/loader tests suddenly return no candidates, check permissions first before blaming cache/env logic. - Prefer argv-style guest commands (
git -C <repo> ..., wrappers, absolute paths) overprlctl exec ... sh -lc 'cd ... && ...'; Tahoe shell-wrapped git commands were flaky here. - For guest Vitest, prefer
scripts/prl-macos-vitest.sh; if you do it manually, usepnpm exec vitestand pass repo-root-relative test paths under--root <repo>. - For env-scoped guest
pnpm/Vitest runs, reuse the shared wrapper/lib path (prl_run_pnpm_envviascripts/prl-macos-vitest.sh) instead of hand-writingprlctl exec ... env ... node ... pnpm ...each time. prlctl execis fine for argv-style commands; for pipes, heredocs, JSON blobs, or multiline shell work, switch toscripts/prl-macos-enter.sh- Website installer verification should use
scripts/prl-macos-install-openclaw.sh, not rawcurl | bashthroughprlctl exec - Gateway version verification should use
scripts/prl-macos-gateway-status-version.sh; it strips pre-JSON warnings and reportsruntimeVersionwhen present - Real release smoke should prefer
scripts/prl-macos-openclaw-update-verify.sh; it now tolerates Tahoelaunchctl bootstrap ... Input/output errorby falling back to a detached manual gateway probe - Released builds may still print
/dev/tty: Device not configuredduring noninteractive installer tail work (doctor/ plugin updates); treat that as a follow-up bug unless the requested version failed to land - If manual gateway probing is needed, first force
gateway.mode=local; released builds can otherwise block startup withset gateway.mode=local (current: unset) or pass --allow-unconfigured - For listener checks, use
lsof -nP -iTCP:<port> -sTCP:LISTEN; plainlsof -i :<port>is too noisy on Tahoe - Avoid
pnpm openclaw ... --openai-api-key .../--anthropic-api-key ...in guest live setup;pnpmechoes the full argv. Usescripts/prl-macos-repo-openclaw.shor directnode <repo>/openclaw.mjs ...instead so secrets stay out of logs. - Noninteractive onboarding seeds one provider choice per run. For OpenAI + Anthropic live coverage, run onboard twice or seed
auth-profiles.jsondirectly. src/agents/models.profiles.live.test.tscan false-green with"[live-models] no API keys found; skipping"after a snapshot restore. Treat that as "not actually tested"; pass guest env keys explicitly and verify the log shows real model runs, not a skip.src/gateway/gateway-models.profiles.live.test.tscurrently filters on stored auth profiles; env-onlyOPENAI_API_KEY/ANTHROPIC_API_KEYis not enough there. Usescripts/prl-macos-live-auth-seed.sh "$VM"(orprl-macos-auth-seed.shwith a prepared file) before rerunning.
OpenClaw/Linux notes:
- Linux guests do not use the macOS Homebrew/
dist/entry.jsassumptions; use theprl-linux-*wrappers - Linux release verification uses manual
gateway runprobes, not launchd - Before assuming Linux support is broken, check whether
openclawwas installed into~/.local/bin,/usr/local/bin, or/usr/bin - Ubuntu guest labels in Parallels may lag the actual distro patch level; verify with
/etc/os-release
OpenClaw/Windows notes:
- Prefer the
prl-windows-*wrappers; rawprlctl exec ... powershell -Commandis fragile because quoting and PowerShell execution policy love breakingnpm.ps1/pnpm.ps1 - If you need multiline/native PowerShell work on Windows guests, use the wrapper/lib encoded path, not raw host-shell
powershell -Command "$..."; host-shell interpolation will happily strip guest$env:variables before the command even reaches the VM - After a hard reboot or snapshot switch, wait until
prlctl exec "$VM" --current-user whoamiworks again before trusting wrapper failures; Parallels Tools/user session readiness lags boot - Right after reboot, plain
prlctl exec "$VM" whoamimay already work asNT AUTHORITY\\SYSTEMwhile--current-userstill fails; that means the guest booted but the desktop user session is not ready yet - A black
prlctl captureright after reboot usually means "guest/session still waking up", not "Windows is dead" prl-windows-onboard-verify.shis the right probe for native onboarding. Without--install-daemon, a final gateway health failure is expected unless a local gateway is already running.- Native Windows managed gateway install currently goes through Scheduled Tasks.
schtasks create failed: ERROR: Access is denied.means admin PowerShell is the blocker, not provider auth. - Keep embedded hatch/provider checks separate from gateway-service checks.
prl-windows-openclaw.sh "$VM" --env "OPENAI_API_KEY=..." agent --local --agent main ...can be green whilegateway installis still red. - Repeat Windows update tests should prefer
--skip-installor rely on the default version-match skip path when you only need to re-check the update leg. - The public Windows installer now bootstraps user-local portable Git under
%LOCALAPPDATA%\OpenClaw\deps\portable-git; this avoids UAC/admin prompts for Git - The portable Git bootstrap is process-local; a fresh guest shell may still say
gitis missing unless the installer or wrapper added the portable paths for that command - For upcoming-build verification, host cache-busted tgz files on the Mac (for example
python3 -m http.server 8138) and pass them withprl-windows-install-openclaw.sh --spec ...orprl-windows-openclaw-update-verify.sh --from-spec ... --update-spec ... - Do not unpack a Windows tgz in the guest and run
openclaw.mjsdirectly as proof; the tarball expects an installed dependency tree. Useprl-windows-install-openclaw.sh --spec ...orprl-windows-overlay-openclaw.sh --spec ...instead - After a reboot,
openclaw.cmdmay not resolve from PATH in a raw guest PowerShell session even though the install is fine; the wrappers already probe%APPDATA%\\npm/%LOCALAPPDATA%\\pnpmfallback paths, so prefer them over ad-hoc commands - For a real OpenAI smoke on native Windows, source
~/.profileon the host and use the embedded path:scripts/prl-windows-openclaw.sh "$VM" --env "OPENAI_API_KEY=$OPENAI_API_KEY" agent --local --agent main --json --thinking low -m "Reply with exactly WINDOWS-HATCH-OK."
- Embedded
agent --localstill needs a target; use--agent mainfor the default session.--modelis not a valid flag here. - Treat native embedded hatch and native gateway service as separate checks:
- embedded hatch can pass with only
OPENAI_API_KEY onboard --non-interactive --accept-risk ...still fails at the final loopback gateway probe if the Windows gateway service is not installed/running
- embedded hatch can pass with only
- Current published native Windows release (
openclaw@2026.3.11) can install and reportopenclaw --version, but deeper CLI paths may still fail on Windows ARM with the released@snazzah/daveybinding load problem - When you need full native Windows verification today, prefer:
- website installer for release smoke
- local tarball/current-checkout install for deeper gateway/plugin/runtime checks
prl-windows-openclaw-update-verify.shreports these published-release blockers instead of pretending the path is greenprl-windows-openclaw-update-verify.shandprl-windows-gateway-status-version.shsupport--help; use that instead of bare execution when checking flags
GUI Automation
Use prlctl capture for the guest screenshot itself.
Use Peekaboo for host-side automation of the Parallels window:
peekaboo see --app Parallels --json
peekaboo click --app Parallels --coords 500,400
peekaboo type --app Parallels "hello"
Important:
prlctl capturepixels are guest-nativepeekaboo click/typetarget the visible host window- coordinate mapping gets fragile if the Parallels window is scaled, moved, fullscreened, or retina-scaled differently
Best practice:
- keep the VM window visible and stable if using Peekaboo
- prefer SSH / Screen Sharing / RDP for serious automation
- use
prlctl send-key-eventonly for limited key injection
Snapshot Safety
- Take a snapshot before risky changes.
- Do not delete or switch snapshots unless asked or clearly part of the requested workflow.
- Call out that snapshot revert discards later guest state.
Decision Rule
- Need lifecycle / state / metadata / screenshots: use
prlctl - Need commands inside guest: use
prlctl execorssh - Need desktop UI control: use guest-native remote control first, Peekaboo second
- Need reproducible visual automation from the host: combine
prlctl capturefor read + Peekaboo for action, but warn about coordinate drift
Peter Notes
peekaboois on PATH on this Mac.~/.codex/skillspoints to~/Projects/agent-scripts/skills, so edits there are live for Codex.