Help us improve
Share bugs, ideas, or general feedback.
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.
npx claudepluginhub krzysztofsurdy/code-virtuoso --plugin agents-virtuosoHow 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.
Generates 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 using Codex CLI image generation from Korean/English text prompts. Supports portrait, landscape, illustration, object, poster, logo, and banner modes with text rendering.
Generates and edits images using Gemini CLI image models for blog featured images, YouTube thumbnails, icons, diagrams, patterns, illustrations, and visuals. Activates on create, generate, make, draw, design, or edit image requests.
Share bugs, ideas, or general feedback.
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.