Loading...
Loading...
Generates, validates, and persists a Figma personal access token to FIGMA_TOKEN. Use this skill whenever a Figma token is needed, missing, expired, or must be refreshed — before any task that calls the Figma API. Triggers on: "generate figma token", "create figma token", "set up figma token", "update figma token", "FIGMA_TOKEN missing", "FIGMA_TOKEN not set", "FIGMA_TOKEN expired", "figma token invalid", "figma authentication", "configure figma access", or any task that requires Figma API access and the token is absent or invalid. Works by checking for an existing valid token first, then auto-login with FIGMA_USERNAME/FIGMA_PASSWORD if available, otherwise falls back to manual login — no manual copy-paste required.
npx skill4agent add clubmediterranee/ai-core figma-generate-personal-tokenFIGMA_TOKEN.envFIGMA_USERNAMEFIGMA_PASSWORD.envSCRIPT_OUTPUT=$(python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py)
EXIT_CODE=$?
echo "$SCRIPT_OUTPUT"
echo "EXIT_CODE=$EXIT_CODE"exit 0exit 3FIGMA_USERNAMEFIGMA_PASSWORDexit 2exit 1FIGMA_USERNAMEFIGMA_PASSWORDFIGMA_AUTO_LOGIN_*$SCRIPT_OUTPUTUSERNAME=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_USERNAME=" | cut -d= -f2-)
PASSWORD=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_PASSWORD=" | cut -d= -f2-)agent-browser open "https://www.figma.com/login" && agent-browser wait --load networkidle && agent-browser snapshot -i@eXagent-browser fill @eX "$USERNAME" && agent-browser fill @eY "$PASSWORD" && agent-browser click @eZagent-browser wait --load networkidle --timeout 30000 && agent-browser get url/loginagent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidle && agent-browser get url/login/loginagent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000ℹ️ A browser session from an earlier step in the same conversation may already be authenticated — always check the URL before waiting.
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidleagent-browser snapshot -i | grep -E "tab.*(Séc|Secur)"
agent-browser click @eREF⚠️ Never useinside a Figma modal — it can close the dialog. ⚠️ Do NOT use JS eval with escaped quotes to find the Security tab — it returnsagent-browser scrollunreliably. Use snapshot refs instead.null
--stdinagent-browser eval --stdin <<'EVALEOF'
(() => {
const btn = Array.from(document.querySelectorAll("button"))
.find(b => b.textContent.includes("Générer un nouveau token") || b.textContent.includes("Create new token") || b.textContent.includes("Generate new token"));
if (btn) { btn.scrollIntoView({ block: "center", behavior: "instant" }); btn.click(); return "clicked: " + btn.textContent.trim(); }
return "not found";
})()
EVALEOF
agent-browser wait 500agent-browser snapshot -i | grep -E "(textbox|combobox)"# 1. Token name — auto-detect project name from cwd
PROJECT_NAME=$(basename $(pwd))
agent-browser fill @eNAME_REF "Claude Code - $PROJECT_NAME"
# 2. Longest expiry — the combobox is CUSTOM (not a <select>), use click + JS
# Click the combobox ref to open it:
agent-browser click @eEXPIRY_REF
# Then click the last [role="option"] via JS (longest duration):
agent-browser eval --stdin <<'EVALEOF'
(() => {
const options = Array.from(document.querySelectorAll('[role="option"]'));
const last = options[options.length - 1];
if (last) { last.click(); return "selected: " + last.textContent.trim(); }
return "no options found";
})()
EVALEOF
# 3. Check ALL permissions in one JS call — always use --stdin
agent-browser eval --stdin <<'EVALEOF'
document.querySelectorAll('input[type="checkbox"]').forEach(cb => { if (!cb.checked) cb.click(); });
document.querySelectorAll('input[type="checkbox"]:checked').length + " checked"
EVALEOF⚠️ Never usefor the expiry field — Figma's expiry combobox is a custom component, not a nativeagent-browser select. It will always fail with "Element is not a <select> element". Always use click + JS<select>.[role="option"]
agent-browser eval --stdin <<'EVALEOF'
(() => {
const btn = Array.from(document.querySelectorAll("button"))
.find(b => b.textContent.trim() === "Générer un token" || b.textContent.trim() === "Generate token");
if (btn && !btn.disabled) { btn.click(); return "clicked"; }
return "not found or disabled: " + Array.from(document.querySelectorAll("button")).map(b => b.textContent.trim()).filter(Boolean).join(", ");
})()
EVALEOF
agent-browser wait 1000pbpasteagent-browser eval --stdin <<'EVALEOF'
(() => {
const btn = Array.from(document.querySelectorAll("button"))
.find(b => b.title?.match(/Copi/i) || b.getAttribute("aria-label")?.match(/Copi/i) || b.textContent?.match(/Copi/i));
if (btn) { btn.click(); return "clicked"; }
return "not found";
})()
EVALEOF
agent-browser wait 300
TOKEN_VALUE=$(pbpaste)
echo "Token retrieved: ${TOKEN_VALUE:0:8}..."pbpasteTOKEN_VALUE=$(agent-browser eval --stdin <<'EVALEOF'
Array.from(document.querySelectorAll("input")).find(i => i.value?.startsWith("figd_"))?.value || ""
EVALEOF
)⚠️ Do not use DOM read as primary — Figma's token display field may use internal React state rather than the DOMproperty, causing the read to return empty even when the token is visible on screen. The clipboard approach is always reliable..value
TOKEN_VALUE--saveif [ -z "$TOKEN_VALUE" ] || [[ "$TOKEN_VALUE" != figd_* ]]; then
echo "❌ Could not retrieve token — check the Figma dialog is still open and the token is visible."
exit 1
fipython3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py --save "$TOKEN_VALUE"agent-browser close.env| File | Usage | Priority |
|---|---|---|
| Vite/Next.js — gitignored by default | 1st (read) |
| Standard — write target | 2nd (read) / 1st (write) |
| Local dev | 3rd |
| Dev only | 4th |
.env.envFIGMA_USERNAME=your@email.com # or FIGMA_EMAIL
FIGMA_PASSWORD=your_passwordFIGMA_TOKEN=figd_xxxxxfigd_W86...zjyM********$SCRIPT_OUTPUT.envFIGMA_USERNAMEFIGMA_PASSWORD.envscrollIntoViewforagent-browser clipboard readpbpastechmod 644 .envFIGMA_USERNAMEFIGMA_PASSWORD<select>agent-browser select[role="option"].valuepbpaste