Install
$ agentstack add skill-krzysztofsurdy-code-virtuoso-image-annotate ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Image Annotate
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.
Core Principles
| 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. |
Quick Start
Install (one-time, ask user first)
pip install --user Pillow
Pillow is the imaging library. Cross-platform, pure-Python install. Check first:
python -c "import PIL; print(PIL.__version__)"
Simple one-off: rectangle + arrow + label
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.
Composition via JSON spec
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.
Operations Reference
Rectangle - rect
Bounding 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) |
Arrow - arrow
Line 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) |
Text label - text
Text 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 |
Circle / ellipse - circle
Outline 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 |
Numbered step marker - step
A 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.
Highlight - highlight
Semi-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) |
Blur - blur
Gaussian 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 |
Redact - redact
Solid-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 |
Crop - crop
Cut 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 |
Common Recipes
Bug-report screenshot: box the broken part, label it
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
Tutorial: three numbered steps with descriptions
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"
Pre-share redaction: hide an API key and a session cookie
python scripts/annotate.py dashboard.png dashboard-shareable.png \
--redact 80,420,580,452 \
--redact 80,490,580,522
Privacy-light: blur a face in a marketing-shot
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.)
Highlight a paragraph in a documentation shot
python scripts/annotate.py docs.png docs-highlight.png \
--highlight 60,300,720,420 --highlight-color yellow --highlight-alpha 90
Picking Coordinates
The three reliable ways to find an (x, y) for a mark:
- Browser DevTools elements panel - hover the element; the highlight shows pixel offsets in the page. Multiply by device-pixel-ratio if the screenshot was retina.
- macOS Preview / Linux image-viewer / Windows Paint - open the screenshot, hover the target; most viewers show coordinates in the status bar.
- A first dry run with a colour mark - drop a
--rect 0,0,100,100and 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.
Quick Reference: Checklist
- [ ] Pillow is installed (
python -c "import PIL") - [ ] Input image exists and is readable
- [ ] Output path is different from the input - never overwrite the source
- [ ] Coordinates are within the image bounds (Pillow will silently clip otherwise)
- [ ] Colours are CSS-style names (
red,yellow,#ff8800) - Pillow accepts both - [ ] For sensitive data:
redactnotblur - [ ] Final output viewed at full resolution to confirm marks land where intended
Reference Files
| Reference | Contents | |---|---| | [redaction-safety](references/redaction-safety.md) | What is safe to blur vs requires solid redaction; recoverability research; common mistakes | | [coordinate-cheatsheet](references/coordinate-cheatsheet.md) | Coordinate-picking tactics for typical screenshot dimensions; retina/DPR multipliers; offset patterns for arrows and labels |
Integration with Other Skills
| 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 |
Critical Rules
- Never overwrite the source image. The output path must differ from the input path. Re-runs need the original.
- Use
redactfor secrets, notblur. 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. - Coordinates are top-left origin and pixel-based. Match the resolution of the image, not CSS pixels - if the screenshot is retina (2x), the coordinates are 2x what DevTools shows.
- Operations apply in order. Later marks paint over earlier marks. Put redactions LAST in the spec so nothing in the source leaks through a partially-transparent overlay.
- Output format follows the file extension.
.pngfor lossless,.jpgfor size-constrained. Annotations on a JPEG re-saved as JPEG will accumulate compression artifacts - prefer PNG when iterating.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: krzysztofsurdy
- Source: krzysztofsurdy/code-virtuoso
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.