Visual Recap
Builder.ioOfficialPublished by Builder.io
- Skill
- Content generator
- Software development
- MIT
- Low risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Visual Recap transforms technical changes—such as PRs, commits, or git diffs—into structured, interactive Agent-Native Plans. By moving beyond line-by-line diffs, it provides reviewers with a high-altitude view of the work, including architectural diagrams, file maps, API summaries, and annotated code. It is designed to replace verbose, inline chat summaries with a hosted, annotatable artifact that highlights the shape and intent of the changes.
What it does
- Generates interactive visual recaps from PRs, branches, or commits.
- Creates structured blocks for data models, API endpoints, and file trees.
- Provides annotated code diffs for key changes to focus reviewer attention.
- Includes wireframes for UI-impacting changes to visualize before/after states.
- Supports a local-files privacy mode for offline or sensitive environments.
- Publishes recaps as hosted Agent-Native Plans for collaborative review.
When to use it
- When a PR or commit is large, multi-file, or involves complex architectural changes.
- When changes touch schema, API contracts, or critical business logic.
- When a reviewer needs to understand the 'shape' of a change before diving into raw code.
- When you need to provide visual evidence for UI/UX modifications.
- When working in environments requiring strict data privacy via local-files mode.
Advantages
- Reduces cognitive load for reviewers by summarizing intent before showing code.
- Provides a consistent, structured format for all technical reviews.
- Enables visual comparison of UI changes through wireframes.
- Keeps review documentation centralized and interactive.
- Supports both hosted and fully local/private workflows.
Usage examples
- “recap this PR”
- The agent analyzes the current PR diff, generates a structured plan with a file tree, key change annotations, and relevant wireframes, then provides a link to the hosted recap.
- “show me what this branch changed”
- The agent gathers changes from the current branch, maps them to data models and API endpoints, and publishes an interactive summary for review.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/builderio/visual-recap/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip visual-recap.zip -d .agents/skills
Documentation by Builder.io
Visual Recap
/visual-recap creates a visual plan built from a diff, not toward one. It
is the reverse of forward planning: instead of describing the change you are
about to make, you describe the change that was just made, at a higher altitude
than line-by-line review. The same plan data model serves both directions —
schema, API, file, and architecture changes become the same data-model,
api-endpoint, file-tree, and diagram blocks a forward plan would use, only
now they summarize work that exists. A reviewer scans the shape of the change
before spending attention on the literal lines.
Publish As An Agent-Native Plan — Never Inline
The deliverable is ALWAYS a published Agent-Native Plan, created with
create-visual-recap on the Plan MCP connector — NEVER inline chat content (not
Markdown prose, an ASCII sketch, a table, a fenced "wireframe", or a "here's the
recap" summary). A recap's entire value is the hosted, interactive, annotatable
plan; an inline summary is not a degraded recap, it is the thing a recap
replaces. If the plan (or legacy agent-native-plans) tools are not visible,
discover them through the host's tool_search first; if they are still missing,
STOP and give the user the client-specific reconnect step rather than improvising
an inline recap. Before publishing, or whenever a connector or auth error
appears, READ references/connection.md in this skill directory — it is the
single source of truth for the never-inline rule, connector discovery, and the
per-client reconnect steps. Local-files privacy mode (below) is the one
exception.
Local-Files Privacy Mode — read references/local-files.md
When the user wants no hosted Plan database writes — no DB writes, no Plan MCP
publish, fully local/offline/private recaps, or AGENT_NATIVE_PLANS_MODE=local-files
— do not call any hosted Plan tool except the schema-only get-plan-blocks
catalog lookup. Read the diff with the local recap collect-diff / scan /
build-prompt --local-files helpers, author a local MDX folder (set
kind: "recap" and localOnly: true), and preview it with plan local check,
plan local serve --kind recap, and plan local verify --kind recap. Before
using local-files mode, READ references/local-files.md in this skill directory
— it is the single source of truth for the full contract.
When To Use
Build a recap when a PR or commit is large, multi-file, or touches schema, API contracts, or architecture, and a reviewer would benefit from seeing the change mapped to structured blocks before reading the raw diff. A GitHub Action can generate one automatically from a PR diff; an agent can generate one on request ("recap this PR", "show me what this branch changed"). Skip it for small, single-file, or obvious diffs — a recap is review overhead, and a tiny change reviews faster as plain diff.
Recap The Whole Work Unit
When /visual-recap is invoked in a chat thread after work has already happened,
the default scope is the whole current work unit/thread, not only the most recent
user message, tool action, or follow-up fix. Gather the thread-owned changes
across the conversation: original implementation work, later bug fixes, UI
follow-ups, tests, changesets, skill/instruction updates, generated plan/source
artifacts, and any local import/linking fixes needed to make the recap open.
Use the current diff plus conversation context to separate thread-owned changes from unrelated dirty work that existed before the thread. Exclude unrelated pre-existing edits. If the scope is genuinely ambiguous and cannot be inferred, state the assumption or ask a concise question before publishing.
When updating an existing recap after feedback, revise the recap so it still covers the whole thread/work unit plus the new correction. Do not replace a broad recap with a narrow recap of only the latest feedback unless the user explicitly asks for that narrower scope.
Keep The Recap Body Lean
Do not add boilerplate intro, disclaimer, provenance, or summary prose blocks to
the generated plan body. In particular, do not create a rich-text block just to
say the recap is an aid, that the reviewer should still review the diff, how many
files changed, or which ref/working tree generated the recap. The plan title,
brief, and file-tree (which carries the per-file change stats) already carry
that context.
Only add prose blocks when they tell the reviewer something specific about the change that the structured blocks do not: the objective, a real compatibility risk, an important decision visible in the diff, or a grounded review note.
Recaps Must Be Substantial
Lean is not the same as thin. A recap is not a single wireframe plus one
sentence — that under-serves the reviewer as much as boilerplate prose over-serves
them. Alongside the visual/structural headline (wireframes, data-model,
api-endpoint, diagram), a substantial recap also carries the implementation
evidence:
A short surface/state inventory before authoring: list the changed routes, components, popovers/dialogs, role/access states, empty/error states, and shared abstractions visible in the diff. The final recap must either represent each meaningful item with a block or intentionally omit it because it is tiny, redundant, or not user-visible.
A
file-treeof the changed files with each entry'schangeflag, so the reviewer sees the footprint of the work at a glance.The split
diffof the KEY changed files, grouped under a## Key changesrich-textheading in a single horizontaltabsblock (the default orientation, one file per tab), with a one-linesummaryand a fewannotationson each — so the reviewer can drop from the high-altitude shape straight into the load-bearing code. Use horizontal file tabs, not a vertical side rail, so the selected file has enough width for the side-by-side diff.
Skip the diff appendix only for a genuinely tiny change that reviews faster as plain diff (see "When To Use"); for any change worth recapping, the file-tree and key-change diffs belong in the plan.
Canonical Shape And Budgets
A strong recap follows one skeleton, top to bottom:
UI-impact headline — wireframes first, when the diff changed rendered UI.
Short outcome narrative (
rich-text): what changed and why, 1-3 paragraphs.data-model/api-endpointblocks for schema and contract changes.file-treeof the changed files withchangeflags.## Key changes— one horizontaltabsblock ofdiff/annotated-code.
Budgets that keep the recap reviewable:
3-8 key-change tabs. Fewer than 3 on a large change under-serves the reviewer; more than 8 stops being a summary.
Keep each diff/annotated-code excerpt focused — prefer under ~150 lines per tab; summarize or link the rest of a long file instead of dumping it.
Title at most ~70 characters; brief 1-3 sentences.
These budgets are also the cost ceiling: do not exceed them in the name of thoroughness, and do not re-read the full diff after the initial sequential pass — work from the notes taken during that pass.
GOOD. A 25-file auth change: Before/After wireframes of the login surface,
a two-paragraph narrative, a diff-aware data-model of the sessions table, an
api-endpoint for the new refresh route, a file-tree with change flags, and
## Key changes with five focused tabs, each with a one-line summary and a
few annotations on the load-bearing hunks.
BAD. One giant unsegmented diff dump with no summaries or annotations; or a sparse three-block recap of a 40-file change (one wireframe, one sentence, one file list) that forces the reviewer back into the raw diff anyway.
UI Impact Needs Wireframes
When the diff changes rendered UI, layout, density, visual state, interaction affordances, navigation, controls, menus, dialogs, or design tokens, the recap MUST include one or more wireframes. Prose and file diffs are not a substitute for showing what changed visually.
Before choosing wireframes, make a UI coverage pass from the diff:
Identify the entry surface where the change appears, such as a page header, list row, toolbar, route shell, or menu trigger.
Identify the interaction surface that opens or changes, such as a popover, dialog, tab, sheet, dropdown, inline editor, or toast.
Identify the resulting destination or persistent state, such as a public page, read-only view, empty state, error state, loading state, permission-denied state, or saved/shared state.
Identify access or role variants when permissions change. Owner/admin/editor versus viewer/non-manager differences are visual behavior and need a compact matrix, paired wireframes, or clearly labeled state sequence.
For UI-heavy PRs, a single before/after of the entry surface is not enough. Show the changed entry point, the main changed interaction surface, and the resulting/destination state. Add more states when the diff adds tabs, role-based controls, public/private visibility, invite/manage flows, destructive controls, or empty/error branches.
Choose the smallest visual surface that makes the review clear:
Use a
Before/Afterwireframe pair when the reviewer benefits from direct comparison, such as a removed or added control, a changed state, layout density, ordering, navigation, or a visible component replacement.references/wireframe.mdowns how to lay that pair out (columns vs. vertical stack by geometry).Use an after-only wireframe when the change is purely additive or the "before" state would only show absence without adding review value.
Use more than two wireframes when the UI change is flow-dependent, responsive, or stateful; show the meaningful states in order instead of forcing a single before/after pair.
For tiny surfaces like menus, popovers, dialogs, toasts, or panels, use the matching
surface(popover,panel, etc.) and show the focused sub-surface. Do not redraw a full page unless placement in the page is itself part of the change.
Ground each wireframe in the changed UI behavior, component names, file paths, and diff-visible labels/states. If exact pixels are inferred rather than captured, say so in the wireframe caption or a concise annotation. For local/manual recaps, import or update the plan source that holds the wireframes so the rendered recap opens with the UI visual available.
Wireframe Quality — read references/wireframe.md
UI recap/plan wireframes must meet a strict quality bar — full-width chrome,
pinned bottom bars, real product content, before/after comparability, the right
surface preset, --wf-* tokens instead of hex, and no <html>/<style>/font
tags. Before authoring ANY wireframe / <Screen> / WireframeBlock, READ
references/wireframe.md in this skill directory — it is the single source of
truth for HTML wireframe quality, shared word for word with /visual-plan
and /visual-recap. Do not author wireframes from memory.
Use the standard WireframeBlock / <Screen> format so the Plan viewer owns the
surface frame, theme, and sketchy/clean toggle. HTML wireframes are appropriate
when placement precision matters, especially popovers, menus, dialogs, and dense
forms. For HTML
wireframes, keep renderMode unset or wireframe unless a design-only editable
mockup is explicitly required, because renderMode="design" disables the
sketchy rough overlay.
When a browser tool is available, render a UI-impact recap in the Plan viewer and visually inspect it at the current theme before sharing. If any label, annotation, toolbar, or wireframe content overlaps another element, fix the MDX and re-import before reporting the link. Limit this to one render-and-inspect pass plus at most one fix-and-re-render; do not keep iterating beyond that unless the user explicitly asks. A text-match screenshot is not enough; visually inspect the captured image. When no browser is available (for example a headless CI agent), state that in the recap handoff instead.
Top Canvas Recaps — read ../visual-plan/references/canvas.md
When a recap includes a top canvas, storyboard, or flow view, READ
../visual-plan/references/canvas.md before authoring canvas.mdx. Recap
canvas artboards must use the same HTML wireframe path as good document-body
wireframes: <Screen surface="..." html={...} /> with a semantic HTML fragment.
Do not author fresh kit-tree children such as <FrameScreen>, <Card>,
<Row>, <Title>, or <Btn> inside canvas <Screen> tags. Those components
are legacy compatibility markup for old plans; in new canvas storyboards they
can produce cramped or overlapping layouts even when the inline body wireframe
looks good. If a canvas mockup looks worse than the same screen below the fold,
assume it used the legacy kit path and replace it with an HTML screen.
Open And Report The Recap
In local-files privacy mode, run plan local check first, then report the local
bridge URL from
npx @agent-native/core@latest plan local serve --dir <plan-dir> --kind recap --open
or from <plan-dir>/.plan-url. It opens the hosted Plan UI but reads from the
localhost bridge on this machine, so it is not shareable across machines. If the
Plan app itself is running locally with the same PLAN_LOCAL_DIR, the
/local-plans/<slug> route is also valid. Do not invent a hosted database URL
and do not publish just to get an absolute Plan link.
After creating the recap, link the reviewer to the rendered plan with an
absolute URL on the origin whose database actually holds the plan. That
origin is the Plan MCP server you just created the recap through — NOT whatever
dev server you happen to know is running. The create tool returns the correct
link; report THAT. Never make the primary link a local plan.mdx file, a local
mirror folder, or a relative path such as /plans/<id>.
When the recap is posted to a PR for a private repo, the plan link is not a public URL. Make the PR comment/handoff copy explicit: reviewers may need to sign in to Agent-Native Plans with an account that has access to the owning organization before the link loads. Use wording like: "Private repo recap: sign in with access to this org if the plan does not open." Do not imply the link is broken or public when access is gated by repo/org visibility.
A recap lives only in the database of the MCP that created it. A separately
running local dev server (e.g. http://localhost:8081) has its OWN database and
will NOT contain a recap created through the hosted MCP, so a hand-built
localhost link returns "Plan not found". This is the most common recap
mistake — do not guess an origin you have not confirmed shares the MCP's data.
Resolve the URL in this order:
Use the absolute URL the create tool RETURNS —
openLink.webUrl, else thevisualUrlin the returnedplan.mdxfrontmatter, elseurl/pathresolved against the MCP server's own origin (for the hosted MCP that ishttps://plan.agent-native.com). This always points at the database that has the plan.Use a
localhost/dev origin ONLY when the recap was created through a Plan MCP bound to that same origin — i.e. that MCP's url ishttp://localhost:<port>/mcp. Creating through the hosted MCP and linking to localhost is the exact mismatch that 404s.If only a plan id is available, build the MCP origin's absolute URL (hosted:
https://plan.agent-native.com/plans/<id>) and say it was inferred.
If the user wants to review on localhost but the recap was created through the
hosted MCP, say so plainly: the local dev server cannot see it. To view a recap
on localhost (e.g. to exercise un-deployed local renderer changes), they must
connect a LOCAL Plan MCP (http://localhost:<port>/mcp) and
re-create the recap through it so it lands in the local database; offer to do
that rather than handing over a localhost URL that will not resolve.
When running in Codex and the Browser/in-app side browser tools are available,
open the returned absolute recap URL there automatically after creation. Still
include the same absolute URL in the final response. Local mirror files like
plans/<slug>/plan.mdx may be mentioned only as secondary source-control
artifacts, not as the main way to open the recap.
Diff → Block Mapping
Map each kind of change to the block that carries it, derived mechanically from
the actual diff. The names below are the CONCEPTUAL block types, not the JSX
tags — resolve every conceptual name to its exact tag + prop schema with the
get-plan-blocks tool (see "Block reference" below) before authoring.
Schema / migration change →
data-modelfor the resulting entities, fields, and relations. Flag what moved per field/entity withchange: "added" | "modified" | "removed" | "renamed", and for a changed type setwasto the prior value (e.g. the old column type) — grounded in the real migration diff. That diff-awaredata-modelis the headline; reach for a splitdiffof the literal SQL only when the exact statement still matters, not by default.API / action / route change →
api-endpointwith the method, path, params, request, and responses as they are after the change. Flag each changed param/response withchange(andwason a param whose type/shape changed), and setchangeon the endpoint root for a wholly added or removed route. Mark removed endpoints withdeprecated: trueand explain in prose. Keep multiple API endpoints in the normal single-column document flow unless they are an explicit before/after contract comparison. Author each request/response example as a SINGLE valid JSON value — one top-level object or array, parseable on its own — so it renders in the collapsible JSON explorer. Do not put//or/* */comments, prose, trailing commas, or two or more concatenated top-level objects inside one example; a non-parseable body falls back to flat text and loses the explorer. When an endpoint has several distinct message shapes (for example separate websocket frame types, or a success body versus an error body), give each its OWN example with its own label rather than cramming them into one body.Compatibility-sensitive change → short
rich-textnotes beside the relevantdata-model/api-endpointblock. Name the changed field, endpoint, or behavior and mark whether it is breaking, risky, or non-breaking; pair that note with a splitdifffor the literal lines.Any meaningful code hunk →
diffwithmode: "split", carrying the realbefore/aftertext and thefilename/language. Split mode is the default for recap code review because before/after legibility is the point; usemode: "unified"only for a genuinely narrow standalone hunk where side-by-side would hide the code. Give everydiffa one-linesummarysaying what the hunk changes and why; it renders as a description above the code so the reviewer reads intent first. Never leave a diff unlabeled. For the KEY changed files, attachannotationsto thediffso the recap calls out what each important hunk does — this is the headline affordance for annotating the key files updated. Each annotation anchors to the AFTER-side line numbers by default (setside: "before"to point at removed lines). Keep it to a few high-signal notes per file, not one per line. When several key files each need a substantial diff, introduce the group with arich-textheading block whose markdown is## Key changes, then place thediffblocks under it in a reusabletabsblock with horizontal orientation (the default — omitorientation) so the selected file's split diff gets the full document width. Let that heading label the section — do NOT also set atitleon thetabsblock. Keep each tab label to the file path or a short basename plus directory hint. The renderer's wide document layout is intentionally allowlisted:diff,annotated-code, verticaltabs, andtabscontaining diff-like children break out wider than prose. Do not put API endpoints, OpenAPI specs, data models, JSON explorers, wireframes, question forms, or custom HTML into tabs merely to make them wide. If the recap ends with more than one supporting diff, that trailing diff appendix should be one horizontaltabsblock under its own## Key changesheading, not a stack of separatediffblocks.Brand-new file or a substantial added block with no meaningful "before" →
annotated-coderather than a one-sided splitdiff. Carry the real new code with itsfilename/languageand anchor a few high-signal notes to the lines that matter so the reviewer reads what the new code does, not code for code's sake. Keep splitdifffor true before/after hunks where the removed lines still carry meaning, and group several annotated walkthroughs in a horizontaltabsblock the same way diffs are grouped.Files added / removed / renamed →
file-treewith each entry'schangeflag (added,removed,modified,renamed) and a shortnote; attach asnippetonly when one tells the reviewer something the path does not.Rendered UI / interaction change → one or more wireframes showing the visible UI delta before the reviewer reads code. Use
Before/Afterwireframes when the comparison clarifies the change; otherwise use after-only or a short state/flow sequence. Use realistic UI surfaces: for a popover change, show a popover with its title row, top-right actions, options/fields, tabs, selected/disabled states, people/lists/rows, and any opened prompt/menu anchored to the correct trigger. If a route was added, show the route body and the unavailable/empty state when the diff implements one. If permissions changed, show what managers can do and what viewers/non-managers see instead. Keep the body lean: the wireframe carries the UI story, while the file tree anddiffblocks carry implementation evidence.Architecture or data-flow shift →
diagramwithdata.html/data.cssas a two-panel before/after, layered, or swimlane layout, ormermaidfor a quick graph. Use two-dimensional layouts; do not reduce a structural change to a left-to-right chain. Do not usediagramas a stand-in for rendered UI controls; UI changes needwireframeblocks. Author diagram HTML/CSS with the renderer-owned.diagram-*primitives (.diagram-panel,.diagram-node,.diagram-pill,[data-rough], …) and the same--wf-*theme tokensreferences/wireframe.mddefines — neverfont-family, hex, rgb/hsl literals, or one-off dark/light palettes. Choose the outerframeintentionally: recap diagrams usually benefit fromframe: "show"when they stand alone, but useframe: "hide"when columns, tabs, a card, or the diagram's own panels already provide the boundary.Outcome-first narrative →
rich-textfor the "what changed and why" prose: the objective the diff served, the key decisions visible in it, and the risks a reviewer should weigh. This is the only place the model writes freely.
Block reference — call get-plan-blocks, do not memorize tags
The conceptual block names above (api-endpoint, data-model, json-explorer,
tabs, …) are NOT the JSX tags you author with, and the exact tags, required
fields, and prop shapes change as the block library evolves. Do not author from
memorized tags — they drift and silently produce a wrong tag (ApiEndpoint
instead of Endpoint, JsonExplorer instead of Json, Tabs instead of
TabsBlock) that errors on import.
Before writing any structured plan content, fetch/read the block catalog. In
hosted or self-hosted mode, call get-plan-blocks on the Plan MCP connector
(plan or legacy agent-native-plans). If no Plan tools are visible yet in a
lazy-loading client, search/load them through the host's tool discovery surface
first (tool_search when available). In local-files mode, or when the skill was
installed as plain text and no MCP tools are registered after discovery, run
npx @agent-native/core@latest plan blocks --out plan-blocks.md and read that
file first. The CLI command calls the public no-auth get-plan-blocks route and
sends no plan/recap content. If network access is unavailable, use the bundled
references and validate with plan local check; run plan local serve only
when the hosted Plan UI is reachable or a local Plan app is already running.
The catalog returns the authoritative, always-current block vocabulary generated live from the app's own block registry — the same config the renderer and MDX round-trip use — so it can never be stale even if this SKILL.md is an old installed copy:
get-plan-blocks(defaultformat: "reference") → a compact table of every block's runtimetype, exact MDX<Tag>, placement, and key data fields. This is your map from each conceptual name above to its real tag and props.get-plan-blockswithformat: "schema"→ the full per-block JSON Schema plus a worked example for each block, when you need exact field types, enums, or nesting (e.g.Diff.annotations,Endpoint.params[].in,DataModel.entities[].fields[]).
Author the recap source against the tags and schemas that call returns. The
complete set of valid block-level tags is whatever get-plan-blocks lists;
any other capitalized tag at the block level is rejected on import with an
"Unknown plan block" / "did you mean" error. Lowercase HTML tags inside
rich-text/markdown prose (<div>, <span>, <code>, <br>, …) are always
fine — only capitalized component-style block tags are validated.
A few recap-specific authoring rules the registry table cannot encode:
Every structured block takes a REQUIRED
id(unique across the whole plan) plus the shared optionalsummary/editableenvelope. Ordinary top-level Markdown prose imports as rich-text automatically; use<RichText id="...">only when prose needs explicit metadata or a preserved referenced block id.Every capitalized block component must be self-closing (
<Diagram ... />) or explicitly closed around children (<RichText ...>...</RichText>). Never leave a bare opening tag like<RichText ...>in a paragraph; MDX treats it as unclosed JSX and import fails before the recap can render.Code-bearing blocks (
Code,AnnotatedCode, andDiff) are whitespace-sensitive. Prefer the exact MDX form from theget-plan-blocksexamples / source exporter, where multiline code is encoded as JSON string attributes such ascode={"const x =\n y"}. Static template literals are accepted only when they are static strings with no${...}interpolation.Endpoint: prosedescriptionis the MDX children (body between the tags), not an attribute; for a WebSocket upgrade usemethod="GET". Each request/responseexampleis a JSON string (the renderer parses it into the JSON explorer), so keep it a single parseable JSON value.TabsBlock: the wholetabsarray (including nested child blocks) is ONE JSONtabs={[…]}prop — there is NO nested<Tab>element.WireframeBlock: its body is a single<Screen surface ... html=… />subtree (nested MDX, not a flat prop);htmlmust be a single-quoted string or static template literal, never a dynamichtml={someVar}expression. Seereferences/wireframe.mdfor the HTML rules.Diagram: the whole payload is onedata={{ html?, css?, nodes?, edges?, … }}attribute and requires eitherhtmlor at least one node;Mermaidis its own separate block (sourcetext), not aDiagramprop.
Before / After Is The Headline
The recap's center of gravity is the before/after comparison. For document-body comparisons there are two primitives, and they cover the whole need together:
columns— the side-by-side container, for structured comparisons. Use two columns labeledBeforeandAfter, each holding a block (commonly adata-model,api-endpoint, orrich-text), so the reviewer reads the old shape against the new shape in one glance. This is the right primitive for "the schema went from X to Y" or "the endpoint contract changed like this." Do not usecolumnssimply to compact or group a list of API endpoints.diff— for code. It renders the literal removed and added lines. Use it for the actual hunks. Use split mode by default for recap code review; reservemode: "unified"for genuinely narrow standalone hunks where side-by-side would hide the code. Key-file diff groups should use horizontal tabs so split diffs get the full document width.
For UI diffs, wireframes are the visual comparison primitive. Use before/after
wireframes when the comparison clarifies the change; use after-only or a state
sequence when that better matches the change. The visual headline must show
exact placement, realistic chrome, and adequate padding before any abstract
explanation. Do not stop at the first visible affordance when the diff adds a
flow; show the entry point, the opened surface, and the resulting state or page
so the reviewer can trace the actual user path. references/wireframe.md owns
the before/after layout choice —
the columns renderer keeps narrow surfaces side by side and auto-stacks wide
desktop/browser frames vertically; never hand-build a side-by-side
wireframe layout in custom-html. For document-body
comparisons, there is no other multi-column primitive — columns plus the
diff block are the whole comparison vocabulary. Do not hand-build side-by-side
layouts in custom-html, and do not stack two data-model blocks vertically
and call it a comparison when columns exists to put them side by side.
Grounding Rule
Structured blocks are true by construction only if they are derived from the
actual changed lines. The diff, data-model, api-endpoint, and file-tree
blocks MUST be built mechanically from the real diff — real paths, real fields,
real method/path, real before/after text — never inferred, rounded, or invented.
The model writes only the prose: the "why", the narrative, the risk read. A
confidently wrong recap is dangerous in a review context, because a reviewer who
trusts the summary may skip the very line the summary got wrong. When the diff
does not contain a fact, leave it out rather than guess; mark anything the model
inferred (not extracted) as inferred in prose.
Security
Gate visibility. Recaps of a private repo are org/login-gated — set the plan's visibility to the owning org or login, never auto-public. A recap can expose unreleased schema, internal endpoints, and architecture; treat it like the source it summarizes. Any PR comment or handoff that links to the recap must say that private-repo recaps require signing in with access to the owning org if the link does not load.
Never transcribe secrets. A diff can contain API keys, tokens, webhook URLs, signing secrets,
.envvalues, or credential-looking literals. Do not copy any of these into adiff,file-treesnippet,api-endpoint, or prose block — redact them (sk-•••,<redacted>). This mirrors the repo's hardcoded-secret rule: obviously fake placeholders only, never the real value, in any block, caption, or note.
Bidirectional Loop
In hosted mode, because a recap is a real, editable plan, the same review loop
as forward plans applies: a reviewer can annotate any block, and the coding
agent reads get-plan-feedback to drive fixes back into the code — annotation →
agent → diff, the same close-the-loop flow forward plans use. After a reviewer
annotates a block, call get-plan-feedback to read the structured feedback,
then either update the recap with create-visual-recap (passing the existing
planId to replace it in place) or apply targeted changes with
update-visual-plan. The loop is live and wired. In local-files privacy mode,
do not call those hosted tools; read review notes from chat or local files, edit
<plan-dir>/*.mdx directly, and rerun plan local check, serve, or verify
for <plan-dir>. The one thing not yet automatic is PR-comment-triggered
re-runs: the GitHub Action creates an initial recap per PR, but it does not yet
re-run automatically when new review feedback is posted in GitHub — that
auto-re-run is the remaining fast-follow.
Related Skills
visual-plan — the canonical command and the source of the shared Wireframe & Canvas and Document Quality cores; a recap follows the same block discipline in reverse.
comment anchors — recap comments use the same anchor rules as forward plans; see "Interpreting comment anchors" in the visual-plan skill for coordinate frames, wireframe node ids, text-quote resolution, detached threads, routing via
resolutionTarget, and two-axis consumed/resolved state.security — data scoping, secret handling, and the hardcoded-secret rule the recap's redaction and visibility gating mirror.
sharing — org/login-gated visibility for the plan that holds the recap.
Files
- ATTRIBUTION.mdAdded by AutoGineer849 B
- LICENSELicence file added by AutoGineer1.0 KiB
- README.md4.1 KiB
- SKILL.md32.8 KiB
- references/connection.md2.9 KiB
- references/local-files.md6.4 KiB
- references/wireframe.md19.0 KiB
references/wireframe.md
HTML wireframe quality — single source of truth
This file is the canonical quality bar for HTML wireframes / <Screen> /
WireframeBlock content, shared word for word by /visual-plan and
/visual-recap. Read it in full before authoring ANY wireframe; do not
author wireframes from memory or paraphrase these rules per command.
<!-- SHARED-CORE:wireframe-quality START -->
A wireframe is an HTML mockup. The renderer owns the look; you write the
content. Set data.html to a self-contained, semantic HTML fragment of the
screen and set data.surface. The renderer owns the surface footprint/aspect,
the dark/light theme, the hand-drawn font, and the rough.js sketch overlay — you
never write <html>/<body>/<script>/<style> tags or any
width/height/coordinates. You write real HTML layout and real product
content; the renderer styles and roughens it.
A wireframe block's data is an HTML screen plus a surface:
{
"surface": "browser",
"html": "<div style=\"display:flex;flex-direction:column;gap:10px;padding:16px;height:100%\"><h1>Sign in</h1><p class=\"wf-muted\">Use your work email to continue.</p><div class=\"wf-card\" style=\"display:flex;flex-direction:column;gap:10px\"><label>Email<input value=\"[email protected]\" /></label><label>Password<input value=\"••••••••\" /></label><label style=\"display:flex;align-items:center;gap:8px\"><input type=\"checkbox\" checked /> Remember me</label><button class=\"primary\">Sign in</button></div><a href=\"#\">Forgot password?</a></div>"
}Write PLAIN semantic HTML and let the renderer style it. Bare elements
(h1/h2/h3, p, button, input, <input type="checkbox">, a, hr)
are auto-themed — no classes needed. Helper classes carry the rest:
.wf-card/.wf-box— a bordered, padded container (a panel, a list item)..wf-pill/.wf-chip— a rounded tag or filter; add.accent(<span class="wf-pill accent">) for the accent-filled variant..wf-muted— secondary/muted text (or use<small>).button.primaryor any element with[data-primary]— the accent-filled primary button.
No decorative shadows around mockups. Do not put box-shadow, filter: drop-shadow(...), Tailwind shadow-* classes, or other fake depth effects on a
wireframe frame, root container, .wf-card / .wf-box, or canvas artboard.
Mockups should read as flat, bordered surfaces; use spacing, borders, labels,
and annotations for separation. Only show a shadow when the real product UI
already has that shadow and it is essential to the change being reviewed.
Use renderer icons, not visible icon words. For icon-only buttons or leading
icons inside fields, chips, menu items, and toolbars, write an empty marker such
as <span data-icon="mail" aria-label="Email"></span> or
<i data-icon="lock"></i>. The renderer replaces it with a Tabler-style SVG and
the .wf-icon class sizes it to the surrounding text. Supported names and
aliases: mail/email, lock/password, search, plus/add, x/close,
check, chevronDown, chevronUp, chevronLeft, chevronRight, dots/more,
chevron/caret/dropdown (down chevron), user, settings, calendar,
bell, send, edit, arrowLeft, and arrowRight. Do not put visible words
like "email", "lock", "search", "chevron", or "more" where the product UI would
show an icon; use text only when it is a real label a user would read.
Use the --wf-* tokens for any custom color, never hex. The renderer flips
these on light/dark, so reading them is what keeps a mockup correct in both
themes. For any inline border, background, or text color, reference a token:
style="border:1.4px solid var(--wf-line)". The tokens are --wf-ink (text),
--wf-muted (secondary text), --wf-line (borders/dividers), --wf-paper
(page background), --wf-card (container surface), --wf-accent /
--wf-accent-fg / --wf-accent-soft (brand action), --wf-warn, --wf-ok,
and --wf-radius. Never hard-code a hex color and never set font-family — the
renderer owns the sketch/clean font.
Never use host/Tailwind theme classes in wireframe HTML. Classes such as
bg-white, bg-zinc-50, bg-slate-950, text-zinc-950,
text-slate-400, border-zinc-200, hover:bg-slate-800, shadow-xl,
or arbitrary color utilities like bg-[#fff] leak the host app's CSS into the
mockup and can make dark-mode canvas frames unreadable. Use bare semantic
elements, .wf-* helper classes, and --wf-* color tokens instead. Before
publishing, scan every wireframe class and style attribute: if a class sets
background, text, border, ring, fill, stroke, gradient, placeholder, decoration,
or shadow color, rewrite it to renderer tokens or remove it. Layout-only classes
are still discouraged; inline flex/grid styles are safer and easier to review.
Keep Rough.js sparse. The renderer sketches the outer frame, standard
.wf-* primitives, controls, and inline border dividers by default. Do not add
data-rough to broad root wrappers, dialog shells, page panels, grid cells, or
nested containers unless that single container is the visual point. Use
data-rough only for a deliberate one-off shape. If a mockup starts looking
like stacked/overlapping sketch lines, remove rough targets from parent
containers and let backgrounds plus spacing separate the surfaces.
Use literal CSS lengths for spacing. The --wf-* tokens are for colors and
renderer-owned visual styling, not layout spacing. Do not use guessed spacing
tokens such as var(--wf-space-4), Tailwind spacing classes, or theme spacing
variables inside wireframe HTML; if a token is unavailable in the Plan renderer,
padding collapses and content hugs the border. Use explicit CSS lengths for
layout: padding:16px, gap:12px, margin-top:18px, minmax(0,1fr).
Lay out with inline style flex/grid. You write the real layout —
display:flex; flex-direction:column; gap:10px; padding:16px and so on — and the
renderer never repositions anything. Compose the actual product: reproduce the
current screen, then show the modification. Real labels, real counts, real dates,
real button text grounded in the screen you read; not lorem or gray bars.
Surface presets — match the real footprint, never default to desktop+mobile.
Pick the surface that matches what the user will actually see:
browser: a web page that needs a browser chrome frame around it.desktop: a full desktop app page or app shell.mobile: a phone screen, only when the work is genuinely mobile.popover: a small floating menu, dropdown, or inline popover.panel: a side panel, inspector, or sidebar widget.
A sidebar popover renders as a small surface, not a desktop page and a phone
frame. Do not emit desktop + mobile variants unless responsive behavior
actually changes the layout. For a component or widget, show one broader
app-context frame only when placement affects understanding, then the focused
component states.
Model the actual component shell for small surfaces. A rendered UI change
belongs in a wireframe; reserve diagram for architecture, dependency, state,
or data-flow relationships. Popovers, dropdown menus, command palettes, and
context menus use surface: "popover" unless the surrounding page placement is
the point of the change. Dialogs, sheets, inspectors, sidebars, and long
property panels use the matching panel / desktop surface as appropriate.
Show the real chrome: trigger or anchor when it matters, title/header row,
top-right actions, separators, fields, options, selected states, body content,
and footer actions that are visible in the workflow.
Modify, don't redesign. When the task changes an existing screen, reproduce the current screen's real layout and footprint FIRST, then change only the delta and call it out with a single annotation. Do not restack the page into a new layout. For net-new surfaces, compose from the real app shell. Inspect the actual app components before drawing an existing product: sidebar density, toolbar actions, overflow menus, property panels, and framework chrome should match the product unless the plan intentionally changes them.
Keep product screens pure. A product wireframe shows the app state a user would actually see. Do not embed file contracts, architecture arrows, repo pills, mode explanations, or implementation callouts inside the screen just to explain the plan. Put those in canvas annotations, a separate diagram, or the document body. Secondary UI such as properties, history, sync, export, or agent controls should appear where the real product would put them: an overflow popover, sheet, panel, or separate framework sidebar state, not a generic permanent right inspector unless that inspector is the actual design.
Classify mockup scope before implementation. Before turning a plan mockup into source code, decide whether each artboard represents the whole page/app shell, a route body inside an existing shell, or a component/sub-surface. If an artboard includes navigation, sidebars, auth banners, or a signup/login form, map those pieces to the real shared shell/auth components instead of nesting the entire mockup inside the current page. When a mockup references the product's standard signup/login page, find and reuse that existing implementation; do not approximate it from the wireframe.
Zoom in on sub-surfaces, don't redraw the page. For a small sub-surface (a
popover, menu, dialog, toast), show the full screen once, then add a small
separate artboard whose html contains ONLY that sub-surface — do not re-draw
the whole page around it, and do not scale a duplicate up. Pick the matching
surface (e.g. popover) so the footprint is right; never widen a popover to
page width.
Loading / skeleton states. Set data.skeleton: true on the wireframe and
fill the html with neutral, textless placeholder geometry — boxes and bars
built as <div>s with background:var(--wf-line) and explicit heights/widths,
no labels or copy. The renderer drops borders, sketch, and color into the
skeleton register automatically. Never escape to a custom-html document block
to fake a loader.
Editing an existing mockup. In hosted mode, to change one element, text, or
color in an existing html mockup, do not regenerate the frame — call
update-visual-plan with
contentPatches: [{ op: "patch-wireframe-html", blockId, edits: [{ find, replace }] }]. Each find is a unique snippet of the current html (read it
first with get-visual-plan); set all: true on an edit to replace every
occurrence. The result is re-sanitized. In local-files privacy mode, do not call
hosted Plan tools; edit the local MDX source directly and rerun the local
check/serve or verify command for <plan-dir>.
Choose the outer frame deliberately. Wireframe and diagram data accept
frame: "auto" | "show" | "hide" in block data (<Screen frame="hide"> in
MDX wireframes, <Diagram frame="hide"> for MDX diagrams). Leave it unset or
auto when the host context should decide: Plan and recap surfaces default to a
drawn outer frame; docs surfaces default to no outer frame. Use show for
standalone product screens, before/after recap comparisons, screenshot-like
artifacts, and visuals that need containment from surrounding prose. Use hide
when a docs page, tab, column, card, canvas artboard, or the visual's own
internal chrome already supplies the boundary. Do not use hide to compensate
for cramped content; fix the layout instead.
Inner padding and borders still matter. Always wrap HTML wireframe content
in a root container with real inner padding before drawing cards, fields, pills,
labels, or controls. Use at least 14-16px of padding, box-sizing: border-box,
height: 100%, and gap between child rows on the root node itself so the
first row never sits flush against the screen edge. Do not rely on padding on a
nested page section as the first visible inset; the outermost element must
create the breathing room. Keep text away from borders: every container, field,
button, menu item, and annotation needs enough padding and line-height to read
cleanly in the rendered Plan view.
Center page-like content inside broad surfaces. For browser or desktop
screens, let the outer root provide the full-width frame and padding, then keep
the page body in a centered wrapper such as
width:100%;max-width:720px;margin-inline:auto. Full-width app bars may span
the root, but onboarding, auth, settings, and other page-like content should
not hug the left border unless the real product does.
For feature-cloud or abundance visuals, optimize the composition over line-by-line reading. Some marketing/product sections need to feel like a large surface area of capability rather than a precise app workflow. In those cases, use one padded root with a short headline and a dense, aesthetic cloud of short feature labels, chips, rings, or columns. Vary scale and opacity with tokens, cluster by meaning, and let many labels be glanceable rather than individually essential. Do not force dozens of features into equal cards with long wrapped sentences; that usually creates a messy unreadable mockup.
Lay out children safely so they never collide. Use HTML flex/grid with
gap, min-width: 0, and sensible overflow. Avoid negative margins, absolute
positioning, or fixed child widths that can collide when the renderer switches
between light/dark, sketch/clean, or different zoom levels.
Do not wrap intentionally single-line labels. For toolbars, tab rails,
breadcrumbs, chip/filter rows, branch and file names, file chips, and code
filenames — any deliberately single-line row — do not let long text wrap. Put
white-space: nowrap on the row (and overflow: hidden; text-overflow: ellipsis
on the individual labels that can grow), so the wireframe demonstrates the actual
layout behavior instead of producing ugly stacked or vertical text. Use
horizontally scrollable or clipped rails for overflow.
Fill the frame; keep labels short. Each artboard is a fixed-size surface — compose enough realistic HTML to fill it top to bottom with even vertical rhythm; never leave a large empty band. On desktop/app-shell sidebars, let the nav stack flex to fill (flex:1) and add any persistent bottom action/status after it so the rail reads complete in taller frames. On mobile especially, flow real rows down the whole screen (status bar, header, then list/detail content) rather than a header floating above a gap. Keep every label short enough to sit on one line within its column — shorten the copy rather than relying on the frame to absorb it (long labels wrap or clip).
Persistent chrome bars span the full frame width. Top bars, app headers,
toolbars, and bottom tab/nav bars are full-width chrome, not centered content.
Lay each one out as a single flex row that fills the frame
(style="display:flex;align-items:center;width:100%") and push trailing actions
to the right edge with a flex spacer (<div style="flex:1"></div>) between the
leading group and the trailing group — never center a bar inside a narrow,
centered block, and never let it collapse to the width of its contents. In a
Before/After pair the bar stays full-width in BOTH states even when one state has
fewer controls; the spacer absorbs the difference so the remaining controls hold
their edge alignment instead of sliding to the center.
Pin bottom bars to the bottom of the frame. For mobile tab bars, footers, and
any persistent bottom action row, make the frame itself a flex column at
height:100% (style="display:flex;flex-direction:column;height:100%"), give the
scrolling body flex:1 so it absorbs the slack, and place the bar as the LAST
child of the frame (or set margin-top:auto on it). The bar then sits flush at
the bottom of the surface instead of floating directly under the content with an
empty band beneath it.
Before / after must be comparable. When showing a state change, preserve the
unchanged controls in both states so the reviewer can see exactly what moved or
appeared; do not show an added control as a generic box floating elsewhere in
the surface. Place the new/changed affordance where the implementation puts it —
for example, a new Edit with AI action in a popover header belongs in the
top-right header slot, aligned with the title, not in the body or footer. Use
the same frame size, scale, outer padding, border radius, and visual density on
both sides unless the change itself alters those properties, and let the frame
height fit the content rather than leaving a tall empty lower half.
Name the states with the column header, never inside the frame. For
document-body wireframes (recaps), put the two
states in a columns block and set each column's label to Before and
After — the renderer draws that label as an h4 heading above each frame. Do
NOT bake a Before/After pill, title, or heading into the wireframe html: a
label placed inside reads as part of the product UI, lands in a random corner,
and clutters the comparison. The column header is the one and only place the
state name belongs. On a canvas, place the two state artboards as neighbors with
frame labels — never encode Before/After inside the html.
Let the surface choose side-by-side vs. stacked. For document-body
wireframes (recaps), the columns renderer lays
narrow surfaces (mobile, popover, panel) out side by side, and
automatically stacks wide surfaces (desktop, browser) vertically at full
document width so a large frame is never crushed into a half-width column and
cropped. Author both wireframes with the real surface and the matching
Before/After column labels; do not hand-stack the pair into separate
top-level wireframes or duplicate the state name as body content.
Good example — a contacts list, surface browser. A small, real screen
composed from the helper classes and tokens, layout in inline flex, no fonts or
hex colors:
<div
style="display:flex;flex-direction:column;gap:12px;padding:16px;height:100%"
>
<div style="display:flex;align-items:center;justify-content:space-between">
<h1>Contacts</h1>
<button class="primary">New contact</button>
</div>
<div style="display:flex;gap:6px">
<span class="wf-pill accent">All 128</span>
<span class="wf-pill">Favorites</span>
<span class="wf-pill">Archived</span>
</div>
<div
class="wf-card"
style="display:flex;flex-direction:column;gap:0;padding:0"
>
<div
style="display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1.4px solid var(--wf-line)"
>
<div
style="width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)"
></div>
<div style="flex:1">
<strong>Jane Cooper</strong><br /><small>[email protected]</small>
</div>
<span class="wf-pill">Lead</span>
</div>
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px">
<div
style="width:32px;height:32px;border-radius:999px;background:var(--wf-accent-soft)"
></div>
<div style="flex:1">
<strong>Marcus Lee</strong><br /><small>[email protected]</small>
</div>
<span class="wf-pill">Customer</span>
</div>
</div>
</div><!-- SHARED-CORE:wireframe-quality END -->
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server, Needs an API key, Needs Node.js
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Builder.io
- Publisher
- Builder.io (Official)
- Source
- BuilderIO/skills · skills/visual-recap · commit f4f10b8
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Builder.io or Builder.io.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download visual-recap.zip