From playbooks-virtuoso
Annotates images with shapes, arrows, text, numbered steps, blur, and redaction. Useful for marking up screenshots for tutorials, highlighting regions, or redacting sensitive information.
How this skill is triggered — by the user, by Claude, or both
Slash command
/playbooks-virtuoso:image-annotate <input-image> <output-image> [--rect ...] [--arrow ...] [--text ...] [--step ...] [--blur ...] [--redact ...] [--spec FILE.json]<input-image> <output-image> [--rect ...] [--arrow ...] [--text ...] [--step ...] [--blur ...] [--redact ...] [--spec FILE.json]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Mark up an existing image with shapes, text, numbered steps, blur, or solid-color redaction. Single CLI, one-off flags for simple marks, JSON spec file for compositions. Output is a flat raster image with every mark burned in - the original is never modified in place.
Mark up an existing image with shapes, text, numbered steps, blur, or solid-color redaction. Single CLI, one-off flags for simple marks, JSON spec file for compositions. Output is a flat raster image with every mark burned in - the original is never modified in place.
| Principle | Meaning |
|---|---|
| Never modify in place | Always write to a new output file. Annotation is destructive - the user must keep the original to redo or refine later. |
| Blur is not redaction | Pixelation and blur can be reversed for text content. For passwords, API keys, SSNs, payment numbers, and other security-critical strings, use solid-color redaction. Blur is acceptable only for casual privacy (faces in marketing shots, peripheral background detail). See references/redaction-safety.md. |
| Marks should read on any background | Use stroke + fill colors that contrast with the surrounding pixels, or add a contrasting outline to text. Yellow on a yellow page is invisible. The default style stacks a thick coloured stroke over the underlying pixels, never relies on transparency alone. |
| Coordinate origin is top-left | All (x, y) coordinates start from the top-left corner of the image. Y increases downward. Match the convention to whatever the source tool reports - browser DevTools and most screenshot tools agree on this. |
| One operation, one purpose | A single annotation does one thing. To layer marks, repeat flags or use a JSON spec - do not try to overload one operation. |
| Burn into a flat image | The output is a single-layer PNG or JPEG. No editable layers, no SVG re-edit path. If the user needs to iterate, they re-run the script with a new spec. |
pip install --user Pillow
Pillow is the imaging library. Cross-platform, pure-Python install. Check first:
python -c "import PIL; print(PIL.__version__)"
python scripts/annotate.py input.png output.png \
--rect 100,100,500,300 --rect-color red --rect-width 5 \
--arrow 600,250,520,200 --arrow-color yellow \
--text 600,260 "Click this button" --text-color white --text-bg black
That produces output.png with a red box around the region, a yellow arrow pointing into it, and a labelled callout next to the arrow.
For three or more marks, prefer a spec file:
[
{"op": "rect", "xy": [100, 100, 500, 300], "color": "red", "width": 5},
{"op": "arrow", "from": [600, 250], "to": [520, 200], "color": "yellow"},
{"op": "text", "xy": [600, 260], "value": "Click this button", "color": "white", "bg": "black"},
{"op": "step", "n": 1, "xy": [150, 150]},
{"op": "step", "n": 2, "xy": [350, 250]},
{"op": "redact", "xy": [50, 600, 400, 640]}
]
python scripts/annotate.py input.png output.png --spec marks.json
Operations apply in order - later marks overlay earlier ones.
rectBounding box around an area. Outline only by default (no fill).
| CLI flag | JSON key | Default |
|---|---|---|
--rect x1,y1,x2,y2 | {"op": "rect", "xy": [...]} | required |
--rect-color NAME | "color": "red" | red |
--rect-width N | "width": 5 | 4 |
--rect-fill NAME | "fill": null | none (outline only) |
arrowLine with a triangular arrowhead at the end point. Points from (x1,y1) to (x2,y2) - the head is at (x2,y2).
| CLI flag | JSON key | Default |
|---|---|---|
--arrow x1,y1,x2,y2 | {"op": "arrow", "from": [...], "to": [...]} | required |
--arrow-color NAME | "color": "yellow" | yellow |
--arrow-width N | "width": 6 | 6 |
--arrow-head N | "head": 20 | 20 (pixels, half-width of the head triangle) |
textText with an optional filled background box for legibility on any underlying image. The position is the top-left of the text.
| CLI flag | JSON key | Default |
|---|---|---|
--text x,y "STRING" | {"op": "text", "xy": [...], "value": "..."} | required |
--text-color NAME | "color": "white" | white |
--text-bg NAME | "bg": "black" | black (set null for no background box) |
--text-size N | "size": 24 | 24 |
--text-padding N | "padding": 8 | 8 |
circleOutline around a centre point. Useful for highlighting a single UI element.
| CLI flag | JSON key | Default |
|---|---|---|
--circle cx,cy,r | {"op": "circle", "center": [...], "radius": N} | required |
--circle-color NAME | "color": "red" | red |
--circle-width N | "width": 4 | 4 |
stepA filled coloured circle with a number inside, drawn in the order the user reads the image. Use for sequential tutorials ("first click here, then here, then here").
| CLI flag | JSON key | Default |
|---|---|---|
--step N x,y | {"op": "step", "n": N, "xy": [...]} | required |
--step-color NAME | "color": "red" | red |
--step-radius N | "radius": 22 | 22 |
--step-text-color NAME | "text_color": "white" | white |
Stack multiple --step flags or list multiple step ops to enumerate a workflow.
highlightSemi-transparent coloured overlay over a rectangular region. Use to call attention to an area without obscuring its content. Defaults to a translucent yellow, like a marker pen.
| CLI flag | JSON key | Default |
|---|---|---|
--highlight x1,y1,x2,y2 | {"op": "highlight", "xy": [...]} | required |
--highlight-color NAME | "color": "yellow" | yellow |
--highlight-alpha N | "alpha": 80 | 80 (0-255, lower is more transparent) |
blurGaussian blur over a rectangular region. Not safe for security-critical content. Use only for casual privacy (faces, peripheral background detail, non-secret personal info).
| CLI flag | JSON key | Default |
|---|---|---|
--blur x1,y1,x2,y2 | {"op": "blur", "xy": [...]} | required |
--blur-radius N | "radius": 16 | 16 |
redactSolid-color block over a rectangular region. Use this - not blur - for passwords, API keys, tokens, SSNs, payment numbers, or anything an attacker would reuse.
| CLI flag | JSON key | Default |
|---|---|---|
--redact x1,y1,x2,y2 | {"op": "redact", "xy": [...]} | required |
--redact-color NAME | "color": "black" | black |
cropCut the image to a rectangular region. Applied first if present in the spec.
| CLI flag | JSON key | Default |
|---|---|---|
--crop x1,y1,x2,y2 | {"op": "crop", "xy": [...]} | required |
python scripts/annotate.py bug.png bug-annotated.png \
--rect 320,180,640,260 --rect-color red --rect-width 6 \
--text 320,140 "Button stays disabled after valid input" --text-bg red
python scripts/annotate.py tutorial.png tutorial-annotated.png \
--step 1 180,210 \
--step 2 420,280 \
--step 3 660,210 \
--text 220,210 "Open the file menu" \
--text 460,280 "Pick Export -> PDF" \
--text 700,210 "Confirm the location"
python scripts/annotate.py dashboard.png dashboard-shareable.png \
--redact 80,420,580,452 \
--redact 80,490,580,522
python scripts/annotate.py team.png team-anon.png \
--blur 120,80,260,240 --blur-radius 24
(Use redact instead if the face must be unrecognisable in any forensic recovery.)
python scripts/annotate.py docs.png docs-highlight.png \
--highlight 60,300,720,420 --highlight-color yellow --highlight-alpha 90
The three reliable ways to find an (x, y) for a mark:
--rect 0,0,100,100 and binary-search outward until the box lands where you want. Wasteful but always works.The coordinate origin is the top-left corner: x increases right, y increases down. Pillow, browsers, and OS screen-capture tools all agree on this.
See references/coordinate-cheatsheet.md for common picks and offsets.
python -c "import PIL")red, yellow, #ff8800) - Pillow accepts bothredact not blur| Reference | Contents |
|---|---|
| redaction-safety | What is safe to blur vs requires solid redaction; recoverability research; common mistakes |
| coordinate-cheatsheet | Coordinate-picking tactics for typical screenshot dimensions; retina/DPR multipliers; offset patterns for arrows and labels |
| Situation | Recommended Skill |
|---|---|
| Need to capture the source image first | web-screenshot - capture, copy to host, then run this skill on the host file |
| Generating a step-by-step tutorial | This skill's step operation plus the web-screenshot multi-viewport script for paired shots |
| Producing a PR or status report with annotated screenshots | report-writer - embed the annotated PNGs into the HTML report |
redact for secrets, not blur. Blur is reversible for text content. Passwords, API keys, payment numbers, SSNs, JWT tokens, and anything an attacker could replay must be covered with a solid block..png for lossless, .jpg for size-constrained. Annotations on a JPEG re-saved as JPEG will accumulate compression artifacts - prefer PNG when iterating.npx claudepluginhub krzysztofsurdy/code-virtuoso --plugin agents-virtuosoGenerates and edits images via a unified CLI supporting OpenAI gpt-image-2 and Codex, with masks, transparent backgrounds, and up to 4K sizes.
Generates images via Codex CLI /imagen using gpt-image-2 backend. Handles Korean/English text and complex layouts for thumbnails, logos, posters, and illustrations.
Generates and edits images using Google's Gemini Nano Banana Pro model (gemini-3-pro-image-preview). Requires GEMINI_API_KEY. Supports prompt-based image creation and editing user-provided images.