---
name: FolderFi
description: "Customize macOS folder icons — graffiti text mode OR fully custom AI-generated images via flickyluv. Use this skill whenever the user wants to change a folder icon, customize a folder's appearance, make a custom folder icon, style a folder, add art to a folder, or make their folders look unique. Also trigger on 'folder icon', 'custom icon', 'FolderFi', 'graffiti folder', or when someone wants their desktop/Finder folders to have a custom look. Covers both quick text-on-folder icons AND fully AI-generated custom artwork icons."
---


# FolderFi

Customize macOS icons — folders, drives, and apps.

## ABSOLUTE RULES — NEVER BREAK THESE

1. **FOLDER ICONS MUST ALWAYS USE THE FOLDER SHAPE.** Never apply a flat square image to a folder. Always extract the macOS folder shape mask and composite the design onto it. No exceptions.
2. **Always use the proven 3-step ImageMagick pipeline** for folders:
   ```bash
   # 1. Get folder shape mask
   sips -s format png "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericFolderIcon.icns" --out /tmp/folder_base.png --resampleWidth 512
   magick /tmp/folder_base.png -alpha extract /tmp/folder_mask.png
   # 2. Resize texture to 512x512
   magick /path/to/texture_or_design.png -resize 512x512! /tmp/tex_512.png
   # 3. Apply folder mask — this gives it the folder shape
   magick /tmp/tex_512.png /tmp/folder_mask.png -alpha off -compose CopyOpacity -composite /tmp/final_icon.png
   ```
3. **Apply icons using NSWorkspace** — this is the method that works:
   ```applescript
   use framework "AppKit"
   set iconImage to current application's NSImage's alloc()'s initWithContentsOfFile:"<path-to-png>"
   current application's NSWorkspace's sharedWorkspace()'s setIcon:iconImage forFile:"<target-path>" options:0
   ```
   Run via `osascript` tool (Control your Mac MCP).
4. **NEVER `killall Finder`** — resets all custom icons.
5. **For drives/volumes**, extract the original system icon shape as the mask (not the folder shape).
6. **For apps**, swap the internal .icns + move Assets.car + re-sign + flush caches.

## Modes

1. **Graffiti Mode** — text overlay on folder shape (canvas-rendered)
2. **Custom Canvas Art** — symbols/drawings on folder shape
3. **AI Texture** — flickyluv generates textures, composited onto folder shape via ImageMagick
4. **System Icon Reskin** — drives/volumes
5. **App Icon Reskin** — internal .icns swap

Infer mode from context. "draw a skull" = canvas art. "leopard print" = AI texture. "put text on it" = graffiti.

## Inputs

- **Folder path** — which folder to customize
- **Mode** — graffiti (text) or custom (AI image)

For **Graffiti Mode**:
- **Text** (optional) — defaults to folder name
- **Color** (optional) — hex color, defaults to `#00cc33`

For **Custom Image Mode**:
- **Prompt** — what to generate (e.g. "dark angel wings logo", "neon skull", "vintage cassette tape")
- **Reference image** (optional) — an existing image to edit/restyle

---

## Mode 1: Graffiti Text

### Step 1: Generate icon via HTML canvas

Write `/tmp/folderfi_icon.html` with a 512x512 canvas:

```html
<!DOCTYPE html>
<html>
<head><style>body{margin:0;background:transparent;display:flex;justify-content:center;align-items:center;height:100vh}canvas{display:block}</style></head>
<body>
<canvas id="c" width="512" height="512"></canvas>
<script>
const c = document.getElementById('c');
const ctx = c.getContext('2d');
const TEXT = '{{TEXT}}';
const COLOR = '{{COLOR}}';

// Folder tab
ctx.beginPath();
ctx.moveTo(40, 160);
ctx.lineTo(40, 125);
ctx.quadraticCurveTo(40, 100, 65, 95);
ctx.lineTo(180, 95);
ctx.quadraticCurveTo(200, 95, 210, 115);
ctx.lineTo(220, 155);
ctx.closePath();
ctx.fillStyle = '#a0a0a0';
ctx.fill();

// Folder body back
ctx.beginPath();
ctx.roundRect(30, 150, 452, 300, 16);
ctx.fillStyle = '#969696';
ctx.fill();

// Folder body front
ctx.beginPath();
ctx.roundRect(30, 180, 452, 270, 14);
ctx.fillStyle = '#b0b0b0';
ctx.fill();

// Grain texture
const rand = (s) => { let x = Math.sin(s) * 10000; return x - Math.floor(x); };
for (let i = 0; i < 4000; i++) {
  const x = 30 + rand(i * 7) * 452;
  const y = 180 + rand(i * 13) * 270;
  const v = Math.floor(rand(i * 3) * 30 - 15);
  ctx.fillStyle = `rgba(${176+v},${176+v},${176+v},0.3)`;
  ctx.fillRect(x, y, 1.5, 1.5);
}

// Text
ctx.save();
ctx.translate(256, 320);
ctx.rotate(-0.12);
let fontSize = 88;
if (TEXT.length > 6) fontSize = Math.max(40, Math.floor(88 * 6 / TEXT.length));
ctx.font = `bold italic ${fontSize}px "Marker Felt", "Comic Sans MS", Impact, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

