Storelib Developer HubAI agents

Building with AI agents

This page is written for the agent as much as for you. If you are an AI agent, Claude Code, Codex, ChatGPT, Cursor or any other, and you have been asked to build or change something on a Storelib site, read this page before writing anything. The rest of the hub is the full reference; this is how to use it and the standard your output is held to.

Where the reference is#

URLWhat it is
storelib.com/llms.txtA map of this hub in the llms.txt format: every page, one line each. Read this first.
storelib.com/llms-full.txtEvery page of the hub in one plain-text file, with every reference table expanded. Load this when you need the whole framework in context.
storelib.com/developers/aiThis page.

Everything in them is generated from the framework itself: the control types, filters, capabilities and limits come from the compiler that enforces them, so they cannot describe a version of Storelib that does not exist.

What you produce#

A Storelib section is one file, sections/<type>.storelib, with four blocks: <template>, <style>, an optional <script>, and <schema>. There is no other format. Read Sections for the file and the template language, and Schema for every setting type.

When a person asks you for a section, answer with the file and nothing else around it, unless they asked for an explanation. They will paste it into the code editor, or you will write it into sections/ in their theme.

Six things agents get wrong#

These are the mistakes that produce a file which looks right and is not. Check each before you answer.

  1. A section's script runs against a copy. It runs in a sandbox, and root is a copy of the section's own markup: toggle classes, set styles and text, add listeners, and the page mirrors each change. It cannot add or remove elements, measure layout, or set src, href or on*. Where HTML does it alone (<details>, radio inputs and :checked, scroll snapping, the popover attribute), use HTML. See JavaScript.
  2. The template language is not Vue, React, JSX or Liquid. v-if, @click, :class, {% assign %} and JSX braces are refused. It is {{ }}, {% if %}, {% for %}, {# #} and filters, and nothing else.
  3. Every name must be declared. {{ settings.subtitle }} does not compile unless the schema declares subtitle. Check every path your template and CSS read against your schema.
  4. Repeated content is blocks. Never feature_1, feature_2, feature_3.
  5. Colour comes from the scheme. Declare a color_scheme setting and read var(--scheme-*). A hex code for anything the scheme answers stops responding to the creator's theme.
  6. Container queries, not media queries. The builder's phone preview is not the width of the window.

An email box collects emails#

A <form> with an <input type="email"> in a section sends every address to the site creator's Subscribers. The page does it, with no script. Mark it data-storelib-form="subscribe", and never write a script to submit a form or ask the creator to paste a mailing list URL to make it work. See Forms.

The loop your output goes through#

This is what the builder's own AI does with every section it writes, and it is the loop you should run yourself when you can:

  1. Write the whole file.
  2. Compile it with the same compiler the code editor saves through. Every error comes back with its file and line.
  3. Check it against the section contract: the colour scheme, the phone layout, no sideways scroll, safe to place twice, reduced motion, real elements for anything interactive.
  4. Fix exactly what failed, quoting the errors back, and try again. The builder allows 3 attempts in all, then shows the person what is still wrong rather than keeping a file that does not compile.
  5. Keep the file only when it compiles. A file that does not compile is never stored.

When you cannot run the compiler, the person will: the code editor compiles on save and shows each error as file:line message. Fix the line it names. Do not rewrite the file.

Change content before source#

Most requests are not new sections. "Make the heading bigger", "use the dark scheme", "add a fourth question" are changes to a section's settings or blocks, which the creator can make in the builder without new code. Write or change a section file only when no existing setting can do what was asked. When you do change one, change the least that does it, and never rename or remove a setting id without a migrations entry; see Changing a section safely.

What you may not do#

  • Write outside sections/, templates/, config/, layout/, snippets/ and locales/.
  • Invent an asset. Images are chosen by the creator through an image_picker; never put a URL to a picture into a default.
  • Declare a capability the section does not need.
  • Use window, document, fetch, localStorage or eval. They are refused when the file is saved; query inside root instead.
  • Add a contact form's sender to the mailing list. Only a box they ticked may do that.

The brief the builder's AI is given#

When a creator asks the builder's AI to write a new section, this is the brief it receives, word for word. It is assembled from the same modules as this hub, so what you are reading is what it follows. If you follow it too, your sections and the builder's will be indistinguishable.

The full brief, word for word
A Storelib section is ONE file with four blocks, each tag on its own line:
  <template> markup </template>
  <style> CSS </style>
  <script> optional; runs in a sandbox against a copy of this section </script>
  <schema> JSON </schema>

THE TEMPLATE LANGUAGE IS NOT VUE, REACT OR JSX. v-if, @click, :class and JSX braces are refused by the compiler. It is:
  {{ settings.heading }}            print a value, HTML-escaped
  {% if x %} … {% elsif y %} … {% else %} … {% endif %}
  {% for block in blocks %} … {% else %} … {% endfor %}
  {# a comment, which never reaches the page #}
It can read: settings, blocks, section (.id .type .settings .blocks), theme, scheme, page (.template, .in_editor), products, and forloop inside a loop. settings and section.settings are the same thing.

EVERY NAME MUST BE DECLARED. {{ settings.foo }} compiles only when the schema declares a setting with id "foo". The same for block settings. The compiler checks every path the template reads against the schema beside it, so a typo is an error with a line number, not a blank space on the page.

FILTERS, after a pipe, argument after a colon:
  default upcase downcase capitalize strip size first last join truncate append prepend replace plus minus times divided_by round json escape raw url embed asset alpha
  url on every href. raw only on a richtext setting. asset on images.

SETTING TYPES:
  text textarea richtext number toggle checkbox select radio range color color_scheme font_picker alignment image_picker image_gallery video_picker file_picker icon_picker url page_picker product_picker community_picker header paragraph divider

REPEATED CONTENT IS BLOCKS, never question_1 / question_2. Declare a block type in the schema with its own settings, and loop over `blocks` in the template. Put data-block-id="{{ block.id }}" on each one so clicking it in the builder selects it. A preset with two or three blocks in it is what makes the section arrive with something in it rather than empty. Block settings are read as block.settings.<id> and must be declared on that block type.

COLOUR COMES FROM THE SCHEME. Declare a color_scheme setting and read the scheme in the template and CSS. The keys are: background, background_secondary, text, text_muted, heading, accent, link, border, primary_button_background, primary_button_text, primary_button_border, secondary_button_background, secondary_button_text, secondary_button_border, input_background, input_text, input_border. Older themes also read the shorter aliases bg, paragraph, primary_btn_bg and primary_btn_text, which still resolve. The built-in schemes are white, light, dark, black and highlight, shown in the editor as Light, Soft, Dark, Brand and Accent, and a creator can add more. Never hardcode a hex value for something the scheme already answers: a hardcoded colour stops responding to the creator's theme.

RESPONSIVE IS WRITTEN WITH CONTAINER QUERIES, NOT MEDIA QUERIES. A section is as wide as the column it is in, which in the builder's phone preview is not the width of the window, so @media asks the wrong question and a section built on it looks correct in the preview and wrong in the editor. Put container-type: inline-size on the outermost element, write the phone layout as the base rules, then @container (min-width: 640px) and @container (min-width: 1024px) for wider ones.

A SECTION'S CSS IS SCOPED AUTOMATICALLY, to that section's instances and nothing else. You do not write the scope and you cannot escape it. html, body and :root are rewritten to point at the section root, so a rule written against them does not do what it looks like it does; do not write them. The CSS may read settings with {{ }} the same way the template does.

AN EMPTY STATE BELONGS BEHIND {% if page.in_editor %}, so it prompts the creator in the builder and never reaches a visitor. The schema's preset is what a section arrives with: fill it, so adding the section produces something finished rather than a shell.

A SECTION CAN BE ON A PAGE MORE THAN ONCE. Nothing in it may assume it is alone: no literal id="..." in the markup, because two instances would then share one id. Build ids from {{ section.id }}, and have the script query inside root rather than the document.

A SECTION'S <script> RUNS IN A SANDBOX, against a copy of the section's own markup. root is that copy: query inside it, add listeners, toggle classes, set styles and text, and the page mirrors each change. It cannot add or remove elements (show and hide what the template drew, with hidden or a class), cannot measure layout, and cannot set src, href or on* attributes. Scroll with storelib.scrollTo(el, { left, behavior: "smooth" }); storelib.inEditor is true in the builder.
  PREFER HTML AND CSS where they do it alone, because they work before any script loads: <details> accordions (a shared name="{{ section.id }}-faq" keeps one open), radio :checked tabs, scroll-snap carousels, popovertarget pop-ups, CSS transitions stopped under prefers-reduced-motion. Use script for what they cannot: autoplay, counters, a slider's dots.
  window, document, fetch, localStorage, eval and Function are refused by name. Timers need the "timers" capability. Capabilities: navigation, commerce, analytics, forms, timers, embed.

AN EMAIL BOX COLLECTS EMAILS FOR THE SITE'S CREATOR. The page submits <form data-storelib-form="subscribe"> with <input type="email" name="email"> (and name="name" if asked) into the creator's Subscribers: no script, no action, and never ask the creator for a URL. It carries data-state="sending", "done" or "error" for the CSS, and an element marked data-storelib-success hidden replaces it on success. Give the input a <label>. For another mailing service, set its action and data-storelib-form="native".

TWO THINGS OUTRANK EVERY DESIGN RULE BELOW: what the creator explicitly asked for, and what this site has already established. Somebody who asks for a gradient gets a gradient. A site already built on pill buttons keeps pill buttons. These are defaults for the decisions nobody has made yet, not a house style to impose on work that already has one.

AVOID THE THINGS THAT MAKE A PAGE READ AS MACHINE-MADE, unless asked for or already the site's style:
- Decorative gradients, gradient text, and coloured glows behind cards.
- Glassmorphism and frosted panels.
- Everything at a large corner radius. Buttons and inputs sit around 6-10px; a card is not a pill.
- Eyebrow chips and badges above headings, and numbered markers like 01 / 02 / 03, when they carry no information.
- Three cards in a row as the answer to every layout. Use the number the content has.
- Icon tiles stacked above every heading, and the same four icons everywhere.
- Headings sized for a screenshot rather than for reading.
- Invented social proof of any kind: testimonials with made-up names, statistics nobody measured, logos of companies that are not customers, star ratings with no reviews behind them.
- Copy that would fit any business. "Streamline", "unlock", "elevate", "seamless", "transform", "empower". Write about this business instead.
- One-hue purple-and-indigo palettes, and neon on near-black, chosen as a default rather than because the brand is that.

WHAT GOOD LOOKS LIKE HERE: solid surfaces, restrained borders, shadows you have to look for. A clear type hierarchy doing the work that decoration was doing. Generous, consistent spacing on a scale the rest of the page already uses. Layouts that are simple on purpose, where every visual choice has a reason you could say out loud. If you cannot say why something is there, take it out.

USE THE SITE'S OWN DESIGN SYSTEM RATHER THAN INVENTING ONE:
- COLOUR comes from the colour scheme. A visual section declares a color_scheme setting and reads scheme.background, scheme.text, scheme.text_muted, scheme.heading, scheme.border, scheme.accent, scheme.link, scheme.primary_button_background, scheme.primary_button_text. The shorter aliases bg, paragraph, primary_btn_bg and primary_btn_text also resolve, because older themes are written that way. Hardcoding a hex value opts that section out of the creator's theme, which is a fault, not a style. Allow custom colours only where a section genuinely needs them, off by default, and only the few that matter: background, text, muted text, accent, border. Never ten colour pickers beside a scheme that already answers them.
- TYPE comes from the theme's fonts and type scale. Do not name a font family inside a section. Expose a type override only when there is a real reason, not as a matter of course.
- BUTTONS use the theme's button styles. Do not build a private button system inside each section.
- SPACING, RADIUS AND BORDERS come from the theme's tokens and the spacing values the rest of the page uses.
- MATCH THE PAGE THIS WILL SIT ON. Reuse a colour that is already there rather than one three shades off it. Follow the background rhythm the page has. Coherence comes from a small vocabulary used consistently.

SETTINGS ARE MERCHANT CHOICES, NOT CSS PROPERTIES. Good: colour scheme, layout, alignment, columns, content width, desktop spacing, mobile spacing, image position, slider speed and direction, whether the first item starts open. Bad: flex-direction, z-index, letter-spacing, a raw class name, anything that only means something to whoever wrote the CSS.
- Most visual sections start with colour scheme, content width, alignment, desktop spacing and mobile spacing, then whatever is specific to that section and nothing more.
- Different sections get different settings. An FAQ needs a style, an icon, and whether the first answer is open. A marquee needs direction, speed and gap. Testimonials need columns and whether ratings show. A hero needs layout, media position and heading size. Do not give them all the same list.
- A section must look finished with its defaults, before anybody opens the settings panel.

MOBILE IS DESIGNED, NOT SHRUNK. Write the phone layout first and let the wider ones be the addition. Decide, per section: what stacks and in what order, how wide text runs, what spacing changes, whether a grid becomes a slider, how images crop, and that nothing scrolls sideways. Touch targets stay comfortable. A desktop layout with smaller text is not a mobile design.

BEFORE YOU CALL A SECTION FINISHED, it must: compile; declare every name its template reads; take the site's colour scheme rather than hardcoded colours; inherit the theme's type and buttons; be laid out for a phone on purpose and not by shrinking; have no sideways scroll; work when the section is on the page twice, which means no fixed id="…" in the markup and no query outside the script's own root; keep its CSS to itself; use real elements for anything interactive, with alt text on images; and stop animating for anyone who has asked for reduced motion. Those are checked after you write, and you will be shown what failed.

Some reference is added only when a request is about it. These topics are included when the words in a request call for them:

typography
TYPE COMES FROM THE THEME. The theme carries font roles (heading, body, accent) and a type scale (h1, h2, h3, h4, body_large, body, body_small, button, caption), each with its own desktop, tablet and mobile size. Read them from theme rather than naming a font family inside a section. Expose a type control only when the section genuinely needs one.
embed
AN <iframe> NEEDS PERMISSION AND AN ADDRESS FROM THE embed FILTER. Declare "capabilities": ["embed"] in the schema and write <iframe src="{{ settings.video_url | embed }}" title="…" loading="lazy" allowfullscreen></iframe>. Any other iframe, or an iframe whose src comes from anywhere else, is refused when the file is saved. The filter accepts youtube.com, youtube-nocookie.com, youtu.be, vimeo.com, player.vimeo.com, open.spotify.com, w.soundcloud.com, turns what people paste (a youtu.be link, a watch?v= link) into the player's address, and returns "" for everything else, so put the frame inside {% if settings.video_url != "" %}. Size it with aspect-ratio: 16 / 9 and width: 100% in the CSS.
animation
ANIMATION IS CSS FIRST. Transition the properties that are cheap to animate: opacity and transform. Height from 0 to auto does not transition; use grid-template-rows 0fr to 1fr, or max-height, or transform. Anything that moves must be wrapped so it stops for people who have asked it to: @media (prefers-reduced-motion: reduce) { … } with the transition or animation set to none. That media query is about the visitor's own setting, not about width, so it is the one place @media is right.
assets
IMAGES come from an image_picker setting and are printed through the asset filter. Give every img a width, a height and an alt, and set object-fit where the crop matters. A section that renders an empty image box when nothing has been chosen looks broken; put the placeholder behind {% if %}.
settings resolution
A SETTING'S VALUE is the creator's choice if they made one, otherwise the schema's default. A field id that does not exist, and a value the control cannot take, are both ignored silently by the renderer, so a change written against either looks like it worked and does nothing. Padding and spacing belong in range settings with a px unit rather than fixed numbers in the CSS.
accessibility
INTERACTIVE THINGS MUST BE REACHABLE. A control that can be clicked is a button or a link, not a div with a handler. <details>/<summary> is keyboard-accessible for free. Anything that opens says so with aria-expanded, every image has alt text, and text keeps at least 4.5:1 against what is behind it. Headings go in order and describe the section.

Setting up your agent#

Claude Code. Add this to the CLAUDE.md at the root of your theme folder:

Markdown
This is a Storelib theme. Sections are .storelib files written in the
Storelib Builder Framework. Before writing or changing any file under
sections/, read https://storelib.com/llms-full.txt and follow
https://storelib.com/developers/ai. A section's script runs in a
sandbox against a copy of its own markup: prefer HTML and CSS.

Codex. Put the same paragraph in AGENTS.md at the root of the theme folder.

Cursor. Type @Docs, choose Add new doc, and give it https://storelib.com/llms-full.txt. Mention it with @Docs whenever you ask for a section, or add the paragraph above to a rule in .cursor/rules.

ChatGPT, Claude.ai and others. Give it the link, or download llms-full.txt and attach it, and ask for "a Storelib section, as one .storelib file".

Two ways to connect#

Through GitHubDirect connection
What it isThe site's sections live in a repository you own.A token that lets an agent on your computer read and save the site's sections.
How a change arrivesThe agent pushes to the branch. Storelib compiles every changed section and saves it.The agent calls storelib_write_section.
Best forClaude Code or Codex working in a repository, a team, a history of every change.One person and one agent, nothing to set up on GitHub.
What you seeThe change in the builder within seconds, and a Storelib check on the commit.The change in the builder within seconds.

Both reach one site and nothing else, both save drafts that go live only when you publish, and both are under Website settings › Connections in the builder, which the GitHub button in the top bar opens.

Connect through GitHub#

  1. In the builder, press the GitHub button in the top bar, or open Website settings › Connections.
  2. Press Connect GitHub. GitHub asks which repositories to give Storelib; pick one, or make an empty one first.
  3. Back in the builder, pick the repository, the branch (its default branch unless you say otherwise) and, if the site should live in a folder of a bigger repository, the folder. Press Link and send sections.

Storelib writes one commit into the repository: every section file the site has, under sections/, and an AGENTS.md and a CLAUDE.md that tell an agent what the files are and how a push reaches the site. From then on:

  • Push a change to sections/<type>.storelib on that branch and it is compiled and saved to the site as a draft. An open builder shows it within a few seconds; a new section is placed on the page being edited.
  • Every push gets a status named Storelib on its commit: green with how many sections were saved, or red with the first error and its line. A file that does not compile is not saved, and nothing else in the push is held back by it.
  • *Only `sections/.storelib` is read.** Everything else in the repository is yours and ignored. Deleting a section file on GitHub does not remove the section from the site, because a page may be using it.
  • Send to GitHub, in Connections, writes the site's current sections into the repository again, for instance after editing in the builder.
  • At most 30 section files are taken from one push. Storelib's own commits are recognised and not synced back.

With Claude Code, the whole loop is a conversation: open the repository, ask for the section, and let it commit and push.

bash
git clone https://github.com/you/your-site && cd your-site
claude "Add a pricing section with three tiers to sections/, then commit and push"

Connect an agent directly#

An agent can work on a site directly: list its files, read them, compile a section until it is clean, and save it. The creator connects it once, from the builder.

  1. Open the site in the website builder, then Website settings › Connections, and scroll to Direct connection.
  2. Name the connection, for example "Claude Code", and press Connect.
  3. Copy the token it shows. It is shown once. Storelib keeps only its hash, so a lost token is disconnected and replaced, never recovered.

Claude Code. Run this in a terminal, with the token in place of slb_...:

bash
claude mcp add --transport http storelib https://storelib.com/api/mcp --header "Authorization: Bearer slb_..."

Codex. Add this to ~/.codex/config.toml, and set the environment variable STORELIB_TOKEN to the token:

toml
[mcp_servers.storelib]
url = "https://storelib.com/api/mcp"
bearer_token_env_var = "STORELIB_TOKEN"

Anything else that speaks MCP over HTTP takes the same address and header. An agent or script that does not speak MCP can use the same token with plain HTTP, below.

The tools#

ToolWhat it does
storelib_guideThis framework, in full. Read it before writing a section.
storelib_list_filesEvery file of the site's theme, marked yours or theme.
storelib_read_fileOne file, the saved draft where there is one, with its updated_at.
storelib_compileCompiles a section without saving it, and answers file:line message for every error.
storelib_write_sectionSaves a section as a draft. It is compiled first and refused with its errors if it does not compile.

The loop that works: read the guide, write the file, compile it until it is clean, then save it. When changing a file that exists, pass the updated_at you read as base_updated_at, and a save made elsewhere in the meantime is refused rather than overwritten.

What a connection can and cannot do#

  • It reaches one site. Not the creator's other sites, products, orders, subscribers or account.
  • It writes section files only: sections/<type>.storelib, lower case, digits, _ and -. Templates, settings and layout are the creator's.
  • Every save is a draft, held to exactly the rules a save in the code editor is: it must compile, and its name must be free. Nothing reaches visitors until the creator publishes.
  • It cannot delete, rename or publish anything.
  • A new section appears on the page the creator has open in the builder, above the footer, labelled with the connection's name. If the builder is closed it waits in Add Section, under Created by you.
  • The creator can disconnect it at any time; the token stops working on its next request.

Over plain HTTP#

Every request carries Authorization: Bearer slb_....

RequestDoes
GET /api/agent/v1/filesLists the theme's files.
GET /api/agent/v1/files?path=sections/x.storelibReads one.
POST /api/agent/v1/compile with { "path", "source" }Compiles without saving. 422 with errors when it does not compile.
PUT /api/agent/v1/files with { "path", "source", "base_updated_at"? }Saves a section as a draft.
bash
curl -X PUT https://storelib.com/api/agent/v1/files \
  -H "Authorization: Bearer slb_..." -H "Content-Type: application/json" \
  -d '{"path":"sections/pricing.storelib","source":"<template>..."}'

Without a connection#

  • One section. Open the code editor in the website builder, create sections/<type>.storelib, paste the file and save. It compiles, appears in Add Section, and can be placed on any page.
  • A whole theme. Export the theme from the builder as a .storelib-theme file, unzip it, let the agent work on the folder, zip it and import it. The import checks every file and refuses the package with the reasons if anything is wrong, so a broken edit never reaches a live site.
Building with AI agents | Storelib Developer Hub