Files
hyperframes/packages/cli/src/commands/figma.ts
T
Vance IngallsandClaude Fable 5 566d49382c feat(skills): reroute /figma by capability - REST/CLI for phases 1-3, MCP for 4-5 (M4) (#1873)
* feat(skills): reroute /figma by capability - REST/CLI for phases 1-3, MCP for 4-5 (M4)

Rewrites the skill from MCP-first to the spec 2 split: asset/tokens/
component route through the hyperframes figma CLI (FIGMA_TOKEN), motion/
shaders stay agent-driven over MCP (no REST equivalent). Adds two-
credential guidance, Starter rate-limit tactics (recursive:true, raw-
response cache, opt-in screenshots), the 7.1 binding flow (tokens before
components, one ask per unknown library, never value matching), and the
shader manual-export default. Catalog blurbs updated in lockstep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(cli): register figma component subcommand

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(skills): add storyboard-to-animatic guidance to /figma

Field-tested against a real 26-scene storyboard section: the parsing
grammar (frame-sized nodes incl. loose rectangles = scenes, x-order =
time order, TEXT below the strip = director notes paired by x-overlap),
batched still export (chunk ~4 ids per render call - big frames timeout
past ~12), a note-verb -> transition vocabulary (EXPLOSION/SLIDE/MORPH/
CYCLE), and the stills-vs-component routing rule for within-scene motion
notes. Catalog blurbs updated in lockstep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(skills): storyboard frames are keyframes, not slides

Field-tested against a second real storyboard section: frames sharing an
element (matched by name, else geometry similarity) define that element's
states through time - tween the element between states, crossfade only
when pixels genuinely differ, enter/exit unmatched children, tween frame
backgrounds as a color track. Stills demoted to fallback for frames that
don't decompose. Validated live: a 4-frame logo-rise reconstructed as one
element with four keyframes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(figma): self-explanatory first-run experience + mintlify guide

- NO_TOKEN/BAD_TOKEN errors now carry the full one-time setup (mint URL,
  read-only scope checklist, persist hint) instead of a bare pointer
- figma subcommands print clean guidance on typed client errors, not a
  stack trace (shared withFigmaErrors boundary)
- CLI help gains component subcommand, FIRST-TIME SETUP and WHAT TO
  EXPECT blocks
- /figma skill: preflight the token before the first CLI call and walk
  the user through setup up front; narrate landed-artifact + next action
  at every step
- new docs/guides/figma.mdx (setup, per-phase walkthroughs, provenance,
  troubleshooting table) wired into docs.json nav

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(figma): review fixes — missing withFigmaErrors imports, 401/403 semantics, docs accuracy

- tokens.ts/component.ts called withFigmaErrors without importing it
  (tsup doesn't typecheck, so every invocation shipped as an immediate
  ReferenceError); imports added, tsc --noEmit now clean
- error boundary widened to all Errors so bad-ref/bad-format input
  errors print their message instead of a stack trace
- 401 no longer claims 'missing scopes' (figma signals that as 403);
  new FORBIDDEN code maps non-variables 403 to scope/access guidance
- docs: asset/component refs require a node id (bare fileKey is
  tokens-only), example snippet matches real output, FORBIDDEN row
- skill: preflight counts a project-.env token as configured (CLI
  auto-loads it); BAD_TOKEN/FORBIDDEN guidance split

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(cli): present figma errors via standard errorBox

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 19:13:08 -07:00

59 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* `hyperframes figma` — import figma assets, tokens, and components over
* the REST API (design spec: phases 13 run on REST; motion/shaders are
* MCP-only and live in the /figma skill, not this command).
*
* Requires FIGMA_TOKEN (personal access token from figma.com/settings).
*/
import { defineCommand } from "citty";
import type { Example } from "./_examples.js";
import { c } from "../ui/colors.js";
export const examples: Example[] = [
[
"Import a frame as a frozen SVG asset",
"hyperframes figma asset 'https://www.figma.com/design/KEY/T?node-id=1-2'",
],
["Import as PNG at 2x", "hyperframes figma asset KEY:1-2 --format png --scale 2"],
["Pull brand tokens into the composition", "hyperframes figma tokens KEY"],
["Import a frame as an editable HTML component", "hyperframes figma component KEY:10-20"],
];
const HELP = `
${c.bold("hyperframes figma")} ${c.dim("<subcommand> [args]")}
Import figma content over the REST API. Requires ${c.accent("FIGMA_TOKEN")}.
${c.bold("SUBCOMMANDS:")}
${c.accent("asset")} ${c.dim("Render a node (png/svg/jpg/pdf), freeze under .media/, print a snippet.")}
${c.accent("tokens")} ${c.dim("Import variables/styles as composition brand variables.")}
${c.accent("component")} ${c.dim("Import a frame as an editable HTML component (brand-linked colors).")}
${c.bold("FIRST-TIME SETUP:")}
1. ${c.dim("Mint a token:")} figma.com/settings → Security → Personal access tokens
2. ${c.dim("Scopes (read-only only — this integration never writes to figma):")}
File content: Read-only · File metadata: Read-only
Variables: Read-only ${c.dim("(optional — Enterprise-only brand variables)")}
3. ${c.accent('export FIGMA_TOKEN="figd_…"')} ${c.dim("— persist in your shell profile or project .env")}
${c.bold("WHAT TO EXPECT:")}
${c.dim("Every import freezes files locally under .media/ and records figma provenance —")}
${c.dim("renders never touch figma. Re-running a command re-imports only what changed.")}
${c.dim("Motion and shader import are agent-only (figma exposes no REST endpoint for")}
${c.dim("either) — use the /figma skill in a Claude session for those.")}
`;
export default defineCommand({
meta: { name: "figma", description: "Import figma assets, tokens, and components (REST)" },
subCommands: {
asset: () => import("./figma/asset.js").then((m) => m.default),
tokens: () => import("./figma/tokens.js").then((m) => m.default),
component: () => import("./figma/component.js").then((m) => m.default),
},
async run({ args }) {
if (!args._?.[0]) console.log(HELP);
},
});