const r = parseInt(COLOR.slice(1,3),16);
const g = parseInt(COLOR.slice(3,5),16);
const b = parseInt(COLOR.slice(5,7),16);
ctx.fillStyle = `rgba(${Math.floor(r*0.3)},${Math.floor(g*0.3)},${Math.floor(b*0.3)},0.5)`;
ctx.fillText(TEXT, 4, 4);
ctx.fillStyle = COLOR;
ctx.fillText(TEXT, 0, 0);
ctx.strokeStyle = `rgba(${Math.floor(r*0.5)},${Math.floor(g*0.5)},${Math.floor(b*0.5)},0.7)`;
ctx.lineWidth = 1.5;
ctx.strokeText(TEXT, 0, 0);
ctx.restore();

// Halo
ctx.save();
ctx.translate(256, 245);
ctx.rotate(-0.12);
ctx.strokeStyle = COLOR;
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.ellipse(0, 0, 50, 14, 0, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
</script>
</body>
</html>
```

Replace `{{TEXT}}` and `{{COLOR}}`.

### Step 2: Render and export

1. Serve with `python3 -m http.server <port> --directory /tmp &>/dev/null &`
2. Open in Chrome via MCP tools, wait for render
3. Execute JS to download canvas as PNG:
   ```javascript
   const canvas = document.getElementById('c');
   const link = document.createElement('a');
   link.download = 'folderfi_icon.png';
   link.href = canvas.toDataURL('image/png');
   link.click();
   ```
4. Find PNG in `~/Downloads/folderfi_icon.png`

### Step 3: Apply icon (same for both modes)

See "Applying the Icon" below.

---

## Mode 2: Custom Canvas Art

For custom artwork (pentagram, skull, symbols, patterns, etc.) on a folder shape, write a custom HTML canvas that:

1. **Always draws the folder shape first** — the icon MUST look like a folder, not a flat square image. Use the folder tab + body structure from Mode 1 but with custom colors (e.g., black folder: `#1a1a1a` body, `#111111` back).
2. **Then draws the custom art on the folder face** — pentagram, symbol, logo, whatever the user wants. Center it on the folder body area (roughly cx=256, cy=315).
3. Add glow/shadow effects as needed for the aesthetic.

The folder shape template for dark folders:
```javascript
// Folder tab
ctx.beginPath();
ctx.moveTo(40, 160); ctx.lineTo(40, 125);
ctx.quadraticCurveTo(40, 100, 65, 95); ctx.lineTo(180, 95);
ctx.quadraticCurveTo(200, 95, 210, 115); ctx.lineTo(220, 155);
ctx.closePath();
ctx.fillStyle = '#1a1a1a'; ctx.fill();

// Folder body back
ctx.beginPath(); ctx.roundRect(30, 150, 452, 300, 16);
ctx.fillStyle = '#111111'; ctx.fill();

// Folder body front
ctx.beginPath(); ctx.roundRect(30, 180, 452, 270, 14);
ctx.fillStyle = '#1a1a1a'; ctx.fill();
```

Adjust the colors (`#1a1a1a` → any color) to match the user's request. Then add the custom artwork on top.

Follow the same render/export/apply flow as Mode 1.

## Mode 3: AI-Generated Image (flickyluv)

Use flickyluv's `generate.py` to create a fully AI-generated icon. **Important**: flickyluv requires Python 3.10+ (uses `X | None` union syntax). Check `python3 --version` first; if < 3.10, fall back to Mode 2 (canvas art).

```bash
python3 ~/.claude/skills/flickyluv/generate.py "PROMPT — square icon, centered, clean background, suitable as a macOS folder icon" \
  --aspect 1:1 --size 2K --out /tmp --no-open
```

**Critical**: If using a flat AI-generated image as a folder icon, it will look like a photo thumbnail, not a folder. To avoid this, either:
- Ask flickyluv to generate an image that includes a folder shape in the prompt
- Or use the AI image as a texture/overlay on a canvas-drawn folder shape (combine Mode 2 + Mode 3)

---

## Rendering: Prefer ImageMagick over Chrome

**Use ImageMagick (`magick`) for compositing** — it's faster and more reliable than the Chrome canvas export pipeline (which often gets blocked by Chrome's download security).

