figma-generate-personal-token
Original:🇺🇸 English
Translated
1 scripts
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.
6installs
Sourceclubmediterranee/ai-core
Added on
NPX Install
npx skill4agent add clubmediterranee/ai-core figma-generate-personal-tokenTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →figma-generate-personal-token
Manages the complete lifecycle of : detection, validation, automated generation via browser, and persistence to the project .
FIGMA_TOKEN.envSupports auto-login if and are available in the environment or files.
FIGMA_USERNAMEFIGMA_PASSWORD.envWorkflow (follow this exact order)
Step 1 — Run the main script
bash
SCRIPT_OUTPUT=$(python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py)
EXIT_CODE=$?
echo "$SCRIPT_OUTPUT"
echo "EXIT_CODE=$EXIT_CODE"The script returns:
- → token valid, nothing to do
exit 0 - →
exit 3/FIGMA_USERNAMEfound → auto-login (see Step 2)FIGMA_PASSWORD - → no token/expired or no credentials → manual login (see Step 3)
exit 2 - → unexpected error
exit 1
Step 2 — Auto-login with credentials (exit 3)
The script found and . Its stdout contains two lines with the values
to use — note: is just the script's output format, not the name of the source
env vars. Extract them from :
FIGMA_USERNAMEFIGMA_PASSWORDFIGMA_AUTO_LOGIN_*$SCRIPT_OUTPUTbash
USERNAME=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_USERNAME=" | cut -d= -f2-)
PASSWORD=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_PASSWORD=" | cut -d= -f2-)Open the browser, fill the form and submit in one chain:
bash
agent-browser open "https://www.figma.com/login" && agent-browser wait --load networkidle && agent-browser snapshot -iFill the form (refs come from the snapshot):
@eXbash
agent-browser fill @eX "$USERNAME" && agent-browser fill @eY "$PASSWORD" && agent-browser click @eZWait for post-login redirect:
bash
agent-browser wait --load networkidle --timeout 30000 && agent-browser get urlIf the URL still contains , Figma may require 2FA — wait for the user to complete the flow:
/loginbash
agent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000Then continue to Step 4.
Step 3 — Manual login (exit 2)
No credentials available. Navigate to settings and immediately check the resulting URL:
bash
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidle && agent-browser get url- If the URL does not contain → already logged in, skip to Step 4 immediately
/login - If the URL contains → inform the user, then wait up to 2 minutes:
/login
bash
agent-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.
Then continue to Step 4.
Step 4 — Open Settings and click the Security tab
After login, open settings. Figma redirects to the files page but opens the settings dialog automatically:
bash
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidleTake ONE snapshot, grep the Security tab ref, then click it directly:
bash
agent-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
Step 5 — Create the token
Click "Generate new token" via JS with (avoids all shell escaping issues):
--stdinbash
agent-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 500ONE snapshot to get the name input and expiry combobox refs:
bash
agent-browser snapshot -i | grep -E "(textbox|combobox)"Fill the form in a single sequence:
bash
# 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"]
Click Generate via JS directly — no snapshot needed:
bash
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 1000Step 6 — Retrieve and persist the token
Click the Copy button and read via (primary method — most reliable):
pbpastebash
agent-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}..."If returns empty or not a Figma token, try reading from the DOM as a last resort:
pbpastebash
TOKEN_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
If is still empty after both attempts, stop and report the error — do not call :
TOKEN_VALUE--savebash
if [ -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
fiThen save:
bash
python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py --save "$TOKEN_VALUE"Step 7 — Close the browser
Always close the browser session after the token has been saved:
bash
agent-browser closeSupported .env
files (priority order)
.envThe script searches and updates in this order:
| File | Usage | Priority |
|---|---|---|
| Vite/Next.js — gitignored by default | 1st (read) |
| Standard — write target | 2nd (read) / 1st (write) |
| Local dev | 3rd |
| Dev only | 4th |
The token is always written to . If is not gitignored, a warning is shown.
.env.envVariables read (credentials)
dotenv
FIGMA_USERNAME=your@email.com # or FIGMA_EMAIL
FIGMA_PASSWORD=your_passwordVariables written (token)
dotenv
FIGMA_TOKEN=figd_xxxxxSecurity
- Token is never logged in plaintext (masked: )
figd_W86...zjyM - Password is masked in human-readable output (); the structured exit-3 output prints it in plaintext so the agent can extract it — do not persist
********$SCRIPT_OUTPUT - If is not gitignored, the script shows a warning
.env - Never commit or
FIGMA_USERNAMEin a non-gitignoredFIGMA_PASSWORDfile.env
Troubleshooting
"The generation dialog closes unexpectedly"
→ Always use JS to click the "Generate new token" button — never use a snapshot ref click. The JS approach calls first to ensure the element is in view before clicking.
scrollIntoView"Checkboxes fail partway through"
→ Do not use a shell loop over refs. Use exclusively the JS command in Step 5 which checks everything in a single evaluation.
for" returns empty"
→ Use directly on macOS. It is more reliable and synchronous.
agent-browser clipboard readpbpaste"Figma keeps redirecting without showing tokens"
→ Figma may require 2FA re-authentication. Let the user complete the flow in the visible browser.
"Permission denied on .env"
→ then retry.
chmod 644 .env"Token generated but immediately invalid"
→ Figma may have a propagation delay of a few seconds. The script retries automatically 3 times with a 2s interval.
"Auto-login fails despite valid credentials"
→ Verify and are correct via manual login.
→ If 2FA is enabled, auto-login will stop at the 2FA step — the user must complete it manually.
FIGMA_USERNAMEFIGMA_PASSWORD"Expiry select fails with 'Element is not a <select> element'"
→ Figma's expiry field is a custom combobox, not a native . Do not use . Instead: click the combobox ref to open it, then click the last via JS (see Step 5).
<select>agent-browser select[role="option"]"DOM input read returns empty even though the token is visible on screen"
→ Figma's token display input uses internal React state; may not reflect the rendered text. Always use the Copy button + as the primary retrieval method (see Step 6).
.valuepbpaste