generate-project-plan
Generate a FigJam project plan board from a PRD plus codebase context. Interactive flow: research → propose sections → per-section deep research → per-section content + block-shape proposal → create FigJam → skeleton → fill → diagrams → wrap. Each content block (section, nested section, intro callout, table, multi-column text, sticky column, diagram section, metadata strip) has its own subskill reference file. Use when the user asks for 'project plan in FigJam', 'interactive project plan', '/generate-project-plan', or provides a PRD and wants per-section confirmation on content + rendering.
Install
npx skills add https://github.com/figma/mcp-server-guide --skill generate-project-plan- disable-model-invocation
- false
generate-project-plan
Turn a PRD (plus optional codebase grounding) into a FigJam project plan board. Section set is not fixed — the skill proposes candidates from the research and the user picks which to include. For each picked section, the skill proposes content + rendering shape (block) and the user confirms.
Mandatory prerequisites
Foundation skills (load by name; available in figma/mcp-server-guide):
figma-use— Load once per session. Stays in context for alluse_figmacalls.figma-use-figjam— Re-load before everyuse_figmacall.figma-generate-diagram— Re-load before everygenerate_diagramcall.
Foundation references (in this plugin):
foundation/palette.md[blocked] — section + sticky + text palette constants (hex/255).foundation/layout.md[blocked] — canvas geometry, sizing rules, placeholder lifecycle.foundation/plugin-api-traps.md[blocked] — documented traps for FigJamuse_figma.foundation/codebase-grounding.md[blocked] — Step 1 expansion rules.
Section catalog:
section-catalog.md[blocked] — the ~10 candidate sections with default blocks and palette.
Block subskills (one per content type — re-load the one(s) you need before each use_figma fill call):
Also pass skillNames: "figma-use,figma-use-figjam,generate-project-plan" on use_figma calls (logging only).
Visual UI conventions — STRICT, do not deviate
These are derived from a canonical reference board. Read the source-of-truth files for the full constants; this section is a single-place summary so an agent can answer "what color / size / font / padding?" without hunting.
Colors (two-tone per section)
Every left-column section uses two coordinated colors of the same hue: a very-pale ARCH_PALE background, and a slightly-more-saturated FigJam-SECTION palette color for any table header inside that section. Right-column diagram sections are pure white.
Source: references/foundation/palette.md. Never use the dark-saturated palette (#874FFF, #3DADFF, etc.) for table headers — that's for FigJam's standalone tables, not project-plan boards.
Architecture-diagram subgraph colors are auto-applied by generate_diagram and must not be overridden. Their canonical values: client #AFF4C6 rounded-rect, gateway #FFFFFF square (diamond if labeled "Load Balancer"/"ALB"/"LB"), service #E4CCFF square, datastore #BDE3FF cylinder, external #FFFFFF PREDEFINED_PROCESS, async #BDE3FF ENG_QUEUE.
Typography (font sizes)
The three different H3 sizes are deliberate. 40 = matches H2 weight when subhead is alone in the section. 32 = sub-section header inside a child section (672px inner width). 24 = column title in narrow contexts (≤ 224px col width). Pick by container width, not by semantic depth.
Always load both Inter Medium AND Inter Bold at the top of any use_figma script that creates tables (Bold) plus any other text (Medium).
Section properties
Outer wrapper + column alignment (STRICT defaults)
The board has one outer wrapper (unlabeled, white) plus the diagram column:
- Column wrapper — an unlabeled white SECTION at
(_, 0). Contains, in order from top:- H1 project title (40px Inter Medium, charcoal) at
(64, 64)(= section padding) - Body row of metadata: Owner / Status / Last updated / Source (16px Inter Medium, charcoal), at
(64, h1.y + h1.height + 16)— 16px gap below H1; 32px gap between each body cell - 64px gap below the body row, then the 6 left-column sections stacked with 64px gutter
- H1 project title (40px Inter Medium, charcoal) at
- Diagram column at
(columnWrapperRight + 64, 0). Each diagram is its own un-wrapped white SECTION; the top diagram's y aligns with the column wrapper's y (= 0). Diagrams stack with 64px gutter.
The metadata is embedded in the column wrapper (NOT a separate section). One column wrapper holds everything text-related.
Constants:
- Wrapper inner padding (all 4 sides): 64
- Vertical gap between sections inside the wrapper: 64
- Horizontal gap between wrapper right edge and diagram-column left edge: 64
- Top of the diagram column aligns with
wrapper.y(same horizontal axis as the wrapper top edge) - Vertical gap between stacked diagram sections: 64 (matches the inner gutter)
The wrapper has no header (no H2 inside) and no name — it's a pure container. Don't repeat the project title here; that lives in the metadata strip.
Position the diagram column after the wrapper exists:
Vertical spacing (STRICT)
Always position children using prevChild.y + prevChild.height + 24 — never use a fixed offset like prevChild.y + 60. The H3 has three different sizes (40 / 32 / 24), so a fixed offset is wrong by definition; always read prevChild.height after the font size is set.
When you change a header's font size after the fact, you MUST re-stack downstream children. Setting h3.fontSize = 40 grows the node's height; if the next child's y was computed before the resize, it will overlap.
Tables
- Both header AND body cells use
Inter Bold16px (NOT Medium). - Header text is
#1E1E1Echarcoal on a light fill (matching the section's hue). - Body cells leave fill at default white; text is charcoal.
- Headers do NOT use white-on-dark — that's wrong for this board style.
Diagrams (right column)
generate_diagram with useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026" produces multiple page-level nodes (1–2 subgraph SECTIONs + bare SHAPE_WITH_TEXTs + CONNECTORs), NOT a single container. To wrap them in a section:
- Collect all new page-level nodes (exclude known plan-section IDs).
- Compute the bbox.
- Create a new SECTION sized to
bboxW + 64 × bboxH + 64 + 64(HEADER_BLOCK = 40 H2 + 24 gap), fillARCH_PALE.white. - Reparent each diagram node, translating local coords to maintain visual layout.
- CRITICAL — delete and recreate every connector after reparent. The assign-to-self trick (
c.connectorStart = c.connectorStart) is NOT reliable: short connectors re-route fine, but long-bend connectors retain stale elbow waypoints and extend hundreds of pixels outside the section. Delete +figma.createConnector()from captured spec produces a clean route every time. Seeblocks/diagram-section.md[blocked] for the spec-capture + recreate pattern. - Connector labels — explicit
fontName+fontSize+fillsALL required. A fresh connector'stextsublayer has no usable defaults. Set ALL FOUR:c.text.fontName = { family: 'Inter', style: 'Medium' },c.text.fontSize = 14,c.text.characters = label,c.text.fills = [{ type: 'SOLID', color: CHARCOAL }]. Defaulttext.fillsis[](empty array) so the label renders transparent and is invisible — read-back ofc.text.characterslies; verify with a screenshot.
Diagram-section convention: section.name = "", H2 text node inside as the title (matches the rest of the board).
What NOT to use (common wrong defaults)
Step template
Every step below uses this shape. Read the step, then execute.
Three step types:
- Research — read-only; checkpoint = self-check list.
- Confirm — no board writes, user decision gate; checkpoint =
AskUserQuestion. - Write — creates/mutates FigJam; checkpoint = screenshot +
AskUserQuestion.
Step 1 — Gather context [Research]
Inputs required
- PRD file path or pasted text.
- Optional: codebase entry points (file paths, service names, doc paths).
Ask if missing
- "Where's the PRD? (path or paste)."
- "Any codebase entry points I should ground in? (paths / services / docs / 'none')."
Tools / refs loaded
Read,Glob,Grep.foundation/codebase-grounding.md[blocked].
Do
- Read the PRD. Extract: title, problem, goals, non-goals, owner, audience, success metrics, rollout hints, risks.
- If entry points provided: follow
codebase-grounding.md— bounded 20-file cap, depth-1 imports, walk up toCLAUDE.md/ARCHITECTURE.md/OWNERS. - Produce the tech-context object:
files_read,services,external_deps,key_modules,architecture_notes,ownership,expansion_truncated.
Self-check
- Have: project title, problem statement, at least 1 concrete goal, owner (or "TBD"), services touched (or empty list with a reason).
- Enough signal to draft candidate section cards in Step 2. If not, loop back and ask.
Step 2 — Propose candidate sections [Confirm]
Inputs required
- Tech-context object from Step 1.
Tools / refs loaded
section-catalog.md[blocked].AskUserQuestion.
Do
- For each section in the catalog, decide if there is real content for it from Step 1. Skip catalog entries that would be empty or padding.
- For each qualifying candidate, produce a card:
- Title (catalog name)
- 1-line description (what this section will contain, specific to the PRD)
- Why suggested (which PRD facts or tech-context items justify it)
- Default block shape (from the catalog)
- Print all cards to chat.
- Fire
AskUserQuestionwith a multiSelect question per batch of ≤4 candidates (max 4 questions per call, 4 options each → up to 16 candidates per call). Each option's label is the section title; description is the 1-line summary.
Checkpoint (AskUserQuestion)
- The multiSelect questions above. User ticks the sections they want. Store the selected set as
approved_sections. - If zero sections selected → stop with a clean exit message. No file is created.
Step 3 — Per-section deep research [Research]
Inputs required
approved_sectionsfrom Step 2; tech-context from Step 1.
Tools / refs loaded
Read,Grep,Glob(optional).
Do
- For each section in
approved_sections, look up its catalog entry. The catalog declares what the section needs (e.g. Dependencies needs cross-team services + external deps + blockers). - Compare what the section needs vs. what the tech-context has.
- Produce a gap list per section: specific facts the user must supply, framed as answerable questions (no "figure it out yourself" gaps).
Self-check
- Every
approved_sectionsentry has eitherready(no gaps) or a specific non-empty gap list. - Gaps are answerable — not vague prompts like "tell me more about X."
Step 4 — Per-section content + block proposal [Confirm]
Inputs required
- Gap lists from Step 3.
Tools / refs loaded
AskUserQuestion.section-catalog.md[blocked].- Block reference(s) for the section's default shape (e.g.
blocks/table.mdfor Dependencies).
Do, per section (one at a time, or small batch if trivial):
- Fill the gap list — free-text prompt for prose;
AskUserQuestionfor bounded choices. - Propose:
- Content: the concrete bullets / rows / stickies that will appear.
- Block shape: the rendering block (body paragraph / table / multi-column / sticky column / …). Default from
section-catalog.md; offer alternative shapes where sensible (e.g. "As a table, or as a multi-column layout?").
- Show a short preview — section title + first line of body + block type summary.
- Fire
AskUserQuestion: "Use this content + shape? [Yes / Edit / Skip this section]." - Edit → accept free-text amendments, re-show, re-ask. Skip → mark the section as
skipped; do not write it to the board.
Checkpoint
- Every section is
approved,edited+approved, orskipped. No board writes yet.
Step 5 — Create FigJam file [Write]
Inputs required
approved_sections(non-skipped); project title;planKey(Figma team plan).
Tools / refs loaded
create_new_fileMCP tool.whoamiMCP tool (forplanKeyif not known).use_figma(once).figma-use(already in context from Step 5 onward).figma-use-figjam(re-loaded for the probe).AskUserQuestion.
Do
- Resolve
planKey: callwhoami. If one plan → use it. If multiple →AskUserQuestionwhich team. - Call
create_new_filewith{ planKey, fileName: "<project title>", editorType: "figjam" }. Capturefile_key+file_url. - Run the first-run probe (
use_figma):
Expect editorType === "figjam" and empty page. If not, halt and report.
Checkpoint (probe output + AskUserQuestion)
- Print probe return +
file_url. - AskUserQuestion: "File created at
<file_url>— proceed to skeleton? [Yes / Cancel]." Cancel = stop, leave empty file.
Step 6 — Skeleton pass [Write]
Inputs required
approved_sectionsin taxonomy order; palette fromfoundation/palette.md; layout constants fromfoundation/layout.md.
Tools / refs loaded
use_figma(one call).- Re-load
figma-use-figjam/SKILL.md. - Re-load
blocks/section.mdandblocks/metadata-strip.md. foundation/palette.md[blocked],foundation/layout.md[blocked].
Do
- Create metadata strip per
blocks/metadata-strip.md(H1 + 4 body texts at board(0, 0)–(0, ~100)). - For each approved section, create a top-level SECTION per
blocks/section.md— colored bg from ARCH_PALE,section.name = ""(no title-bar label — STRICT),placeholder = true,resizeWithoutConstraints(LEFT_COL_W, DEFAULT_H)for left-column sections orresizeWithoutConstraints(RIGHT_COL_W_MIN, DEFAULT_H)for right-column diagram sections. - Position each left-column section at
(0, SECTION_TOP_Y + cumulative_y). Right-column sections at(832, SECTION_TOP_Y + cumulative_y_right). - Return all created node IDs:
{ createdNodeIds: { metadataStrip: {...}, sections: { <slug>: "<id>", ... } }, status: "skeleton-complete" }. (Theslugis internal, used to look up palette + drive Step 7 fills. It is never written tosection.name.) - Take an inline screenshot at
await figma.currentPage.screenshot({ scale: 0.3 }).
Checkpoint (screenshot + AskUserQuestion)
- AskUserQuestion: "Skeleton looks right? [Yes / Fix / Cancel]." Fix = targeted fix script; Cancel = stop.
Step 7 — Fill pass (one call per section) [Write]
Inputs required
- Approved content + block shape for each section; section ID from Step 6.
Tools / refs loaded
use_figma(one call per section).- Re-load
figma-use-figjam/SKILL.md. - Re-load whichever block refs this section uses:
blocks/section.mdalways.blocks/text-primitives.mdif body/H3/list.blocks/table.mdif table.blocks/multi-column-text.mdif multi-column.blocks/nested-section.mdif nested subsections.blocks/intro-callout.mdif highlighted intro.blocks/sticky-column.mdif stickies.
Do, per section:
await figma.getNodeByIdAsync(sectionId)— confirm type is SECTION.- Build content: H2 header first, then children per approved block shape. Append to section FIRST, then set x/y.
- Two-pass measure for stickies per
blocks/sticky-column.md. section.resizeWithoutConstraints(LEFT_COL_W, computed_height).section.placeholder = false.await section.screenshot()inline.return { mutatedNodeIds: [...], sectionHeight, screenshotIncluded: true }.
Checkpoint (screenshot + AskUserQuestion)
- Per section: AskUserQuestion: "Section
<name>done? [Yes / Edit this section / Skip rest]." Edit = targeted fix; Skip rest = exit fill loop (user will finalize manually).
After all left-column fills: run the re-stack pass (single use_figma) to fix cumulative Y based on actual resized heights:
Then run the outer-wrapper pass (single use_figma) — wrap all left-column sections in an unlabeled white outer SECTION (see "Outer column wrapper" in the visual conventions block above). This is STRICT; do not skip.
Step 8 — Diagrams [Write]
Inputs required
- Diagram intents from Step 2/4 (Current State, Target State, 0–N Key Flows); tech-context for Mermaid composition.
Tools / refs loaded
- Re-load
generate-diagram/SKILL.mdbefore eachgenerate_diagramcall. - Re-load
figma-use-figjam/SKILL.md+blocks/diagram-section.mdbefore each reparentuse_figmacall. generate_diagramMCP tool,use_figma.
Do, per diagram:
use_figma: create the right-column section (white fill, H2 header,placeholder = true) perblocks/diagram-section.md.generate_diagram: compose Mermaid from tech-context; architecture diagrams useuseArchitectureLayoutCode: "FIGMA_DIAGRAM_2026".use_figma: locate the generated diagram node, reparent into the section (section.appendChild(diag)), position below H2, resize section to fit.placeholder = false.await section.screenshot().
Failure handling: if generate_diagram fails, leave a text placeholder in the section reading "Diagram generation failed: <message>. Regenerate manually." Continue to the next diagram.
Checkpoint (screenshot + AskUserQuestion)
- Per diagram: AskUserQuestion: "Diagram
<name>looks right? [Yes / Regenerate / Skip]."
Step 9 — Final review + report [Write, then Read]
Inputs required
file_urlfrom Step 5.
Tools / refs loaded
use_figma(one call for full-page screenshot).
Do
await figma.currentPage.screenshot({ scale: 0.25 })— full board.- Inspect: two-column structure, no overlaps, no sections left with
placeholder = true, metadata strip visible. - Fix any issues with a targeted
use_figmascript. Do not regenerate from scratch. - Post to chat:
Checkpoint (screenshot + AskUserQuestion)
- AskUserQuestion: "Done, or tweak? [Done / Tweak]." Tweak = targeted fix script on user's request, not regeneration.
Operational rules
- ≤10 logical operations per
use_figmacall. - Always return
createdNodeIds/mutatedNodeIdsfrom every write script. - Use
hex/255notation for all palette colors (seefoundation/palette.md). - STRICT: section backgrounds use the
ARCH_PALEpalette, NOT the FigJam standard SECTION palette. ARCH_PALE colors (#EBFFEE,#F8F5FF,#F5FBFF,#FFF7F0, etc.) visually pair with the architecture-diagram subgraph wrappers thatgenerate_diagramproduces. The FigJam SECTION palette (#CDF4D3,#C2E5FF,#DCCCFF,#FFE0C2) is too saturated and causes visible color clash next to diagrams. Seefoundation/palette.md. - STRICT:
section.name = ""on every project-plan section (left-column, right-column, and nested children). The user-facing title is rendered as the H2 text node inside the section, NOT via FigJam's section title-bar label. The reference board uses empty section names; setting a non-emptynameproduces a duplicate label that visually clutters the board. - Read, edit, or cancel at every Confirm/Write checkpoint — never write past an unanswered AskUserQuestion.
- If a
use_figmascript errors: atomic — no changes made. Read the error, fix, retry.
Trigger phrases
"/generate-project-plan", "interactive project plan", "project plan", "make a FigJam project plan", "PRD to FigJam".