Example: composite a texture onto a folder shape:
```bash
# Extract alpha mask from base icon
magick /path/to/base_icon.png -alpha extract /tmp/mask.png

# Resize texture to 512x512
magick /path/to/texture.png -resize 512x512! /tmp/texture_512.png

# Apply mask to texture (clips texture to icon shape)
magick /tmp/texture_512.png /tmp/mask.png -alpha off -compose CopyOpacity -composite /tmp/final_icon.png
```

For canvas-based generation (graffiti mode, custom drawings), still use the Chrome pipeline:
1. Write HTML to `/tmp/folderfi_icon.html`
2. Serve with `python3 -m http.server <port> --directory /tmp &>/dev/null &`
3. Open in Chrome, screenshot to verify
4. Export using blob download (may get blocked after repeated downloads from same page — if so, fall back to ImageMagick)

## Applying the Icon (folders, drives, files)

**Use NSWorkspace setIcon via osascript.** This is the proven method:

```applescript
use framework "AppKit"

set iconPath to "<path-to-png>"
set folderPath to "<target-path>"

set iconImage to current application's NSImage's alloc()'s initWithContentsOfFile:iconPath
current application's NSWorkspace's sharedWorkspace()'s setIcon:iconImage forFile:folderPath options:0

return "done"
```

Run this via the `osascript` tool (Control your Mac MCP). It works for folders, files, and volumes.

**NEVER `killall Finder`** — resets ALL custom icons.
**NEVER use a flat square image** — always mask to the item's shape first.

## Mode 4: Reskinning System Icons (drives, volumes, etc.)

For reskinning existing macOS system icons (hard drives, removable disks, etc.) with a custom pattern/texture:

1. Extract the original system icon as a base PNG:
   ```bash
   sips -s format png "/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns" --out /tmp/base_icon.png --resampleWidth 512
   ```
   Common icon locations:
   - Removable disk: `/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns`
   - External disk: `/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/External.icns`
   - Internal disk: `/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/SidebarInternalDisk.icns`

2. In the HTML canvas, load the base icon, create the pattern on an offscreen canvas, then composite:
   - Draw the base icon first
   - Use `globalCompositeOperation = 'source-in'` to clip the pattern to the icon's shape
   - Use `globalCompositeOperation = 'multiply'` to overlay the original for depth
   - Use `globalCompositeOperation = 'source-over'` at low opacity (~0.2) to bring back arrow/detail definition

3. Apply to the volume: `set drivePath to "/Volumes/<volume-name>"`

## Mode 5: Reskinning App Icons

Replace an app's internal icon so it shows everywhere — Dock, Launchpad, Finder, Spotlight.

### Prerequisites
- Terminal needs **Full Disk Access** AND **App Management** in System Settings > Privacy & Security
- Only works on third-party apps in `/Applications/` — system apps are SIP-protected

### Steps

1. **Find the icon file name** from Info.plist:
   ```bash
   plutil -p "/Applications/MyApp.app/Contents/Info.plist" | grep CFBundleIconFile
   ```

2. **Back up the original icon**:
   ```bash
   cp "/Applications/MyApp.app/Contents/Resources/<icon>.icns" /tmp/app_icon_backup.icns
   ```

3. **Extract the icon shape as a mask** for compositing:
   ```bash
   sips -s format png /tmp/app_icon_backup.icns --out /tmp/app_base.png --resampleWidth 512
   magick /tmp/app_base.png -alpha extract /tmp/app_mask.png
   ```

4. **Generate or prepare the texture** (use flickyluv for best results):
   ```bash
   export OPENROUTER_API_KEY="..."
   python3 ~/.claude/skills/flickyluv/generate.py "TEXTURE PROMPT, large bold pattern, high contrast, zoomed in, fills entire image" --aspect 1:1 --size 2K --out /tmp --no-open
   ```
   **Important**: At icon size, patterns need to be LARGE and HIGH CONTRAST or they'll look like a solid color blob.

5. **Composite texture onto app icon shape** with ImageMagick:
   ```bash
   magick /tmp/texture.png -resize 512x512! /tmp/tex_512.png
   magick /tmp/tex_512.png /tmp/app_mask.png -alpha off -compose CopyOpacity -composite /tmp/app_skinned.png
   ```

