Update to Figma
Snapshot Oct 28, 2026 · 18:05 UTC · version 17.0.0
Collection source: downloaded plugin package.
Instructions updated for figma-spec
Skill instructions — Now says: Check that `figma.spec` is defined in your first `use_figma` script; it exists only when the file’s plan has Specs enabled. If it is undefined, or...
Observed in instructions or declared skills. Runtime behavior has not been tested.
Up to 3 plugins free · no card required.
All fields and original values.
Skill instructions
Oct 24, 2026 · 12:48 UTC
You MUST always call `whoami` before using any `figma.spec.*` API and confirm that the target file’s plan (or the destination plan for a new file) has `specs_enabled: true`. `whoami` does not identify which plan owns a file. If the plan ...
Oct 28, 2026 · 18:05 UTC
Check that `figma.spec` is defined in your first `use_figma` script; it exists only when the file’s plan has Specs enabled. If it is undefined, or `use_figma` reports that it is not supported for Figspec files, explain that Specs is not ...
Compare saved observations
Download comparison JSONSKILL.md text changes
− removed · + added
--- before +++ after @@ -6,7 +6,7 @@ # Work with a Figma spec -You MUST always call `whoami` before using any `figma.spec.*` API and confirm that the target file’s plan (or the destination plan for a new file) has `specs_enabled: true`. `whoami` does not identify which plan owns a file. If the plan is unclear, you MUST ask the user; never infer it from the file key or another plan’s access. If `specs_enabled` is false or absent for that plan, explain that Specs is unavailable and stop the workflow. +Check that `figma.spec` is defined in your first `use_figma` script; it exists only when the file’s plan has Specs enabled. If it is undefined, or `use_figma` reports that it is not supported for Figspec files, explain that Specs is not enabled for this file’s plan and stop the workflow. To create a new standalone Figma spec file, call `whoami`, choose a destination plan with `specs_enabled: true`, and call `create_new_file` with `editorType: "figspec"` and that plan's `key` as `planKey`; if no plan has it, explain that Specs is unavailable and stop. A native Figma spec is GitHub-Flavored Markdown (GFM) under a `SPEC_BLOCK` node. A standalone Figma spec is just one spec; a FigJam or Design file can contain several embedded specs.
Full technical diff · 1 changed fields
changed /skill_md_contents
"---\nname: figma-spec\ndescription: \"This skill is required to create, read, edit, review, or comment on Figma specs. You must read it when working on a PRD, brief, RFC or any kind of document in either a standalone Figma spec or a Figma spec embedded in a Figma Design or Figjam file.\"\ndisable-model-invocation: false\n---\n\n# Work with a Figma spec\n\nYou MUST always call `whoami` before using any `figma.spec.*` API and confirm that the target file’s plan (or the destination plan for a new file) has `specs_enabled: true`. `whoami` does not identify which plan owns a file. If the plan is unclear, you MUST ask the user; never infer it from the file key or another plan’s access. If `specs_enabled` is false or absent for that plan, explain that Specs is unavailable and stop the workflow.\n\nA native Figma spec is GitHub-Flavored Markdown (GFM) under a `SPEC_BLOCK` node. A standalone Figma spec is just one spec; a FigJam or Design file can contain several embedded specs.\n\n## Discover and choose the Figma spec\n\nResolve a supplied URL `node-id` when present with `figma.getNodeByIdAsync` and require `type === \"SPEC_BLOCK\"` plus `specBlockType === \"DOCUMENT\"`. Otherwise, search the current page with `findAllWithCriteria({ types: ['SPEC_BLOCK'] })`, filtering for `specBlockType === \"DOCUMENT\"` (and examine content with `figma.spec.getContent` if needed) until you find your target. If it's not on the current page, load other pages one at a time with `page.loadAsync()` and repeat your query, stopping once the target is resolved.\n\nPass the resolved root ID to every content call. Omit `specNodeId` only when the file is known to contain exactly one Figma spec; omission throws otherwise. Never edit ordinary canvas text nodes as a fallback.\n\n## Create a Figma spec on a canvas\n\n```js\n// Creates an embedded Figma spec on a Design or FigJam canvas (not a standalone Figma spec file).\nconst { specNodeId } = await figma.spec.createOnCanvasAsync({\n title: 'Untitled spec',\n content: '# Title\\n\\nA paragraph.\\n',\n})\n\nconst specNode = await figma.getNodeByIdAsync(specNodeId)\nif (!specNode) throw new Error('The created Figma spec could not be resolved')\n\nconst after = await figma.spec.getContent({ specNodeId })\nif (!after.markdown.includes('A paragraph.')) {\n throw new Error('The new Figma spec content was not applied')\n}\n```\n\nAlways pass a non-empty `title` and a `content` string (`''` is allowed). `title` names the root; include any desired `# H1` in `content`.\n\n## Content methods\n\n```js\n// READ — returns specNodeId, markdown, title, etc.\n// The body is `markdown`, not `content`.\nconst before = await figma.spec.getContent({ specNodeId: chosenSpec.id })\nconst page = await figma.spec.getContent({\n specNodeId: before.specNodeId,\n offset: 100,\n maxLines: 50,\n})\n\n// REPLACE — clears + recreates the document; use only for an empty first draft.\nawait figma.spec.setContent({\n specNodeId: before.specNodeId,\n content: '# Title\\n\\nA paragraph.\\n',\n})\n\n// EDIT — find/replace; each oldText must match exactly once.\nawait figma.spec.editContent({\n specNodeId: before.specNodeId,\n edits: [{ oldText: 'First paragraph.', newText: 'Edited first paragraph.' }],\n})\n\nconst after = await figma.spec.getContent({ specNodeId: before.specNodeId })\nif (!after.markdown.includes('Edited first paragraph.')) {\n throw new Error('The targeted Figma spec edit was not applied')\n}\n```\n\n- Use **`getContent`** with the same `specNodeId` before edits and to verify writes; DON'T rely on screenshots.\n- Default to multiplayer-safe, surgical **`editContent`**.\n- Use destructive **`setContent`** only for an empty first draft or an explicit whole-document replacement.\n\n## Critical rules\n\n1. **`editContent` requires exactly one match.** Zero or multiple `oldText` matches fail without changes. Add surrounding context to make it unique.\n2. **Pass Markdown as plain strings.** Never encode it with `Buffer`/base64 or decode it with `atob()`. Base64 is only for image data URLs inside Markdown.\n3. **Keep Markdown and traversal separate.** `figma.spec.*` handles content; node traversal exposes only document roots and embedded-canvas branches.\n4. **Preserve comment markers.** Keep overlapping `[comment:<uuid>]…[/comment:<uuid>]` pairs unchanged around the same text, or the thread may detach or disappear. When in doubt copy the marked span exactly and edit around it.\n5. **Use portable references.** Shared Figma specs must use links such as GitHub permalinks, never machine-local paths or `localhost` URLs.\n\n## Style\n\n- Keep the title block first and formatted as H1, even when its text is empty. Never remove it or insert blocks before it.\n- Prefer bold text in table header row.\n- Use `<ins>` for underline, including combined formatting: `<ins>**bold and underlined**</ins>`. Do not use `<u>`, which only works in HTML imports.\n\n## Images\n\nImages use GFM image syntax inside `setContent` or `editContent`:\n\n- `` embeds bytes that are decoded and uploaded.\n- `` is fetched server-side; disallowed hosts are rejected.\n\n`getContent` emits image URLs as `https://<host>/spec/<fileKey>/image/<sha>?w=<width>&h=<height>`.\n\n## Comments\n\nUse `figma.spec.comments.*` for anchored threads. Prefer comments over leaving silent edits when you're unsure of a change.\n\n```js\nconst comment = await figma.spec.comments.create({\n specNodeId: before.specNodeId,\n anchorText: 'the exact rendered text to anchor to', // MUST appear exactly once\n contextText: 'a longer unique substring containing anchorText', // optional; disambiguates repeats\n message: 'Comment body (markdown).',\n})\n\nconst { root, replies, hasMore } = await figma.spec.comments.getThread({ uuid: comment.uuid })\nawait figma.spec.comments.reply({ uuid: comment.uuid, message: 'A reply.' })\n```\n\n- `anchorText` must match exactly once in the rendered Figma spec. Use a longer `contextText` containing it to disambiguate repeated text.\n- `getThread` and `reply` require the root comment UUID surfaced by a `[comment:<uuid>]` marker; never pass a reply UUID to `reply`.\n""---\nname: figma-spec\ndescription: \"This skill is required to create, read, edit, review, or comment on Figma specs. You must read it when working on a PRD, brief, RFC or any kind of document in either a standalone Figma spec or a Figma spec embedded in a Figma Design or Figjam file.\"\ndisable-model-invocation: false\n---\n\n# Work with a Figma spec\n\nCheck that `figma.spec` is defined in your first `use_figma` script; it exists only when the file’s plan has Specs enabled. If it is undefined, or `use_figma` reports that it is not supported for Figspec files, explain that Specs is not enabled for this file’s plan and stop the workflow. To create a new standalone Figma spec file, call `whoami`, choose a destination plan with `specs_enabled: true`, and call `create_new_file` with `editorType: \"figspec\"` and that plan's `key` as `planKey`; if no plan has it, explain that Specs is unavailable and stop.\n\nA native Figma spec is GitHub-Flavored Markdown (GFM) under a `SPEC_BLOCK` node. A standalone Figma spec is just one spec; a FigJam or Design file can contain several embedded specs.\n\n## Discover and choose the Figma spec\n\nResolve a supplied URL `node-id` when present with `figma.getNodeByIdAsync` and require `type === \"SPEC_BLOCK\"` plus `specBlockType === \"DOCUMENT\"`. Otherwise, search the current page with `findAllWithCriteria({ types: ['SPEC_BLOCK'] })`, filtering for `specBlockType === \"DOCUMENT\"` (and examine content with `figma.spec.getContent` if needed) until you find your target. If it's not on the current page, load other pages one at a time with `page.loadAsync()` and repeat your query, stopping once the target is resolved.\n\nPass the resolved root ID to every content call. Omit `specNodeId` only when the file is known to contain exactly one Figma spec; omission throws otherwise. Never edit ordinary canvas text nodes as a fallback.\n\n## Create a Figma spec on a canvas\n\n```js\n// Creates an embedded Figma spec on a Design or FigJam canvas (not a standalone Figma spec file).\nconst { specNodeId } = await figma.spec.createOnCanvasAsync({\n title: 'Untitled spec',\n content: '# Title\\n\\nA paragraph.\\n',\n})\n\nconst specNode = await figma.getNodeByIdAsync(specNodeId)\nif (!specNode) throw new Error('The created Figma spec could not be resolved')\n\nconst after = await figma.spec.getContent({ specNodeId })\nif (!after.markdown.includes('A paragraph.')) {\n throw new Error('The new Figma spec content was not applied')\n}\n```\n\nAlways pass a non-empty `title` and a `content` string (`''` is allowed). `title` names the root; include any desired `# H1` in `content`.\n\n## Content methods\n\n```js\n// READ — returns specNodeId, markdown, title, etc.\n// The body is `markdown`, not `content`.\nconst before = await figma.spec.getContent({ specNodeId: chosenSpec.id })\nconst page = await figma.spec.getContent({\n specNodeId: before.specNodeId,\n offset: 100,\n maxLines: 50,\n})\n\n// REPLACE — clears + recreates the document; use only for an empty first draft.\nawait figma.spec.setContent({\n specNodeId: before.specNodeId,\n content: '# Title\\n\\nA paragraph.\\n',\n})\n\n// EDIT — find/replace; each oldText must match exactly once.\nawait figma.spec.editContent({\n specNodeId: before.specNodeId,\n edits: [{ oldText: 'First paragraph.', newText: 'Edited first paragraph.' }],\n})\n\nconst after = await figma.spec.getContent({ specNodeId: before.specNodeId })\nif (!after.markdown.includes('Edited first paragraph.')) {\n throw new Error('The targeted Figma spec edit was not applied')\n}\n```\n\n- Use **`getContent`** with the same `specNodeId` before edits and to verify writes; DON'T rely on screenshots.\n- Default to multiplayer-safe, surgical **`editContent`**.\n- Use destructive **`setContent`** only for an empty first draft or an explicit whole-document replacement.\n\n## Critical rules\n\n1. **`editContent` requires exactly one match.** Zero or multiple `oldText` matches fail without changes. Add surrounding context to make it unique.\n2. **Pass Markdown as plain strings.** Never encode it with `Buffer`/base64 or decode it with `atob()`. Base64 is only for image data URLs inside Markdown.\n3. **Keep Markdown and traversal separate.** `figma.spec.*` handles content; node traversal exposes only document roots and embedded-canvas branches.\n4. **Preserve comment markers.** Keep overlapping `[comment:<uuid>]…[/comment:<uuid>]` pairs unchanged around the same text, or the thread may detach or disappear. When in doubt copy the marked span exactly and edit around it.\n5. **Use portable references.** Shared Figma specs must use links such as GitHub permalinks, never machine-local paths or `localhost` URLs.\n\n## Style\n\n- Keep the title block first and formatted as H1, even when its text is empty. Never remove it or insert blocks before it.\n- Prefer bold text in table header row.\n- Use `<ins>` for underline, including combined formatting: `<ins>**bold and underlined**</ins>`. Do not use `<u>`, which only works in HTML imports.\n\n## Images\n\nImages use GFM image syntax inside `setContent` or `editContent`:\n\n- `` embeds bytes that are decoded and uploaded.\n- `` is fetched server-side; disallowed hosts are rejected.\n\n`getContent` emits image URLs as `https://<host>/spec/<fileKey>/image/<sha>?w=<width>&h=<height>`.\n\n## Comments\n\nUse `figma.spec.comments.*` for anchored threads. Prefer comments over leaving silent edits when you're unsure of a change.\n\n```js\nconst comment = await figma.spec.comments.create({\n specNodeId: before.specNodeId,\n anchorText: 'the exact rendered text to anchor to', // MUST appear exactly once\n contextText: 'a longer unique substring containing anchorText', // optional; disambiguates repeats\n message: 'Comment body (markdown).',\n})\n\nconst { root, replies, hasMore } = await figma.spec.comments.getThread({ uuid: comment.uuid })\nawait figma.spec.comments.reply({ uuid: comment.uuid, message: 'A reply.' })\n```\n\n- `anchorText` must match exactly once in the rendered Figma spec. Use a longer `contextText` containing it to disambiguate repeated text.\n- `getThread` and `reply` require the root comment UUID surfaced by a `[comment:<uuid>]` marker; never pass a reply UUID to `reply`.\n"Full snapshot data
{
"description": "This skill is required to create, read, edit, review, or comment on Figma specs. You must read it when working on a PRD, brief, RFC or any kind of document in either a standalone Figma spec or a Figma spec embedded in a Figma Design or Figjam file.",
"included_files": [],
"name": "figma-spec",
"skill_md_contents": "---\nname: figma-spec\ndescription: \"This skill is required to create, read, edit, review, or comment on Figma specs. You must read it when working on a PRD, brief, RFC or any kind of document in either a standalone Figma spec or a Figma spec embedded in a Figma Design or Figjam file.\"\ndisable-model-invocation: false\n---\n\n# Work with a Figma spec\n\nCheck that `figma.spec` is defined in your first `use_figma` script; it exists only when the file’s plan has Specs enabled. If it is undefined, or `use_figma` reports that it is not supported for Figspec files, explain that Specs is not enabled for this file’s plan and stop the workflow. To create a new standalone Figma spec file, call `whoami`, choose a destination plan with `specs_enabled: true`, and call `create_new_file` with `editorType: \"figspec\"` and that plan's `key` as `planKey`; if no plan has it, explain that Specs is unavailable and stop.\n\nA native Figma spec is GitHub-Flavored Markdown (GFM) under a `SPEC_BLOCK` node. A standalone Figma spec is just one spec; a FigJam or Design file can contain several embedded specs.\n\n## Discover and choose the Figma spec\n\nResolve a supplied URL `node-id` when present with `figma.getNodeByIdAsync` and require `type === \"SPEC_BLOCK\"` plus `specBlockType === \"DOCUMENT\"`. Otherwise, search the current page with `findAllWithCriteria({ types: ['SPEC_BLOCK'] })`, filtering for `specBlockType === \"DOCUMENT\"` (and examine content with `figma.spec.getContent` if needed) until you find your target. If it's not on the current page, load other pages one at a time with `page.loadAsync()` and repeat your query, stopping once the target is resolved.\n\nPass the resolved root ID to every content call. Omit `specNodeId` only when the file is known to contain exactly one Figma spec; omission throws otherwise. Never edit ordinary canvas text nodes as a fallback.\n\n## Create a Figma spec on a canvas\n\n```js\n// Creates an embedded Figma spec on a Design or FigJam canvas (not a standalone Figma spec file).\nconst { specNodeId } = await figma.spec.createOnCanvasAsync({\n title: 'Untitled spec',\n content: '# Title\\n\\nA paragraph.\\n',\n})\n\nconst specNode = await figma.getNodeByIdAsync(specNodeId)\nif (!specNode) throw new Error('The created Figma spec could not be resolved')\n\nconst after = await figma.spec.getContent({ specNodeId })\nif (!after.markdown.includes('A paragraph.')) {\n throw new Error('The new Figma spec content was not applied')\n}\n```\n\nAlways pass a non-empty `title` and a `content` string (`''` is allowed). `title` names the root; include any desired `# H1` in `content`.\n\n## Content methods\n\n```js\n// READ — returns specNodeId, markdown, title, etc.\n// The body is `markdown`, not `content`.\nconst before = await figma.spec.getContent({ specNodeId: chosenSpec.id })\nconst page = await figma.spec.getContent({\n specNodeId: before.specNodeId,\n offset: 100,\n maxLines: 50,\n})\n\n// REPLACE — clears + recreates the document; use only for an empty first draft.\nawait figma.spec.setContent({\n specNodeId: before.specNodeId,\n content: '# Title\\n\\nA paragraph.\\n',\n})\n\n// EDIT — find/replace; each oldText must match exactly once.\nawait figma.spec.editContent({\n specNodeId: before.specNodeId,\n edits: [{ oldText: 'First paragraph.', newText: 'Edited first paragraph.' }],\n})\n\nconst after = await figma.spec.getContent({ specNodeId: before.specNodeId })\nif (!after.markdown.includes('Edited first paragraph.')) {\n throw new Error('The targeted Figma spec edit was not applied')\n}\n```\n\n- Use **`getContent`** with the same `specNodeId` before edits and to verify writes; DON'T rely on screenshots.\n- Default to multiplayer-safe, surgical **`editContent`**.\n- Use destructive **`setContent`** only for an empty first draft or an explicit whole-document replacement.\n\n## Critical rules\n\n1. **`editContent` requires exactly one match.** Zero or multiple `oldText` matches fail without changes. Add surrounding context to make it unique.\n2. **Pass Markdown as plain strings.** Never encode it with `Buffer`/base64 or decode it with `atob()`. Base64 is only for image data URLs inside Markdown.\n3. **Keep Markdown and traversal separate.** `figma.spec.*` handles content; node traversal exposes only document roots and embedded-canvas branches.\n4. **Preserve comment markers.** Keep overlapping `[comment:<uuid>]…[/comment:<uuid>]` pairs unchanged around the same text, or the thread may detach or disappear. When in doubt copy the marked span exactly and edit around it.\n5. **Use portable references.** Shared Figma specs must use links such as GitHub permalinks, never machine-local paths or `localhost` URLs.\n\n## Style\n\n- Keep the title block first and formatted as H1, even when its text is empty. Never remove it or insert blocks before it.\n- Prefer bold text in table header row.\n- Use `<ins>` for underline, including combined formatting: `<ins>**bold and underlined**</ins>`. Do not use `<u>`, which only works in HTML imports.\n\n## Images\n\nImages use GFM image syntax inside `setContent` or `editContent`:\n\n- `` embeds bytes that are decoded and uploaded.\n- `` is fetched server-side; disallowed hosts are rejected.\n\n`getContent` emits image URLs as `https://<host>/spec/<fileKey>/image/<sha>?w=<width>&h=<height>`.\n\n## Comments\n\nUse `figma.spec.comments.*` for anchored threads. Prefer comments over leaving silent edits when you're unsure of a change.\n\n```js\nconst comment = await figma.spec.comments.create({\n specNodeId: before.specNodeId,\n anchorText: 'the exact rendered text to anchor to', // MUST appear exactly once\n contextText: 'a longer unique substring containing anchorText', // optional; disambiguates repeats\n message: 'Comment body (markdown).',\n})\n\nconst { root, replies, hasMore } = await figma.spec.comments.getThread({ uuid: comment.uuid })\nawait figma.spec.comments.reply({ uuid: comment.uuid, message: 'A reply.' })\n```\n\n- `anchorText` must match exactly once in the rendered Figma spec. Use a longer `contextText` containing it to disambiguate repeated text.\n- `getThread` and `reply` require the root comment UUID surfaced by a `[comment:<uuid>]` marker; never pass a reply UUID to `reply`.\n"
}SHA-256 of public snapshot: a757002ef2775808bcc930b7c5da4d84aa6f56cd28dabbcfd8a51a29e8c3765f