Skip to content

Commit d280b10

Browse files
committed
Add monochrome icon validation with currentColor requirement
1 parent 0ece3e5 commit d280b10

2 files changed

Lines changed: 70 additions & 3 deletions

File tree

.github/workflows/build_registry.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
# Icon requirements
3535
PREFERRED_ICON_SIZE = 16
36+
ALLOWED_FILL_STROKE_VALUES = {"currentcolor", "none", "inherit"}
3637

3738
# URL validation
3839
SKIP_URL_VALIDATION = os.environ.get("SKIP_URL_VALIDATION", "").lower() in ("1", "true", "yes")
@@ -182,6 +183,48 @@ def validate_distribution_urls(distribution: dict) -> list[str]:
182183
return errors
183184

184185

186+
def validate_icon_monochrome(content: str) -> list[str]:
187+
"""Validate that icon uses currentColor and no hardcoded colors."""
188+
errors = []
189+
reported_colors = set()
190+
191+
# Check fill attributes - must be currentColor or none
192+
fill_matches = re.findall(r'\bfill\s*=\s*["\']([^"\']+)["\']', content, re.IGNORECASE)
193+
for fill_value in fill_matches:
194+
normalized = fill_value.strip().lower()
195+
if normalized not in ALLOWED_FILL_STROKE_VALUES:
196+
errors.append(f"Icon has hardcoded fill=\"{fill_value}\" (use currentColor or none)")
197+
reported_colors.add(fill_value.strip())
198+
199+
# Check stroke attributes - must be currentColor or none
200+
stroke_matches = re.findall(r'\bstroke\s*=\s*["\']([^"\']+)["\']', content, re.IGNORECASE)
201+
for stroke_value in stroke_matches:
202+
normalized = stroke_value.strip().lower()
203+
if normalized not in ALLOWED_FILL_STROKE_VALUES:
204+
errors.append(f"Icon has hardcoded stroke=\"{stroke_value}\" (use currentColor or none)")
205+
reported_colors.add(stroke_value.strip())
206+
207+
# Check for hardcoded colors in style attributes
208+
style_matches = re.findall(r'\bstyle\s*=\s*["\']([^"\']+)["\']', content, re.IGNORECASE)
209+
for style_value in style_matches:
210+
# Check for fill/stroke with hardcoded colors in style
211+
style_fill = re.search(r'\bfill\s*:\s*([^;]+)', style_value, re.IGNORECASE)
212+
if style_fill:
213+
fill_val = style_fill.group(1).strip().lower()
214+
if fill_val not in ALLOWED_FILL_STROKE_VALUES:
215+
errors.append(f"Icon has hardcoded style fill: {style_fill.group(1).strip()}")
216+
reported_colors.add(style_fill.group(1).strip())
217+
style_stroke = re.search(r'\bstroke\s*:\s*([^;]+)', style_value, re.IGNORECASE)
218+
if style_stroke:
219+
stroke_val = style_stroke.group(1).strip().lower()
220+
if stroke_val not in ALLOWED_FILL_STROKE_VALUES:
221+
errors.append(f"Icon has hardcoded style stroke: {style_stroke.group(1).strip()}")
222+
reported_colors.add(style_stroke.group(1).strip())
223+
224+
# Deduplicate errors
225+
return list(dict.fromkeys(errors))
226+
227+
185228
def validate_icon(icon_path: Path) -> list[str]:
186229
"""Validate icon.svg and return list of warnings/errors."""
187230
errors = []
@@ -216,6 +259,10 @@ def validate_icon(icon_path: Path) -> list[str]:
216259
if vb_width != PREFERRED_ICON_SIZE or vb_height != PREFERRED_ICON_SIZE:
217260
errors.append(f"Icon should be {PREFERRED_ICON_SIZE}x{PREFERRED_ICON_SIZE} (got {int(vb_width)}x{int(vb_height)})")
218261

262+
# Validate monochrome (currentColor) usage
263+
monochrome_errors = validate_icon_monochrome(content)
264+
errors.extend(monochrome_errors)
265+
219266
return errors
220267

221268

CLAUDE.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ This is a registry of ACP (Agent Client Protocol) agents. The structure is:
1919
```
2020
<agent-id>/
2121
├── agent.json # Agent metadata and distribution info (required)
22-
└── icon.svg # Agent icon, must be 16x16 (optional)
22+
└── icon.svg # Agent icon: 16x16 SVG, monochrome with currentColor (optional)
2323
```
2424

2525
**Build process** (`.github/workflows/build_registry.py`):
2626
1. Scans directories for `agent.json` files
2727
2. Validates against `agent.schema.json` (JSON Schema)
28-
3. Validates icon dimensions (16x16 SVG required)
28+
3. Validates icons (16x16 SVG, monochrome with `currentColor`)
2929
4. Aggregates all agents into `dist/registry.json`
3030
5. Copies icons to `dist/<agent-id>.svg`
3131

@@ -38,7 +38,7 @@ This is a registry of ACP (Agent Client Protocol) agents. The structure is:
3838
- `id`: lowercase, hyphens only, must match directory name
3939
- `version`: semantic versioning (e.g., `1.0.0`)
4040
- `distribution`: at least one of `binary`, `npx`, `uvx`
41-
- `icon.svg`: must be 16x16 (warnings for non-compliance)
41+
- `icon.svg`: must be SVG format, 16x16, monochrome using `currentColor` (enables theming)
4242
- **URL validation**: All distribution URLs must be accessible (binary archives, npm/PyPI packages)
4343

4444
Set `SKIP_URL_VALIDATION=1` to bypass URL checks during local development.
@@ -48,3 +48,23 @@ Set `SKIP_URL_VALIDATION=1` to bypass URL checks during local development.
4848
- `binary`: Platform-specific archives (`darwin-aarch64`, `linux-x86_64`, etc.)
4949
- `npx`: npm packages
5050
- `uvx`: PyPI packages
51+
52+
## Icon Requirements
53+
54+
Icons must be:
55+
- **SVG format** (only `.svg` files accepted)
56+
- **16x16 dimensions** (via width/height attributes or viewBox)
57+
- **Monochrome using `currentColor`** - all fills and strokes must use `currentColor` or `none`
58+
59+
Using `currentColor` enables icons to adapt to different themes (light/dark mode) automatically.
60+
61+
**Valid example:**
62+
```svg
63+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
64+
<path fill="currentColor" d="M..."/>
65+
</svg>
66+
```
67+
68+
**Invalid patterns:**
69+
- Hardcoded colors: `fill="#FF5500"`, `fill="red"`, `stroke="rgb(0,0,0)"`
70+
- Missing currentColor: `fill` or `stroke` without `currentColor`

0 commit comments

Comments
 (0)