#!/usr/bin/env python3 """ figma-asset-fetch.py — Figma asset 取 + auto-detect 实际格式 + 保存正确扩展名。 避免反模式 (2026-05-13 retrospection Lesson 7): - AI 假设 Figma MCP asset URLs 是 PNG (错的, 实际是 SVG) - AI 没 verify 实际格式就 approximate 重画 / 错走 raster 路径 Usage: python3 scripts/figma-asset-fetch.py [output-dir] 例: # 仅 UUID python3 scripts/figma-asset-fetch.py 858c8478-b20d-4f06-b7cf-890118845106 # → ./858c8478-b20d-4f06-b7cf-890118845106.svg (auto-detected) # 完整 URL + output dir python3 scripts/figma-asset-fetch.py https://www.figma.com/api/mcp/asset/abc... ./assets/ 输出: - 实际格式 (SVG / PNG / JPEG / GIF / WebP / TIFF) - 文件大小 - 正确扩展名命名的本地文件 (e.g. logo.svg / image.png) R8 v2 acceptance criteria 强制: 任何 Figma asset 处理前必须用此 script 取 (不允许 manual curl + 假设格式). """ import sys import subprocess import shutil import tempfile import os import re # Mapping from `file` command output keywords → file extension EXT_MAP = { "SVG": "svg", "PNG": "png", "JPEG": "jpg", "GIF": "gif", "WebP": "webp", "TIFF": "tif", } def detect_extension(file_info: str) -> str: """Map `file` command output to a file extension; fallback 'bin'.""" for keyword, ext in EXT_MAP.items(): if keyword in file_info: return ext return "bin" def fetch(url_or_uuid: str, out_dir: str = ".") -> str: # Normalize input → full URL if url_or_uuid.startswith("http"): url = url_or_uuid else: url = f"https://www.figma.com/api/mcp/asset/{url_or_uuid}" # Download to temp via curl (cross-platform, avoids Python SSL cert issues) with tempfile.NamedTemporaryFile(delete=False) as tmp: tmp_path = tmp.name curl_result = subprocess.run( ["curl", "-sSL", "-o", tmp_path, url], capture_output=True, text=True, ) if curl_result.returncode != 0: raise RuntimeError(f"curl failed: {curl_result.stderr.strip()}") # Detect real format via `file` command (cross-platform, ground truth) result = subprocess.run( ["file", "-b", tmp_path], capture_output=True, text=True, check=True ) file_info = result.stdout.strip() ext = detect_extension(file_info) if ext == "bin": print( f"⚠️ Unknown format: {file_info}\n" f" File saved as .bin (manual inspect needed)", file=sys.stderr, ) # Compose output filename using UUID from URL as base uuid_match = re.search(r"/asset/([a-f0-9-]{8,})", url) name = uuid_match.group(1) if uuid_match else "figma-asset" os.makedirs(out_dir, exist_ok=True) out_path = os.path.join(out_dir, f"{name}.{ext}") shutil.move(tmp_path, out_path) size = os.path.getsize(out_path) print(f"✅ Saved: {out_path}") print(f" Format: {file_info}") print(f" Size: {size:,} bytes") return out_path def main() -> int: if len(sys.argv) < 2: print(__doc__, file=sys.stderr) return 2 url_or_uuid = sys.argv[1] out_dir = sys.argv[2] if len(sys.argv) > 2 else "." try: fetch(url_or_uuid, out_dir) return 0 except Exception as e: print(f"❌ Error: {e}", file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main())