figloops-go-push
Push phase — upload images and create Figma frames
Skill by tyburrowbridge
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/tyburrowbridge-figloops-go-push
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: design
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/figloops-go-push
What's inside
- Skill name
figloops-go-push- Description
- Push phase — upload images and create Figma frames
- License
- MIT
- Instructions
- 117 lines in SKILL.md · view on GitHub
Files (1)
- SKILL.md
SKILL.md (first lines)
## Setup
Resolve `FIGLOOPS_PLUGIN_DIR` from env or `.env`. If unset, abort. Scripts: `cd "<CONSUMING_REPO>" && "<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/<name>.ts" <args>`. Always double-quote paths.
## Errors
TS exits non-zero → relay stderr verbatim. MCP fail → relay error, note partial state ("images uploaded but no frames created — delete the Round <N> page if it was created"), don't advance. State load fail → abort.
---
## Handler
1. Mark `[FIGLOOPS] Push to Figma` as `in_progress`.
2. Read `figloops.config.json` for `figma.fileKey` and `viewport.width`. Read `feedback/state.json` for `currentRound`, `uiTheme` (default `'light'`), and current round's `captures` (`[{ label, filename }]`). Resolve captures dir: `feedback/round-<currentRound>/captures/`. If missing or contains no `.png`, `.jpg`, or `.jpeg` files, abort: `✗ No captures found — run /figloops:go from the capture phase first.`
3. Timestamp: `"<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/timestamp.ts" page` (e.g. `24 June 2026 (3:45 PM)`). Page name: `Round <round> — <timestamp>` (em dash).
4. Compute SHA-256 for every capture in one call: `"<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/hash-captures.ts" "<capturesDir>"` → returns `{ "<filename>": "<sha256>", … }`. Read prior round's manifest from `feedback/state.json` at `rounds[<currentRound-1>].pushManifest.frames[]` if exists; build a `{label → imageHash}` map for reuse.
Note: embed only `label` and `filename` strings from `captures[]` — never image bytes.
Call MCP **once** to find/create the page in `<fileKey>` and create all frames in a single JS execution. Embed `captures`, `pageName`, `viewportWidth`, `pageBg`, and `labelClr` as literals.
**Resolve bg/label colors BEFORE building the JS** (do not branch inside the JS — agents sometimes substitute the wrong literal):
- Read `uiTheme` from `feedback/state.json`. If missing, default `'light'`.
- Intent: app theme and canvas bg are **opposite** for contrast.
- `uiTheme === 'light'` → `pageBg = { r: 30/255, g: 30/255, b: 30/255 }` (dark canvas `#1E1E1E`), `labelClr = { r: 1, g: 1, b: 1 }` (white text)
- `uiTheme === 'dark'` → `pageBg = { r: 240/255, g: 240/255, b: 240/255 }` (light canvas `#F0F0F0`), `labelClr = { r: 51/255, g: 51/255, b: 51/255 }` (near-black text)
- Inline both as object literals in the JS below. Do NOT include the `uiTheme` variable or any ternary in the embedded script.
```js
const captures = /* [{label, filename}] from state.json */;
const pageName = '<Round N — timestamp>';
const viewportWidth = <width>;
const pageBg = /* resolved literal, e.g. { r: 30/255, g: 30/255, b: 30/255 } */;
const labelClr = /* resolved literal, e.g. { r: 1, g: 1, b: 1 } */;
function pathToTitle(filename) {
const stem = filename.replace(/\.(png|jpe?g|webp)$/i, '').replace(/^\d+-/, '');
return stem.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
}
let page = figma.root.children.find(p => p.name === pageName);
if (!page) {
const blankPage1 = figma.root.children.find(
p => p.name === 'Page 1' && p.children.length === 0
);
if (blankPage1) { page = blankPage1; page.name = pageName; }
else { page = figma.createPage(); page.name = pageName; }
}
await figma.setCurrentPageAsync(page);
// Always overwrite — Figma's default page bg is ~#F0F0F0; reused pages may keep stale bg.
page.backgrounds = [{ type: 'SOLID', color: pageBg, opacity: 1, visible: true, blendMode: 'NORMAL' }];
const appliedBg = page.backgrounds[0].color; // include in return for verification
await figma.loadFontAsync({ family: 'Inter', style: 'Semi Bold' });
const GAP_Y = 80, LABEL_HEIGHT = 50, LABEL_GAP = 32, FRAME_HEIGHT = 900;
let currentY = 0;
const frames = [];
for (const capture of captures) {
const labelNode = figma.createText();
labelNode.fontName = { family: 'Inter', style: 'Semi Bold' };
labelNode.fontSize = 24;
labelNode.characters = pathToTitle(capture.filename);
labelNode.fills = [{ type: 'SOLID', color: labelClr, opacity: 1, visible: true, blendMode: 'NORMAL' }];
labelNode.x = 0;
labelNode.y = currentY;
page.appendChild(labelNode);
// Clear Figma's frame-name strip (renders just above the frame top).
currentY += LABEL_HEIGHT + LABEL_GAP;
const frame = figma.createFrame();
frame.name = capture.label;
frame.x = 0;
frame.y = currentY;
frame.resize(viewportWidth, FRAME_HEIGHT);
page.appendChild(frame);
frames.push({ label: capture.label, filename: capture.filename, frameId: frame.id });
currentY += FRAME_HEIGHT + GAP_Y;
}
return JSON.stringify({ pageId: page.id, appliedBg, frames });
```
Capture `pageId`, `appliedBg`, and `frames` from the return value. Verify `appliedBg` matches the `pageBg` you embedded (component-wise within 1/255). If it doesn't, abort and surface the mismatch — do not proceed to uploads.
5. **Upload images (parallel)** — partition `frames` into `reuse` (hash matches prior round's frame with same label) and `upload`.
a. For each `upload` frame, call MCP `upload_assets` with `count=1`, `nodeId=<frameId>`, `fileKey=<fileKey>`. Collect `{frameId, filename, uploadUrl, commitUrl?}` pairs. (One MCP call per frame is required because `nodeId` only works with `count=1`.)
b. Upload all bytes and commit in one call — build a JSON array and pipe it to `upload-to-urls.ts` (uploads run with bounded concurrency; commit URLs POST after; per-file progress prints to stderr live). Content-Type is derived from each file's extension by the script:
```bash
echo '[{"file":"<capturesDir>/<f1>","uploadUrl":"<url1>","commitUrl":"<commitUrl1>"}, …]' \
| "<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/upload-to-urls.ts"
```
Omit `commitUrl` for items that didn't return one. The script returns `{ uploaded, failed, commitFailed }`. Surface any `failed` entries verbatim and continue — do not abort. A non-empty `commitFailed` exits non-zero (an uploaded blob never finalized → blank frame): relay it verbatim and stop; do not advance the phase.
d. For `reuse` frames, make ONE `use_figma` call that loops them and sets `fills` on each `frameId` to `[{ type: 'IMAGE', scaleMode: 'FILL', imageHash: '<prior hash>' }]`.
6. Persist manifest:
```bash
echo '<manifest JSON>' | "<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/set-manifest.ts"
```
Manifest JSON: `{ "pageId": "<id>", "frames": [{ "label": "...", "frameId": "...", "imageHash": "..." }, ...] }`. Use the SHA-256 from step 4 as `imageHash` (for reused frames, carry forward the prior hash).
7. Regenerate snapshot: `"<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/render-snapshot.ts"`
8. Print: `🚀 Pushed to Figma!` + the Figma file URL + prompt to share with reviewers.
9. Mark `[FIGLOOPS] Push to Figma` as `completed`. Run:
```bash
"<PLUGIN_DIR>/node_modules/.bin/tsx" "<PLUGIN_DIR>/scripts/advance-phase.ts" await-comments
```
Invoke skill `figloops-go-await`.From tyburrowbridge/figloops, MIT.
More from tyburrowbridge/figloops
- 0.491score
figloops-whatsnew
tyburrowbridge
Show latest figloops release notes and check for available upgrade
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-go-plan-ack
tyburrowbridge
Plan-ack phase — render plan frame in Figma, anchor comment threads, refresh on subsequent runs
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-discover
tyburrowbridge
Auto-detect modal/panel/menu/tab scenarios by crawling the running app
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-go-review
tyburrowbridge
Review phase — display comments by frame; gate on user decision
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-init
tyburrowbridge
Setup wizard — verifies MCP, PAT, Figma file; collects config
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-uninstall
tyburrowbridge
Remove all local figloops files (state, config, .env keys).
SkillCFreshness: C★ 1Claude Code +9 - 0.488score
figloops-go-pull
tyburrowbridge
Pull phase — fetch Figma comments and advance
SkillCFreshness: C★ 1Claude Code +9 - 0.488score
figloops-go-cluster
tyburrowbridge
Cluster phase — group comments into themes and draft plan
SkillCFreshness: C★ 1Claude Code +9 - 0.488score
figloops-restart
tyburrowbridge
Restart or discard the current round
SkillCFreshness: C★ 1Claude Code +9 - 0.488score
figloops-summary
tyburrowbridge
Render every round to date as a single status table
SkillCFreshness: C★ 1Claude Code +9 - 0.488score
figloops-go-await
tyburrowbridge
Await phase — gate until Figma comments arrive
SkillCFreshness: C★ 1Claude Code +9 - 0.488score
figloops-go-capture
tyburrowbridge
Capture phase — Playwright screenshots + preview gate
SkillCFreshness: C★ 1Claude Code +9
Signals
- Stars
- 1
- Forks
- 1
- Open issues
- 0
- Last commit (this folder)
- Jun 25, 2026 (4 months ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/tyburrowbridge/figloops
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from tyburrowbridge
- 0.491score
figloops-go-review
tyburrowbridge
Review phase — display comments by frame; gate on user decision
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-uninstall
tyburrowbridge
Remove all local figloops files (state, config, .env keys).
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-go-plan-ack
tyburrowbridge
Plan-ack phase — render plan frame in Figma, anchor comment threads, refresh on subsequent runs
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-discover
tyburrowbridge
Auto-detect modal/panel/menu/tab scenarios by crawling the running app
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-init
tyburrowbridge
Setup wizard — verifies MCP, PAT, Figma file; collects config
SkillCFreshness: C★ 1Claude Code +9 - 0.491score
figloops-whatsnew
tyburrowbridge
Show latest figloops release notes and check for available upgrade
SkillCFreshness: C★ 1Claude Code +9
Cite this page
figloops-go-push skill. Agent Mart. https://agentmart.dev/skills/tyburrowbridge-figloops-go-push (last updated 2026-10-24)