skills/snapshot/SKILL.md
Takes a snapshot of a Figma node — fetches its JSON structure and PNG screenshot and stores both to ~/.figma-differ/ for later diffing. Use when the user runs /figma-differ:snapshot with a Figma URL, or says "snapshot this Figma frame", "save a Figma baseline", or "take a Figma snapshot".
npx skillsauth add tokyo-megacorp/figma-differ snapshotInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Extract fileKey and nodeId from the Figma URL:
https://www.figma.com/design/<fileKey>/...?node-id=<nodeId>nodeId: convert - to : (e.g. 2895-40497 → 2895:40497)Verify the Figma token is loadable (via $FIGMA_TOKEN env var or ~/.figma-differ/.env):
bash scripts/auth.sh status || { echo "ERROR: no Figma token. Run: bash scripts/auth.sh set"; exit 1; }
Get your Figma personal access token at: https://www.figma.com/settings → Personal access tokens. Save it with bash scripts/auth.sh set (prompts, verifies, writes to ~/.figma-differ/.env at mode 600).
~/.figma-differ/)Derive nodeId_safe by replacing : with _ (filesystem-safe): 2895:40497 → 2895_40497.
TIMESTAMP=$(date -u +"%Y%m%dT%H%M%SZ")
NODE_ID_SAFE="${nodeId//:/_}"
FIGMA_DATA="$HOME/.figma-differ"
SNAP_DIR="${FIGMA_DATA}/<fileKey>/${NODE_ID_SAFE}/${TIMESTAMP}"
mkdir -p "$SNAP_DIR"
# One-time migration from legacy storage (safe to re-run — skips if already migrated)
LEGACY_DIR="$CLAUDE_PLUGIN_ROOT/data/snapshots"
if [ -d "$LEGACY_DIR" ] && [ "$(ls -A "$LEGACY_DIR" 2>/dev/null)" ]; then
cp -rn "$LEGACY_DIR"/* "$FIGMA_DATA/" 2>/dev/null || true
echo "Migrated legacy snapshots from data/snapshots/ to ~/.figma-differ/" >&2
rm -rf "$LEGACY_DIR"
fi
bash $CLAUDE_PLUGIN_ROOT/scripts/figma-api.sh fetch_node_json <fileKey> <nodeId> > "$SNAP_DIR/node.json"
If this fails, show the error and stop.
After saving node.json, inspect the node type:
NODE_TYPE=$(jq -r '.nodes["<nodeId>"].document.type // .document.type // empty' "$SNAP_DIR/node.json")
If NODE_TYPE is CANVAS, stop immediately — do NOT save the node.json, do NOT continue to PNG export. Remove the snapshot directory created in step 3. Then:
CANVAS node detected — saving a full Figma page produces 180MB+ snapshots with poor search quality. Redirecting to index instead.
/figma-differ:index <figma-url> to catalog child frames individually.bash $CLAUDE_PLUGIN_ROOT/scripts/figma-api.sh fetch_node_png <fileKey> <nodeId> "$SNAP_DIR/screenshot.png" "${NODE_TYPE:-}"
If this fails, warn the user but continue — JSON snapshot is still useful for structural diffs.
Note: If the node type is CANVAS (a full page), the script now skips the PNG export gracefully and logs: "Skipping PNG export for CANVAS node (pages cannot be exported as images)."
Check if ~/.figma-differ/config.json exists and has a slack_channel_id. If so:
Read ~/.figma-differ/<fileKey>/slack-threads.json (create { "channel_id": "<channel_id>", "threads": {} } if missing)
If the nodeId is NOT already in the threads registry, create a parent message:
Infer a semantic emoji from the frame name:
:lock: — login, sign in, auth:gear: — settings, preferences, config:bust_in_silhouette: — profile, account, user:inbox_tray: — inbox, notifications, messages:moneybag: — net worth, balance, portfolio, finance:gift: — gift, rewards, offers, promotions:page_facing_up: — statements, documents, history:shield: — security, privacy, verification:house: — home, dashboard, overview:mag: — search, explore, discover:credit_card: — payments, cards, transactions:bar_chart: — analytics, reports, charts:iphone: — default/fallback for any screenBuild Figma deep-link: https://www.figma.com/design/<fileKey>/<fileName>?node-id=<nodeId with : replaced by ->
To get the frame name and page name, read the node.json that was just saved and check the index if available, or extract from the Figma API response.
Post via mcp__claude_ai_Slack__slack_send_message:
channel_id: from configmessage: <emoji> *<Frame Name>* · <<figma-deep-link>|Figma> · _<Page Name>_Save the returned message_ts to the registry:
{ "ts": "<message_ts>", "name": "<Frame Name>", "page": "<Page Name>" }
Write the updated slack-threads.json.
If the nodeId is already in threads, skip — parent already exists.
Tell the user:
Snapshot saved to ~/.figma-differ/<fileKey>/<nodeId_safe>/<timestamp>/
node.json — <size> bytes
screenshot.png — <size> bytes (if successful)
Run /figma-differ:diff <same-url> to compare against this snapshot later.
testing
Subscribe to a Figma file for automatic syncing and semantic search. Adds the file to tracked.json, runs initial index + snapshot-all + frame.md generation, and initializes the QMD search collection. Use when the user runs /figma-differ:track or says "track this Figma file", "subscribe to Figma", "watch this design file", or "add to tracked files".
documentation
Refresh snapshots and search index for tracked Figma files. Fetches current state from Figma, generates frame.md documents, and updates the QMD search index. Use when the user runs /figma-differ:sync or says "sync Figma", "refresh snapshots", "update the Figma index", or "re-sync tracked files".
development
Bulk snapshots every frame in a Figma file — fetches all node JSONs, exports PNGs, and stores comments. Uses a single API call for the tree and batched image exports. Use when the user runs /figma-differ:snapshot-all with a Figma file URL, or says "snapshot all frames", "bulk snapshot", "baseline the whole file", or "snapshot everything in this Figma file".
content-media
Semantic search across all tracked Figma frames using QMD hybrid search. Finds frames by content, component names, text strings, or natural language queries like "the login screen" or "frames with portfolio charts". Use when the user runs /figma-differ:search or says "find the Figma frame for", "which frame has", "search Figma frames", or "find frames with".