244 - Awaitable Watch-Mode Screenshots#245
Conversation
- Emit a NERO_SHOT_DONE completion line and a <trigger>.done sentinel on both success and failure so callers can await a capture instead of racing the output-file write - Parse the watch trigger on the last whitespace (path-like tail only) so multi-word labels such as "PIT - DRIVE" capture correctly - Add NERO_SCREENSHOT_DELAY_MS to override the fixed 500ms settle delay - Add scripts/nero-shot.sh (write trigger -> await sentinel -> print path) and a thin .claude/skills/nero-shot skill that wraps it; document in readme - Un-ignore only .claude/skills/nero-shot so the shared skill is tracked
Co-locate the wrapper with the skill that uses it so /nero-shot is self-contained; update the path references in SKILL.md and the readme.
Build the status payload once and reuse it for the log line and the sentinel; write the sentinel with QFile::write instead of a QTextStream and drop the redundant close(). Output is byte-identical. Correct the comment: qInfo goes to stderr, not stdout.
TheJeffreyKuo
left a comment
There was a problem hiding this comment.
Few changes. Also worth a pass for Linux and Windows compatibility in the skill doc.
|
|
||
| # Fresh handshake: drop any stale sentinel, then fire the request. | ||
| rm -f "$done_file" | ||
| printf '%s %s\n' "$page" "$out" >"$trigger" |
There was a problem hiding this comment.
A relative OUT saves relative to the app's cwd, so the printed path doesn't resolve from the caller's directory. Absolutizing it here would also sidestep the path sniffing on the app side.
| const int lastWs = request.lastIndexOf(QRegularExpression("\\s")); | ||
| if (lastWs >= 0) { | ||
| const QString tail = request.mid(lastWs + 1); | ||
| if (tail.contains('/') || tail.endsWith(".png", Qt::CaseInsensitive)) { |
There was a problem hiding this comment.
An out arg with no slash that isn't .png (shot.jpg, or any backslash Windows path) gets folded into the page label and fails as unknown page.
| echo "PIT - DRIVE /tmp/pit.png" > /tmp/nero-shot | ||
|
|
||
| # Each capture reports completion: a `NERO_SHOT_DONE ok "<path>"` line on | ||
| # stdout, plus a `<trigger>.done` sentinel file for callers not reading stdout |
There was a problem hiding this comment.
qInfo goes to stderr, not stdout.
| BIN="$NERO/NERODevelopment/build/macos_development-Debug/NEROApp" # build dir matches your Qt Creator kit | ||
| ``` | ||
|
|
||
| If you changed code, rebuild first: `/build-run build`. No broker is needed — |
There was a problem hiding this comment.
/build-run doesn't appear to be tracked anywhere, other checkouts won't have it.
|
|
||
| ```bash | ||
| NERO=$(git rev-parse --show-toplevel) # run from anywhere inside the repo | ||
| BIN="$NERO/NERODevelopment/build/macos_development-Debug/NEROApp" # build dir matches your Qt Creator kit |
There was a problem hiding this comment.
This default is specific to the macOS kit, won't resolve on Linux checkouts.
| grabAndSave(out, quitAfter); | ||
| }); | ||
| // Settle delay is tunable so bulk captures aren't stuck at 500ms | ||
| const int delayMs = qEnvironmentVariableIntValue("NERO_SCREENSHOT_DELAY_MS"); |
There was a problem hiding this comment.
A non numeric value silently falls back to 500 here, the ok overload would let it warn like setupWatch does.
| # Poll for the sentinel NEROApp writes once the grab (or failure) completes. | ||
| tries=$((timeout * 10)) | ||
| i=0 | ||
| while [ ! -f "$done_file" ]; do |
There was a problem hiding this comment.
The done file exists empty for an instant before the payload lands, -s instead of -f avoids catching it.
…race - nero-shot.sh: absolutize a relative OUT against the caller's cwd, and wait for a non-empty sentinel (-s) so the poll can't catch the .done file in the empty instant between create and write - screenshottool.cpp: treat the trailing token as an output path when it has a slash, backslash, or file extension (not just .png); warn on a set-but-bogus NERO_SCREENSHOT_DELAY_MS instead of silently using 500 - nero-shot skill: auto-discover the binary instead of hardcoding the macOS kit path, drop the untracked /build-run reference, add platforms.md with per-OS paths - readme: qInfo goes to stderr not stdout; note the broadened path rule
- grabAndSave grabs into a QImage and fails (warns + fail sentinel) on a null or entirely-uniform image instead of saving a blank PNG, so a headless render failure is loud rather than a silent success - platforms.md: QT_QUICK_BACKEND=software remedy for headless Linux, and sharper Windows guidance (WSL is the clean path; native needs explicit trigger/output paths) - SKILL.md: note the blank-capture failure and the software-backend fallback
Changes
Makes the headless screenshot harness (#242) scriptable for agent-driven development. Watch-mode captures now emit a completion signal — a
NERO_SHOT_DONE ok|fail "<path>"line in the app's log plus a<trigger>.donesentinel — on both success and failure, so a caller can await a capture instead of racing the output-file write. Also fixes multi-word page labels in watch mode, adds a tunable settle delay, and ships a thin/nero-shotskill that bundles a nero-shot.sh wrapper for driving the trigger file.Notes
.claude/is gitignored repo-wide, so.gitignoreis adjusted to un-ignore only.claude/skills/nero-shot/; every other local skill stays untracked.NERO_SCREENSHOT*env vars; production paths are unchanged.Test Cases
Dogfooded with the skill's nero-shot.sh wrapper against a running offscreen watch-mode build:
HOME→ saved (single-word label)PIT - DRIVE→ saved (multi-word label; previously failed asunknown page "PIT")OFFwith no OUT arg → saved to the default<trigger dir>/off.pngBOGUS(unknown page) → fails fast (rc=1) with afailsentinel, no hangNERO_SCREENSHOT_DELAY_MS=300to exercise the tunable delayNERO_SHOT_DONEline and<trigger>.donecontents for eachReview fixes re-dogfooded: multi-word labels still capture; non-.png and backslash output paths now split off correctly (previously unknown-page); a relative wrapper OUT resolves to an absolute path; a bogus delay value warns instead of silently defaulting to 500; and the blank-grab guard was verified not to misfire on real screens.
Checklist
Closes #244