Getting started
Install once, check your machine, then make your first plan, in an empty folder or in a project you already have.
Backbase is for EF teams: installing it and the source links need access to the ef-global GitHub organisation.
Install once
Do these in order. The last step, bb doctor, checks what Backbase needs and says how to fix what’s missing.
All the commands, for when you know the steps:
claude --version
node --version
git --version
gh auth login
gh auth setup-git
# Step 4 first: GH_PACKAGES_TOKEN in your shell profile, and ~/.npmrc
npx -y @ef-global/backbase --version
claude mcp add --transport http --scope user figma https://mcp.figma.com/mcp
claude mcp login figma
# Then /mcp in Claude Code to sign in to Storyblok (step 6)
claude mcp add --transport http --scope user Storyblok https://mcp.storyblok.com/mcp
claude plugin marketplace add ef-global/backbase
claude plugin install backbase@backbase
npx -y @ef-global/backbase doctor
1. Claude Code
Install it with the Claude Code setup guide, then start it once and sign in.
claude --version
Why
Backbase is a Claude Code plugin. The agent that reads the design and writes the plan runs in your Claude Code session.
2. Node 26 (LTS) and git
node --version
git --version
git needs your name and email: git config --global user.name and user.email.
Why
Use Node 26, the current LTS. bb create clones the boilerplate and makes the project’s first commit with git.
3. GitHub access to ef-global
gh auth login
gh auth setup-git
gh auth status
Why, and SSH
The plugin installs from the ef-global/backbase repository, and the boilerplate lives in ef-global too.
bb create clones over SSH first. Without a key GitHub accepts, it clones over HTTPS with the sign-in gh set up, or any other credentials git has: gh auth setup-git lets git use gh’s. A missing SSH key is only a warning. To check yours:
ssh -T git@github.com/backbase:upstream uses the same sign-in to clone ef-global/backpack, file its issue and open its pull request, so it needs your access to that repository too.
4. GitHub Packages for @ef-global
Create a GitHub personal access token (classic) with the read:packages scope. If ef-global uses single sign-on, authorise the token for it. Export it in your shell profile (~/.zshrc or ~/.bashrc):
export GH_PACKAGES_TOKEN=<token with read:packages>
Add these two lines to ~/.npmrc in your home folder, with your token in place of the placeholder:
@ef-global:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=<token with read:packages>
Open a new terminal, so it has the token, and check that npm can reach bb:
npx -y @ef-global/backbase --version
Never paste a token into a chat with an agent. Put it in your shell profile and
~/.npmrcyourself.
Why
Backpack and bb are published to GitHub Packages, not to the public npm registry. The boilerplate’s .npmrc reads GH_PACKAGES_TOKEN to install them, and ~/.npmrc lets npx run bb from any folder, which the plugin’s widget server, bb mcp, needs too. Start Claude Code from a terminal that has the token.
5. Your Figma connection
claude mcp add --transport http --scope user figma https://mcp.figma.com/mcp
claude mcp login figma
Or install the Figma plugin instead: claude plugin install figma@claude-plugins-official.
Why
The agent reads the frame through your own Figma account. Backbase doesn’t bring a Figma connection of its own. Sign in from the terminal, as above: it’s more reliable than signing in through /mcp in the desktop app.
6. Storyblok (needed from Gate 2)
claude mcp add --transport http --scope user Storyblok https://mcp.storyblok.com/mcp
Then run /mcp in Claude Code, choose Storyblok and sign in, as Storyblok’s setup guide says. Grant access to the spaces you work in.
sb-mig also needs a Storyblok personal access token, STORYBLOK_OAUTH_TOKEN, in the project’s .env.
Why, and where the token goes
Exporting STORYBLOK_OAUTH_TOKEN in your shell profile works too, but a shell export overrides the project’s .env. With it exported, bb create writes it to the new project’s .env. See Setting the Storyblok token.
The agent uses the Storyblok MCP to look up your space’s presets, and /backbase:compose uses it to read the space and upload the page’s images. sb-mig uses the token at Gate 2, when /backbase:sync writes the project’s components to the space, and bb story push uses it to write the draft page. Up to Gate 1 nothing writes to Storyblok, so both can wait. bb doctor --write checks them before a write.
7. The plugin
claude plugin marketplace add ef-global/backbase
claude plugin install backbase@backbase
What it brings
The /backbase:new, /backbase:analyze, /backbase:sync, /backbase:compose, /backbase:edit, /backbase:refine, /backbase:component and /backbase:upstream commands, the skills the agent follows, the write guard that checks every write to Storyblok, and bb mcp, the MCP server that shows the plan as a widget where the app can (see Seeing the plan as a widget).
8. Check your machine
npx -y @ef-global/backbase doctor
It lists every check, and under each one that fails or warns, the exact steps to fix it: the same steps as in Troubleshooting.
- A failure is something Backbase can’t work without.
bb doctorexits with 1, and/backbase:newstops before it makes anything. Fix it and runbb doctoragain until nothing fails. - A warning is something to know about, and it doesn’t stop you.
It shows whether a token is set and whether it works, never the token itself.
Before anything writes to Storyblok, run bb doctor --write in the project. It’s stricter: the token, project and space checks have to pass, and a warning or a skip counts as a failure. It also sets the project up for Chaperone: it pins bb as a dev dependency and installs it, and adds the preset, component-map.exceptions.json, .prettierignore and .claude/launch.json when they’re missing. See bb doctor –write.
Every check bb doctor runs
| Check | What it looks at | When it isn’t right |
|---|---|---|
node |
Node 20 or later | Fails |
git |
git is installed | Fails |
bb-binary |
Any bb on your path is Backbase’s, not Babashka’s |
Warns. No bb at all passes: the plugin runs it through npx |
github |
gh is signed in to GitHub, or GH_TOKEN or GITHUB_TOKEN is set |
Fails |
github-ssh |
GitHub accepts this machine’s SSH key | Warns: bb create clones over HTTPS instead |
packages-token |
GH_PACKAGES_TOKEN is set, and GitHub Packages accepts it |
Fails |
figma-mcp |
Local Figma connector or plugin detection; access is checked when the frame is read | Passes if present; skips neutrally if unseen. Omitted under --write |
storyblok-token |
STORYBLOK_OAUTH_TOKEN is set in your shell, or covered by the project’s .env |
Warns if neither supplies it: only a write needs it |
page-browser |
bb page measure and compare have a browser: the project’s own Playwright, or bb’s Chromium in bb’s cache |
Warns without bb’s Chromium: run bb page browser install |
Run in a project, it checks the project and its Storyblok space too. Outside a project, these checks skip, each with one line saying why:
| Check | What it looks at | When it isn’t right |
|---|---|---|
project-backpack |
Backpack 4 is installed in node_modules |
Fails: run npm ci |
project-chaperone |
.chaperone.json extends Backbase’s preset with Chaperone 0.12.0 or newer, component-map.exceptions.json is there, and bb is a pinned dev dependency, installed, so Chaperone can load the preset and run its rules |
Fails: run bb doctor --write. Warns when the installed bb isn’t the one running |
project-prettierignore |
.prettierignore ignores .next, .turbo and node_modules |
Warns: run bb doctor --write |
project-launch |
.claude/launch.json has the project’s dev server (npm run dev on port 3000, over https, in the boilerplate), so Claude’s preview pane can start the site |
Warns: run bb doctor --write. Skips when the dev script has no port bb can tell |
catalog |
A saved .backbase/catalog.json was compiled from the Backpack and the schemas the project has now |
Warns when it’s stale, with the command that rebuilds it. Passes when there’s none |
project-storyblok-token |
sb-mig can find STORYBLOK_OAUTH_TOKEN, in the project’s .env or your shell |
Fails. Warns when your shell sets a different token, which overrides .env’s |
project-env-ignored |
git ignores .env, which holds the token |
Fails |
space-id |
The project has a space id: --space, then spaceId in storyblok.config, then space.id in .backbase/config.json, then STORYBLOK_SPACE_ID in your shell or project’s .env |
Fails |
space-access |
The token can read that space: one read-only request, to the Storyblok API sb-mig uses | Fails |
project-translation-strategy |
NEXT_PUBLIC_TRANSLATION_STRATEGY in .env (none if unset) |
Warns for folder: compose must write under the locale folder |
space-settings-story |
A settings story at the root (none/field) or in each configured locale folder (folder) |
Warns with steps to create it in Storyblok; never blocks a write |
space-field-plugins |
Each field plugin the project’s components use is in the space: used by a component there, or assigned to it | Warns only on positive evidence one is missing, naming component, field and plugin, with steps to install it or leave the component out at Gate 2; never blocks a write |
storyblok-mcp |
Claude Code has a Storyblok plugin enabled, or a connected Storyblok server | Warns: a claude.ai connector can’t be seen from the command line |
A project is a folder whose package.json depends on @ef-global/backpack, or that has a .backbase/config.json.
A missing SSH key is a warning: bb create clones over HTTPS. A neutral skip on figma-mcp means doctor cannot detect your connector and adds the hint “Add a Figma connector in your agent’s settings and sign in.” Doctor says “Figma: checked when the frame is read”, including when a visible local MCP needs authentication. Your claude.ai connector can still work; the analyse flow checks access when reading the frame.
Start from scratch
You have a Figma frame and no project yet, and you’ve done Install once.
-
In Figma, select the frame and copy a link to it (right-click, then Copy link to selection). The link needs a
node-id. -
Open Claude Code where the new folder goes:
cd ~/projects claude -
Run
/backbase:newwith the link and a folder name:/backbase:new <figma frame link> my-site
The folder is optional: without it, the agent suggests one from the frame’s name and asks. That’s the only question before Gate 1. If you’ve created a Storyblok space for the site, add --space <space id>: it’s recorded in .backbase/config.json, marked as a sandbox.
The agent checks your machine, makes the project, reads the frame and plans it. It stops at Gate 1 with the plan committed. Nothing is written to Storyblok or Figma.
Each step in detail
- Doctor. The agent runs
bb doctor. On a failure it shows each failing check with its fix and stops. Warnings don’t stop the run, and go in the report. - The folder. It uses the folder you named, or asks for one. If the folder exists and isn’t empty, it stops and asks. It never overwrites anything.
- Create. It runs
bb create, which takes a few minutes: it checks the folder, then the machine (node and git, and your packages token right before the install), clones the boilerplate (over SSH, or HTTPS when SSH fails), names the package after the folder, installs, pinning bb as a dev dependency, adds Backbase’s files (the Chaperone preset,.prettierignoreand.claude/launch.jsonamong them) and makes the first commit. It says in a line when it’s done, with each step in the report. If a step fails, it shows the step’s message and its fix, and stops. - Analyse. In the new project, it runs the same flow as
/backbase:analyze: it checks Figma, reads the frame part by part, loads the project’s catalog, classifies each part and hasbbcheck the plan until it passes. - Gate 1. It commits the plan, then shows you its card and stops. The full plan is on the report page,
.backbase/report.html, with a picture per section, and its table of buckets, confidence, design fit and questions is in.backbase/reports/gate-1.md. The design fit says whether each part’s blocks can reach the frame’s look with their design options: the card names what Backpack can’t do and counts the close matches, and you accept them or pick another approach. See Design fit at Gate 1.
The report starts with What you have now: the folder, the boilerplate commit it started from, the project’s two commits (the boilerplate, then the plan), and that nothing was written to Storyblok or Figma.
You can run bb create yourself too, then /backbase:analyze in the new folder:
npx -y @ef-global/backbase create my-site --space <space id>In a project you already have
Your project has Backpack 4 in it, and you’ve done Install once.
-
Install its dependencies, even if you’ve worked in it before. The run needs
node_modules/@ef-global/backpack, version 4:cd my-site npm install -
Open Claude Code in the project and run
/backbase:analyzewith the frame’s link:/backbase:analyze <figma frame link>
To analyse for a project in another folder, add --project <dir>. The agent checks, reads the frame, plans it and stops at Gate 1, as above.
Each step in detail
The agent plans against your project: the Backpack version it has installed and the bloks it defines. Backbase supports Backpack 4 only.
- Checks. The agent checks that Figma is connected, that the folder is a Backpack project and that
bbruns. If one fails, it tells you how to fix it and stops. In a folder without Backpack, it points you to/backbase:new. - Reading. It reads the frame part by part and takes screenshots of it, then reads how each part looks: its auto-layout, colours, radii and typography, the design tokens behind them, its components’ variants, its icons and its images. A long page takes a few minutes.
- Classifying. It loads your project’s catalog and compiles its knowledge pack,
.backbase/knowledge.md: what each blok can hold and look like. Then it decides a bucket for each part, and hasbbcheck the plan until it passes. - Gate 1. It commits the plan, then shows you its card and stops. You accept what Backpack can’t do and the close matches, or pick another approach; gaps never block. The report,
.backbase/reports/gate-1.md, has the whole plan: when a blok in the plan isn’t in your component map, it says so once there, and what that means, and for every Wrap or Create it shows what was tried, why existing blocks don’t fit, and the placeholder to fill in the final component stage.
What the agent tells you
Every message has the same three parts:
- Where you are: what’s done, and whether it needs you.
- Your call: only the choices you have to make, numbered, each with the answer the agent recommends. Reply yes to take them all, or name what to change, such as “2: no”. A question with no recommended answer needs your own words, by its number (“7: live”), and the step waits for them.
- The details, on the report page it links,
.backbase/report.html. Ask for a part (“show section 6”) and it shows just that.
At Gate 1, a yes takes only the answers the card named. Each new component needs an explicit yes from you. What you approve is never reworded: an approval sentence such as Approve plan 1a2b3c… is quoted as bb gives it, and you reply with it.
An example: Gate 1 on the ILS frame
Questions the agent can’t recommend an answer to need yours: it lists the ones that change what gets built first, numbers the rest in the report, and the plan waits until each has an answer:
Plan ready for "Package (ILS)": 14 sections. Nothing is written yet.
8 built from existing blocks, 3 need new components, 2 from the site settings, 1 I couldn't place.
Your call:
1. New components from Backpack's React: campus day selector, fan stack and testimonial card. Placeholders now, built after the page is up. OK?
2. 96px padding and 3px gaps aren't in Backpack: I'll use 128px and 4px, and ask GCS. OK?
3. I'll set lavender, pale blue, peach and pink as theme colours at the space step. OK?
4. 6 places match better with another block: I'll switch them unless you say no. OK?
5. Backpack can't do 15 things yet, like rounded corners: I'll ask GCS. 34 more are close matches. OK?
6. I couldn't place "Live the language": Does Backpack support gradient text? Your answer?
7. Header and Footer: The master starter has no header or footer; the new-blok fork does. Which one does this site use? Your answer?
8. Package hero and Highlight package: Is the price typed by editors, or live data? The hero shows $811/week and the floating price bar $1,100/week. Your answer?
9. Sticky bar: The doc doesn't map the floating price bar; the key plans it as the sticky promo bar block next to the nav bar. Is that right, and is the price live data? Your answer?
10. What's included: The same local component is used twice (What’s included, Customization options): make it a saved layout. Your answer?
11. Package campus: The Google rating has no Backpack component: live or static? Your answer?
12. Other packages: Hand-picked cards, or a live list of trips? Live would make it a Create. Your answer?
13. The page: Do designers usually deliver mobile page frames, or only component variants like here? Your answer?
7 more questions: 14–20, in the report.
Answer by number ("6: …"), and yes to the rest: the plan waits for every answer.
The full plan, with a picture per section: .backbase/report.html.How it words things, and where the details go
It uses plain words: “built from existing blocks” rather than Compose, “a placeholder until it’s built” rather than a slot, “a saved layout” rather than a preset. A message is about 150 words, plus any questions only you can answer, and while work runs it says one line per stage at most. An error says what happened, what it means, and the one thing to do.
The agent runs bb report at each step, which draws the plan, the space, the push and the review with a picture per section (see Seeing it as a page). What the page doesn’t draw, such as bb’s own output and every warning, is in a report file for each step: .backbase/reports/gate-1.md for the plan, then gate-2.md, push.md, review.md and done.md.
After Gate 1: design requests
After you confirm, the agent offers drafted design requests in .backbase/requests/design-gaps.md, alongside optional Wrap and missing-primitive requests recorded in plan.json. A spacing, padding, gap or radius the frame has that Site Builder has no step for, like the ILS frame’s 96px section padding between xxl (64) and xxxl (128), is written as the nearest step and never silently: the plan names each one, 96px → xxxl (128), +32px, requested, and it’s one request for the value, with every section that uses it. An icon the frame uses that EF’s icons/bold set doesn’t have, such as a brand logo (Icon: google), is a request too. Palette mismatches go to design and Backpack together. The requests go to GCS, who make Site Builder, as issues in ef-global/backpack: /backbase:upstream requests files them, each only after your yes, and filing doesn’t hold up the next step. See Drafting design requests.
Gate 2: sync the space
Once you’ve confirmed the plan, the project’s Storyblok space needs its components and datasources before a page can use them. Run, in the project:
/backbase:sync
The agent runs bb doctor --write, plans the write and shows you every change: components, datasources, presets, the frame’s palette as the space’s theme, and each destructive change by name. Nothing is written until you say yes to that plan. The sync only creates and updates: nothing ever runs sb-mig with --ssot, which would delete whatever the code doesn’t have.
- Close the Settings story in Storyblok before you sync, and reopen it after. A Settings tab opened before the sync holds the old colours, and saving it would bring them back.
- The theme is saved in Settings as a draft. Publish it when you’re happy. bb never publishes it, so until you do, the published site keeps its old colours.
Each step in detail
- Doctor. The agent runs
bb doctor --write. When the project isn’t ready, it shows what to fix and stops. It never asks for your token. - The plan. It runs
bb space plan, which has the project’s own sb-mig plan the write without writing anything: datasources first, then components. sb-mig has to be6.5.0-beta.25or later. With the plan you confirmed at Gate 1, it passes the frame’ssections.jsontoo, and bb plans the frame’s palette as the space’s theme (below). - Gate 2. It shows you the space, what would be created and updated, the presets it would create and overwrite, the theme, every destructive change on its own line, and the plan’s fingerprint. A destructive change can lose content already in the space, such as a removed field, so you approve each one by name. For destructive changes, copy the exact approval sentence it gives you; one reply names the fingerprint and all the changes.
- Missing field plugins. bb reads the field plugins listed for your account and organisation, and names each component whose schema needs one that’s assigned to other spaces but not yours: the component, the field and the plugin, such as
sb-submit-button’sdescription. sb-mig would fail on it, so the agent offers two ways round it: install the plugin in the space, then see a fresh plan, or leave the component out. A plugin bb can’t verify is only a note. When a sync fails because Storyblok says a plugin is missing, the agent names it and offers the same two ways. - Leaving a component out. Say so, with your reason, and the agent plans again with
bb space plan --exclude "<component>=<your reason>". The plan lists the component and your reason, its fingerprint changes, and the sync writes everything else and leaves that component as the space has it.
- Missing field plugins. bb reads the field plugins listed for your account and organisation, and names each component whose schema needs one that’s assigned to other spaces but not yours: the component, the field and the plugin, such as
- The write. It runs
bb space sync --approved <fingerprint>, with the same--excludes, and Claude Code asks you to allow it, showing what it writes and where. bb plans again first, and writes only when nothing changed since you approved. Otherwise it refuses, and you see the new plan. The theme depends on the colour datasources, not on the components, so a component that fails never holds it back: the theme is still written, and the failure is reported next to it, with the offer to leave that component out next time. - The report. What was created and updated in the space, per kind, what failed, the theme colours written, and the Settings story saved.
- Settings, when the theme changed. The site renders the theme from the Settings story’s
theme.selected. After the theme’s writes, bb saves it there as a draft, rebuilt exactly as thebackpack-themeplugin rebuilds it, so the colours show right away, locally and in Storyblok’s preview. Gate 2 showed you that change too, token by token. Storyblok keeps the last save, which is why you close Settings before the sync. The agent ends with this step, and it’s yours: publish the draft when you’re happy./backbase:composeends its hand-off with the same reminder. When bb didn’t save the Settings story (the space has none, or the save was skipped or failed), the agent says so, and you open Settings, check the colours, then save and publish it yourself.
How the theme is planned
Site Builder is meant to be re-themed per space, so Backbase reproduces a frame’s palette through the space’s theme, not colour by colour. In the ILS frame, ✱ System/Surface/Brand/Accent/3 is lilac, #e0d7f9, while Backpack’s default theme gives color-system-surface-brand-accent-3 Color/Red/Lightest (100). For each semantic colour the frame uses, bb compares the frame’s colour with what the space paints today and plans what makes them agree:
- the token’s value in the
userdimension ofcolors-theme-light, the theme configurator’s overrides:var(--color-<primitive>)when the frame’s colour is exactly one of Backpack’scolors, else the colour itself,#E0D7F9. A near miss is never rounded to the nearest primitive; - a
custom-colorsentry for each colour of its own,Package (ILS) · System/Surface/Brand/Accent/3, so the colour pickers offer it too. When the space has nocustom-colorsdatasource, the plan says so and the sync creates it first: nothing is dropped silently.
Backpack’s default values are never changed, and an empty user value resets a token to its default. But an override someone set in the space is lost when the theme replaces it: Storyblok keeps no copy. So Gate 2 lists each token with what it is today and what it becomes, and the approval sentence names each override it replaces, old value and new; after the sync, .backbase/space-sync.json records the old value, which is what restores it. The theme is space-wide: every page in the space takes it. A colour the frame paints translucent is left alone and listed, as the pickers have no opacity. Only the light theme is planned: the frame’s read has no dark-mode colours, so Backpack’s dark defaults stay. See The theme part.
Composing the page
After Gate 1 and Gate 2, the agent builds the page as a draft story in your space. You need the Storyblok MCP and the token from step 6.
/backbase:compose
Add --slug <slug> for the story’s slug; without it, the slug comes from the frame’s name.
The agent builds the story from the plan, fills it with the frame’s copy, images and design values, and checks it. A placeholder holds the place of each component that isn’t built yet, including bare section slots: a dashed box in dev and drafts, and nothing in published production content. When your dev server is there, it measures and reviews the page beside the frame, once, with your yes.
It asks you before it uploads images, and again before the push, which shows exactly what it will write. The story goes in as a draft. Nothing is published: you review the draft in Storyblok and publish it yourself.
- The push guard. In Claude Code, in a Backbase project, the plugin’s hooks block
bb story pushunless it’s the plan you approved, and ask you before it runs. - The write guard. On the Storyblok MCP it lets through only the approved image uploads and folder creation. It blocks publishing, asset replacement, updates and deletion, a write to another space, and any story content that didn’t pass
bb story validate --stamp, whatever the agent tries.
Both need node on your PATH.
Each step in detail
- Checks. The agent checks that the plan validates, that the space sync ran, and that
bb doctor --writepasses. If one fails, it tells you what to do and stops. Compose checks the bloks this plan uses, so a failure syncing an unused component doesn’t block the page. With folder translation it creates the draft in the default locale’s existing root folder and checks the full locale path for collisions. See compose preflight and locale folders. - Scaffold.
bb story scaffoldbuilds the story from the plan, offline: your project’s page with Reuse, Compose, Wrap and Create sections in order. Abackbase-slotreserves each missing component in its surrounding structure. Global and Unsure stay outside the page, with the reason in the report. See Scaffolding a story. - Fill. The agent fills the placeholders with the frame’s copy, every line of each section’s text by node id, as ops envelopes through
bb story ops apply, thenbb story fillfills the rest, and the empty fields the frame has text for, such as a button’s label, and drops the tags and eyebrows the frame doesn’t show. What the design doesn’t show stays a placeholder, starting[Backbase placeholder, and the report says why. See Filling a story. Original image fills are downloaded through your Figma MCP and matched to nodes withbb figma images match. The agent matches them to page slots and runsbb assets planto record the resolved project space and plan missing uploads by hash; nothing is uploaded yet. It also readsdesign.iconsor the screenshots to fill list, button and tag icons, using kebab names such asfork-knifefor Figma’sForkKnife. - Design. For each section, the agent reads its screenshot, its
designfromsections.json(the frame’s auto-layout, tokens, variants and icons) and the knowledge pack, and writes what Site Builder can set, one ops envelope per section: each blok’s variant, size and heading level, and its design values, asupdateDesignops, such as"changes": { "gap": { "s": "xl" } }, the gallery beside the price card on large screens, a surface’s radius, and its colours, as thecolorsentry whose value is the frame’s hex, or else the one Backpack’s theme maps the frame’s variable to. It uses only names the pack lists. Where the frame’sdesignstates a layout, it follows the frame, mapped to the nearest token; where the frame is silent or ambiguous, it follows the pack’s house style, how Backpack’s own presets are built, and its final report says which it used, per section. What it can’t express exactly, such as a colour nocolorsentry matches, it lists per section as approximations, in its final report, quoting the frame’s variable, its hex and the entry it wrote. What runs edge to edge in the frame, the plan’s full bleed, it writes as the plan gives it: the section grid’s bleed lines at the frame’s breakpoint and below, and an image’sviewport_size 100vw. - Materialize.
bb space datasourcesreads your space’s colour entries, read-only, thenbb story materializewrites the story as Storyblok’s editor would: every field each blok has, every design as the envelope the editor stores, for where the blok sits, with only the subfields that have a value, and every colour, size, link and section list in its stored format. It turns icon names into inline SVG selections, fetching EF’s manifest once and caching it in the ignored.backbase/cache/;--offlineuses the cache. Unknown names stay as written with closest-name warnings. See Materializing a story. - Spacing.
bb story spacing planturns each section’s auto-layout into the design values that render it, before anything renders: for each blok with a Figma layer, the field, the blok and the token that reproduce its gaps, padding, fill and hug, from what each value renders in px (item_spacingpads every row above and below, so 16px between rows isxs; a card body pads 24 by default). Each px rounds to the nearest token, and each change says the error. The agent applies the plan withbb story ops apply, in one journaled step, and lists what no field can express as approximations. See Planning the spacing. - Validate.
bb story validatechecks the story against your project’s schemas, counts the placeholders left, and reports the story’s size with its five heaviest sections. - Your yes. The agent checks the slug is free in the space and that the story could be pushed there, with
bb story push --plan, before any image is uploaded. Then it asks before uploading anything, with what’s left for you:Create draft story <name> at <slug> in space <name> (<id>) and upload M distinct images into From Figma / <frame name>? There are R reused images and U unresolved image slots. It has N placeholders left and K sections that need a space preset applied in the editor. images in Figma can be stock or placeholders; check their rights before publishing.If a story with that slug exists, it asks you what to do instead, and never overwrites it: when you choose another slug,bb story sluggives the story that slug, renaming its file and records to match.bb story pushchecks the slug in the space too, and refuses one a different story has (slug-taken). When bb pushed this story before, it offers to update that one. - Upload and stamp. After your yes,
bb assets upload --approved <fingerprint>creates missing asset folders, uploads the missing images, records their full CDN URLs in.backbase/assets.json, and sets them in the selected image slots.bb story validate --stamprecords exactly what passed. It won’t stamp a story with placeholders unless you said yes to them. - Measure and review, once. When your dev server is there, the agent asks to render the local story with
bb story serveand measure it against the frame withbb page measure. The story’s file, its slug and its slug in Storyblok are one name, which bb checks everywhere;bb story serveserves the story at it, andbb page measuremeasures only a page that came from the local file, and says so when it came from Storyblok instead. It starts with the section heights: “N of M sections within ±2px; K more within a token step”. Then it lists every section height, blok size, gap and padding that’s more than a step off. Thenbb page reviewputs every section beside its Figma shot, one image a section, and the agent opens each, worst first, and writes down what differs: layout, spacing, typography, colour, imagery, component variant, overflow or content. It fixes what’s compose’s to fix before the push (copy, an image, a blok, a variant), looking at that section again after each change, and you choose: refine now, with/backbase:refine, or push as is. bb measures the page only once it has finished loading and holds still: when it hasn’t within 20 seconds, the agent tries once more, then tells you what was still loading or moving. Without a dev server, or without your yes, it says plainly that the page wasn’t measured or reviewed. - Push.
bb story push --planshows you what it will write: the space, create or update, the name and full path, the parent folder, the size and section count, a fingerprint and an approval sentence. After your yes,bb story push --approved <fingerprint>writes exactly that as a draft, and records the story in.backbase/stories/<slug>.remote.json. See Pushing a story. - Report. The link to open the draft in the visual editor, with what’s composed, skipped, uploaded or reused, any unresolved image slots, where each section’s design came from, what the spacing plan set, whether the page was measured, each section’s review items and how each ended, and the approximations.
- Compare, if you like.
bb page comparecompares the rendered page with the design.bb story servemakes your dev server render the local story file, with nothing pushed; the agent asks before it points the dev server at it in.env.local, and you restart the dev server. See Serving the local story.
Changing a page
To change a story that’s already in your space, say what to change:
/backbase:edit en/package-ils-10 make the hero image bigger
Name the story by its id or its full path; without one, it’s the story the project pulled or pushed, when there’s only one. You need the token from step 6.
The agent pulls the story as Storyblok has it now, editors’ changes included, and changes only the sections you name. A new image can come from a file, a Figma node or a link, and is uploaded only after your yes. It shows you the diff, then the push plan, and pushes a draft after your yes. It never overwrites a section someone changed in Storyblok since the pull unless you name it.
Each step in detail
This flow doesn’t use the Storyblok MCP.
-
Pull.
bb story pullreads the story as Storyblok has it now, editors’ changes included, into.backbase/stories/<slug>.json, and records what it pulled in<slug>.remote.json. It never writes to the space, and never replaces a local file with changes you haven’t pushed unless you say so. -
Change. The agent says which sections it will change, then changes only those, on the pulled content: every other section stays as it was. Each change is an ops envelope, applied with
bb story ops apply, whether it’s text, a design value or the structure (a FAQ item added, a section moved, a card duplicated, a preset inserted), and each is one entry in the story’s ops journal, whichbb story ops undotakes back in one step. It never edits the story file by hand, and never scaffolds the page again. See Editing a story with ops.A new image goes up the way compose’s images do, so you don’t upload it in Storyblok yourself. Give the agent a file (on your machine, such as one in
~/Downloads), a Figma node or an image URL, and say where it’s from when it isn’t yours. A URL is downloaded only from a public HTTPS host.bb assets addchecks the slot is an image field, copies the image into the project by its hash,bb assets planplans the upload intoEdits / <story name>, and the agent shows you the plan: each image, where it came from, its slot and the alt text it proposes. One yes covers them all; after it,bb assets uploaduploads them, and the agent sets each in its slot with its alt text, which the push needs. An image the project uploaded before, from compose or an earlier edit, is reused, never uploaded again. See An edit’s new images. -
Materialize and stamp.
bb story materialize, thenbb story validate --stamp, as in compose. -
Measure and review, locally. When the page was composed in the project from a Figma frame and your dev server is there, the agent asks once to serve the pulled story locally with
bb story serve, at its own slug, as compose does. After your yes, and a restart of the dev server, it measures the page withbb page measureand puts each section it changed beside its Figma shot withbb page review --section, writes down what differs, fixes what its change caused, and looks at that section again. Then it puts.env.localback, before anything is pushed. Without your yes, it reviews the pushed draft instead, in step 7, and says so. -
Diff.
bb story diffshows, section by section, what the push would change, and what someone else changed in Storyblok since the pull. See Pulling and diffing a story. -
Push.
bb story push --planshows you the update, and each new image it puts in the story. If someone changed a section in Storyblok since the pull that the push would overwrite, the plan refuses and names it: the agent pulls again and redoes your change, or, if you name each one, overwrites them, and the approval sentence says so. After your yes,bb story push --approved <fingerprint>writes the draft. Nothing is published. -
Measure the draft. When the page was composed in the project from a Figma frame and your dev server is running, the agent measures the pushed draft against the frame with
bb page measure --draft <full slug>, which opens it through your site’s preview route withSTORYBLOK_PREVIEW_SECRETfrom the project’s env. The secret is never printed: you don’t paste it anywhere. It reports the section heights first (“N of M sections within ±2px; K more within a token step”), then each section it changed whose height, a blok’s size, a gap or a padding is more than a step off. When you didn’t let it serve the story locally in step 4, it reviews each section it changed on the draft instead, withbb page review --draft, and tells you whether your change shows and what’s left.
Refining the page
Once the page is composed, the agent can make it match the frame. You need the project’s dev server.
/backbase:refine
It measures every blok against Figma in pixels, looks at every section beside its frame, changes design values, and keeps only the changes that bring the page closer. It never changes the page’s content or structure. Refine skips slots, and can suggest one after two stuck rounds, with your approval. It pushes the story once at the end, as a draft, after your yes.
The budget, the browser and each step
Add the story’s slug when .backbase/stories/ holds more than one. --tries <n>, --budget <n> and --fit-drop <fit> set the budget: by default at most 5 tries per section and 15 in all, across the page. Every section with a delta beyond a token step gets one try before any gets a second, and the rest go to the most px off. --sections <n> still works for this release, as a budget of that many sections’ tries, but it’s deprecated: use --budget. A fit that falls by more than 0.05 in a kept try is a reason to look at the images, never to undo it.
bb brings its own browser for bb page compare, bb page measure and bb page review: the first time, the agent asks before it runs bb page browser install, which downloads about 120 MB into bb’s own cache, outside the project. See bb’s browser.
- Set up. The agent checks the story is stamped, asks before it points your dev server at the local story in
.env.localwithbb story serve, and asks you to restart the dev server. - Baseline.
bb page measuremeasures every blok against its Figma layer: the gaps, padding, sizes and type that are off, in pixels.bb page reviewputs every section beside its Figma shot, each captured by its own element once it has finished loading in view, and the agent writes down what differs in each, worst first, before it changes anything: the measure can’t see a wrong variant, a colour or a missing image. - The audit. For the sections most off, the agent works out which design value renders the frame’s pixels, and applies all of it as one envelope of ops with
bb story ops apply, each change saying which difference it’s for. - Measure, keep or undo. bb measures the page again, locally, and judges each change by the differences it set out to close: it keeps what moved them toward the frame, and
bb story ops undoundoes what didn’t, or what pushed another one off. The agent looks again at each section it changed, and marks what it wrote down as fixed, a design gap or content. Then the agent fixes up the rest, section by section. Nothing is pushed while it works, and it never changes the page’s content or structure: when a section’s bloks or copy are wrong, it tells you, and you change them with/backbase:editafter the push. - The report. The agent shows you the comparison report,
.backbase/compare/<slug>/latest/index.html, each section’s review items and how each ended, each section’s pixels off before and after, every change frombb story ops log, and what’s left, each with a reason. - Push once.
bb story push --planshows you what it will write, and after your yes,bb story push --approvedwrites it as a draft. Then the agent puts.env.localback as it was. Restart the dev server after it, and the agent measures the pushed draft withbb page measure --draft <full slug>, through your site’s preview route withSTORYBLOK_PREVIEW_SECRETfrom the project’s env: it should measure as the last local try did.
See Refining the page for the details.
Building the missing components
/backbase:component
It builds each placeholder’s component locally, a Wrap or a Create you approved at Gate 1, checks it with Chaperone, syncs it at Gate 2, replaces the placeholder, measures it and pushes a draft after your yes. The custom-component flow shows each step.
Taking a component to Backpack
A component the project built shouldn’t stay in the project forever:
/backbase:upstream fan-stack
The agent drafts an issue in ef-global/backpack and files it after your yes. Then it prepares a branch in a fresh clone and, after a second yes, opens a draft pull request. It never merges it: the Backpack team reviews, merges and releases it.
The same command files the plan’s design requests to GCS, each only after your yes:
/backbase:upstream requests
Each step in detail
Name the component by its schema name, its name or its folder; without one, the agent lists the project’s components and asks.
- A fresh clone. The agent clones
ef-global/backpackinto a new temp folder, never your own checkout, andbb component upstream-planreads Backpack’s layout from it: where the component’s files go, itssb-name (fan-stackbecomessb-fan-stack), its imports in Backpack’s own form, and what Backpack expects that the project doesn’t have yet: presets, tests, the story, the package export, its generated component list, and the parents your project places it in, which have to allow it. A name Backpack already has is refused. - The issue. The agent drafts it: what the design needs, the project and pages that use it, the Figma frame and the section’s screenshot, and the closest thing Backpack has today. It files it only after your yes. gh can’t attach the screenshot, so you drag it into the issue.
- The branch. In the clone, the agent writes the moved files with
bb component upstream-plan --write, adds the missing pieces the way Backpack’s own components have them, runs Backpack’s lint and tests, and commits in Backpack’s commit convention. - The pull request. It shows you the diff, and only after a second yes pushes that branch and opens a draft pull request that references the issue. It never asks for it to be merged.
- Afterwards. Once Backpack releases it, the project’s stories move from
fan-stacktosb-fan-stack, and the local component goes. A later release of Backbase does that for you; until then, the project keeps its own.
For design requests, the agent searches Backpack’s open issues for each one, then drafts a new issue, or, when another project asked already, a comment adding your evidence to that issue. It files only the ones you say yes to, then records each link in .backbase/requests/design-gaps.md, so it’s never filed twice. See Design requests to GCS and Taking a component to Backpack.
Seeing it as a page
For the whole picture, run bb report in the project at any point after Gate 1:
bb report --open
It writes .backbase/report.html and opens it: the plan, the space’s changes, the push and the review, each section beside its Figma shot. It only reads: nothing goes to Storyblok or Figma, and the page loads nothing from the network. bb report plan draws one view, in .backbase/reports/plan.html. See The report page.
Seeing the plan as a widget
The plugin also starts bb mcp, a small MCP server that draws the report page’s views in the chat. In an app that shows MCP Apps, such as Claude Desktop or Claude on the web, the plan, the space, the push plan and the review show as widgets, each section beside its Figma shot. The agent’s message is then only the decision card.
In the terminal, Claude Code shows no widget. You get the card and the link to the report page, as before.
The widget only shows: it has no approve button. You still approve by typing in the chat, so the write guard and the approval sentences are unchanged. What you’re approving stays in the message: “Nothing is written yet.”, what can be lost, closing the Settings story, the decisions, the approval sentence and the report link. The tools read only the run’s files in .backbase/: they never write to Storyblok, Figma or the project, and the widget loads nothing from the network.
How it starts, and trying it in Claude Desktop
At each moment (the plan at Gate 1, the space at Gate 2, the push plan, the review results and done), the agent calls one of the server’s tools: show_plan, show_space_plan, show_push_plan or show_review. Where the app lets it, each section has a Show section N button, which asks the agent for that section’s detail, as typing “show section 6” would.
The server runs bb through npx, so it needs the ~/.npmrc lines from step 4, with your GitHub Packages token. The first start downloads bb, which takes a little while; later starts use npx’s copy. When the server can’t start, nothing breaks: the agent writes its usual message, the card and the report link, as in the terminal.
To try it in Claude Desktop’s chat, add the server yourself: Settings → Developer → Edit Config, then in claude_desktop_config.json:
{
"mcpServers": {
"backbase": {
"command": "npx",
"args": ["-y", "@ef-global/backbase@<version>", "mcp", "--root", "/Users/you/Sites"]
}
}
}Use the plugin’s version for <version> (bb --version in a project prints it), and for --root, the folder your projects are in: the tools read only projects inside it, and bb refuses your home folder or the disk’s root. Restart Claude Desktop, then ask it to show the plan of a project by its folder, such as “show the plan for my-site”. If the server doesn’t start, Claude Desktop may not find npx on its own: put npx’s full path (which npx) as the command. See The widgets.
The files it writes
Everything goes in .backbase/ in the project. At Gate 1, the agent commits sections.json and plan.json, and nothing else, as Plan "<frame name>" at Gate 1. Each correction you make is committed the same way, so git log -p .backbase/plan.json shows how the plan changed. Your own changes are never staged or committed.
Every file, and what’s committed
Nothing is written to Storyblok or Figma, and no code is created, until Gate 2 and /backbase:compose. /backbase:new also makes the project itself, with bb create. Image uploads run through bb assets upload after approval. Gate 2 adds two files of its own, space-plan.json and space-sync.json, both ignored, and composing adds stories/ and the ignored datasources.json.
Image exports add a committed images.json, and compose commits assets.json as it records confirmed assets. The catalog, PNGs, drafts and per-run space snapshots are ignored: bb create adds their patterns to .gitignore, and in a project made without it, the agent adds them in the same commit. When git has no identity, it skips the commit and says so.
In .backbase/ |
What’s in it |
|---|---|
config.json |
Written by bb create: the boilerplate commit the project started from, and the Storyblok space when you gave one. |
figma/<node-id>/sections.json |
The frame, one entry per part: its components, libraries and text, its design, and its copy, every line by node id. While it reads, the agent drafts it in sections.draft.json next to it. |
figma/<node-id>/figma-design.raw.json |
The design read, as Figma returned it, pages included, which bb figma sections design turns into each part’s design and copy in sections.json. Ignored. |
figma/<node-id>/shots/ |
The screenshots it took of the frame, or of each part of a tall one. |
figma/<node-id>/images.json |
Image nodes, hashes, sizes, sections and local PNG paths. Committed; the images/ PNG files are ignored. |
assets.json |
Confirmed assets by Figma hash, bound to this space, with ids, full CDN URLs and folders. Committed and reused on reruns. |
edits/<slug>/images.json |
The images /backbase:edit adds to a story, from bb assets add: each slot, its image’s hash, size and origin, and its local copy. The images/ files are ignored. |
catalog.json |
What your project can place: bloks, the children they take, presets, and Backpack’s components. |
knowledge.md |
The catalog compiled for the agent by bb catalog --knowledge: one line per blok your page can hold, with its options, design values, parents and presets. Ignored. |
plan.json |
The plan: each part’s bucket, the bloks it’s built from, and the questions. |
space-plan.json |
Written at Gate 2 by bb space plan: the space, sb-mig’s two plans, the theme, the summary and the fingerprint. Ignored. |
space-sync.json |
Written by bb space sync: what sb-mig wrote to the space, and the theme colours bb wrote. Ignored. |
datasources.json |
Written by bb space datasources: the space’s colour entries, which colours are stored with. Ignored. |
stories/<slug>.json |
Written by bb story scaffold, then filled in: the story /backbase:compose creates. bb story pull writes it too, from the story in the space. Its slug is its story’s story.slug, and only bb story slug changes it, with the file’s name and the records beside it. |
stories/<slug>.report.json |
What the scaffold composed and skipped, the placeholders, the images to upload, what completing the story filled, under fill, what bb story fill filled, dropped and left, and under bloks, the Figma layer each blok was built from. |
stories/validated.json |
Each story content that passed bb story validate --stamp, and the space it may go to. |
stories/push-plan.json |
Written by bb story push --plan: the push you approve, with its fingerprint. Per run. |
stories/<slug>.remote.json |
Written by bb story push and bb story pull: the story in the space, its updated_at, the hashes of its content and of each section then, and the stamp of what was pushed. The next push of this file updates that story, and refuses to overwrite a section someone changed there since. |
stories/<slug>.ops.jsonl |
The ops journal, written by bb story ops apply and bb story ops undo, and by bb story design set and revert, which wrap them: each applied envelope and undo, with its ops, what its diff measured, the design values it changed and the content’s hash before and after, which bb story ops log, bb story design log and bb page compare read. |
stories/<slug>.changes.jsonl |
The design log bb story design set wrote before bb 0.15, kept as history: nothing writes it now, and bb page compare still lists its entries beside the journal’s. |
stories/<slug>.ops/<n>.json |
The story file before journal entry n, byte for byte, which bb story ops undo restores. The last 50 entries keep theirs. |
compare/<slug>/ |
Written by bb page compare: index.html and compare.json, and with --iteration, every iteration’s folder, latest/ and history.json. |
spacing/<slug>.ops.json |
Written by bb story spacing plan: the ops envelope that gives the story the frame’s gaps, padding, fill and hug, for bb story ops apply. |
measure/<slug>/ |
Written by bb page measure: measure.json, every blok’s pixel deltas against its Figma layer, and measure.txt, the table per section. |
review/<slug>/ |
Written by bb page review: a numbered folder a run, each with one side-by-side image a section, Figma beside the page, and index.json, worst first. The skills write what differs in review.md beside them. |
stories/written.json |
Each story an earlier /backbase:compose created through the Storyblok MCP, by its id. |
reports/<moment>.md |
What the agent told you, in full, at each moment: gate-1.md, gate-2.md, push.md, review.md and done.md, written again each time. Ignored. |
upstream/<kebab>/issue.md, pr.md |
Written by /backbase:upstream: the issue and the pull request’s description, as you approved them. |
requests/design-gaps.md |
Written by bb plan requests after Gate 1: every design request, and each one’s link once it’s filed. |
report.html |
Written by bb report: the plan, the space, the push and the review as one page, from the files above. Ignored. |
reports/<view>.html |
Written by bb report <view>: one view on its own page. Ignored. |
requests/<key>.issue.md, <key>.comment.md |
Written by bb request plan: a request’s issue, or its comment on an open issue that has it, for /backbase:upstream requests to file after your yes. |
Something not working? See Troubleshooting.