6. **Convert to .icns**:
   ```bash
   mkdir -p /tmp/app.iconset
   magick /tmp/app_skinned.png -resize 512x512 /tmp/app.iconset/icon_512x512.png
   magick /tmp/app_skinned.png -resize 512x512 /tmp/app.iconset/icon_256x256@2x.png
   magick /tmp/app_skinned.png -resize 256x256 /tmp/app.iconset/icon_256x256.png
   magick /tmp/app_skinned.png -resize 256x256 /tmp/app.iconset/icon_128x128@2x.png
   magick /tmp/app_skinned.png -resize 128x128 /tmp/app.iconset/icon_128x128.png
   magick /tmp/app_skinned.png -resize 64x64 /tmp/app.iconset/icon_32x32@2x.png
   magick /tmp/app_skinned.png -resize 32x32 /tmp/app.iconset/icon_32x32.png
   magick /tmp/app_skinned.png -resize 32x32 /tmp/app.iconset/icon_16x16@2x.png
   magick /tmp/app_skinned.png -resize 16x16 /tmp/app.iconset/icon_16x16.png
   iconutil -c icns /tmp/app.iconset -o /tmp/app_new.icns
   ```

7. **Replace the icon, strip attrs, re-sign, flush caches** — run each as separate `do shell script ... with administrator privileges` calls via osascript:
   ```bash
   # Copy icon
   cp /tmp/app_new.icns "/Applications/MyApp.app/Contents/Resources/<icon>.icns"
   # If app has Assets.car (asset catalog), move it out — macOS reads icons from .car over .icns
   mv "/Applications/MyApp.app/Contents/Resources/Assets.car" /tmp/app_assets_backup.car
   # Strip extended attrs
   xattr -cr "/Applications/MyApp.app"
   # Re-sign (ad-hoc) so macOS allows it to launch
   codesign --force --deep --sign - "/Applications/MyApp.app"
   # Re-register with Launch Services
   /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -f "/Applications/MyApp.app"
   # Flush icon caches
   rm -rf /Library/Caches/com.apple.iconservices.store
   find /private/var/folders/ -name com.apple.iconservices -exec rm -rf {} +
   killall Dock
   ```

8. **Quit and reopen the app** to see the new icon. If still cached, a reboot will flush it.

### Key gotchas
- **Assets.car overrides app.icns** — many modern apps (Chrome, Slack, etc.) have an asset catalog. Move it out or the .icns swap won't show.
- **Must re-sign after modifying** or macOS will refuse to open the app ("can't be opened").
- **App updates will overwrite** your custom icon — you'll need to re-apply.
- **Patterns must be large/bold** — subtle textures disappear at icon size.

## Clean Up

- Remove `/tmp/folderfi_icon.html` and temp files
- Kill any temp HTTP server processes
- Keep backups in `/tmp/` (original .icns and Assets.car) in case you need to restore

## Important Lessons — READ EVERY TIME

- **NEVER use a flat square image as a folder icon.** Always extract the macOS folder shape mask and composite your design onto it using ImageMagick. A square image will show as a photo thumbnail, not a folder.
- **The proven pipeline is: sips → magick alpha extract → magick resize → magick CopyOpacity composite → NSWorkspace setIcon.** This is the ONLY pipeline that reliably produces correct folder-shaped icons. Use it every single time.
- **Apply with NSWorkspace setIcon via osascript** (Control your Mac MCP). This is the method that works for folders/drives/files.
- **NEVER `killall Finder`** — resets all custom icons.
- **NEVER use `fileicon`** — silently fails (0 byte Icon file).
- **NEVER use clipboard paste via Get Info** as primary method — it's unreliable and sometimes doesn't overwrite the old icon.
- **Use ImageMagick (`magick`) for ALL compositing** — faster and more reliable than Chrome canvas.
- **Use flickyluv for textures** — canvas-drawn patterns look amateur at icon size. AI-generated textures look real. Use large bold high-contrast patterns. Needs `OPENROUTER_API_KEY` env var and Python 3.10+.
- **For drives/volumes**, extract the ORIGINAL system icon shape (not the folder shape) as the mask.
- **For apps**: move Assets.car out (overrides .icns), swap .icns, xattr -cr, codesign --force --deep --sign -, lsregister -f, killall Dock. Use `do shell script ... with administrator privileges` for the GUI password prompt.
- Icons persist across restarts. To revert: Get Info → click icon → Delete key.
