mcpbeat

Liberate Local

automattic/liberate-local

The OWNED LOCAL static-site path of /liberate — dispatched inline by `/liberate` when its input is a local directory (not a URL). Point it at a directory of hand-authored / Claude-generated HTML+CSS+JS and it stands up a fresh WordPress Studio site and converts the source into a native block theme + live editable pages, carrying the source's own CSS/JS for identical parity. The local-source analog of the URL path: no extraction (the source is already on disk), no platform detection — it provisions the Studio target itself (studio site create), processes the HTML/CSS/JS, then builds the theme and the site. One command drives everything. Hidden from autocomplete and runs only via the `/liberate` front door; not directly user-invokable.

3k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
505
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/Automattic/studio --skill liberate-local

What it tells the agent to use

found in the instruction text
Task spawns other agents

The instruction itself

9 sections, as written by the author

Liberate a local static site

> Dispatched by /liberate. This is the local-directory branch of the /liberate front door, not a standalone entry point — it is user-invocable: false (hidden from the user's autocomplete) and disable-model-invocation: true (the Skill tool can't invoke it). Instead, /liberate <dir> reads & follows this SKILL.md inline (see skills/liberate/SKILL.md → "Step R — Route on input type"). The steps below run in that shared context.

The owned local-source path: a directory of HTML/CSS/JS → a live WordPress Studio site running a native block theme that carries the source's own design. Unlike the URL path of /liberate (which detects a remote platform and extracts over the network), this path has the source on disk already, so it skips detect/discover/extract entirely and goes straight to: provision the Studio site → process the source → build the theme + pages → capture → parity compare → deterministic repair.

liberate_convert_local_site is the one command that drives all of it (with createSite: true it provisions the Studio target too). This skill is the thin front door that resolves inputs, runs that command, and reports.


Pipeline overview

/liberate-local <source-dir>
│
├─ resolve inputs: source dir · site name · theme slug · Studio site path · output dir
│
├─ if JS-data OR static content cards: start these together, then wait for both:
│     ├─ background provision: studio site create --path <studioSitePath> --start --skip-browser --skip-log-details
│     └─ model-local-data subagent → <outputDir>/data-model.json
│
└─ liberate_convert_local_site({ dir, studioSitePath, createSite:true, … })  ── one command, drives everything:
      1 createSite      studio site create (fresh WP+SQLite, started)  — skipped if the site already exists or was already provisioned (idempotent)
      2 ingest          source HTML → native block sidecars (roundtrip-validated) + per-instance lib-i styles
      3 block-fixer     canonicalize each page through @wordpress/blocks (editor-valid markup)
      4 design capture  serve source over HTTP → palette/typography/breakpoints + self-host Google fonts
      5 theme assemble  block theme: nav from the graph, footer, no-title page templates, carried source.css/js + instance-styles.css (+ add_editor_style)
      6 install         write + activate the theme; create WP Pages from sidecars (idempotent via _source_url); front page; page template
      7 compare         capture the WP replica (desktop+mobile) and score parity vs the source
      8 repair          deterministic parity loop: diff → computed-style probe → parity-patch.css → re-compare (bounded, no AI)

There is no path checkpoint (unlike /liberate): the owned-local path is always carry-to-block-theme — the source IS the design authority. The only decision is the optional editor-fidelity surface (below).


Step-by-step

Step 0 — Resolve inputs

Ask for the source directory if not given. Then derive (let the operator override any):

  • dir — the absolute source directory (HTML/CSS/JS).
  • themeSlug — kebab-case, e.g. basename(dir)maison-clouet.
  • siteTitle — the home page <title>, or a name the operator gives.
  • studioSitePath~/Studio/<slug> for a fresh site (must NOT collide with an existing unrelated site unless you intend to reuse it). Confirm the chosen path with the operator before creating.
  • outputDir — liberation artifacts (sidecars, reports, screenshots). Default ~/Studio/_liberations/<slug> or a run-local dir.

Step 0b — WordPress-driven data (conditional)

If the source renders its main content from JavaScript data (an empty <div id="…"> filled at runtime by a mount call over an in-file array, e.g. a catalog/listing/gallery) or from repeated static-HTML content cards (post-preview cards on index/archive pages), those grids would be empty or non-native in the block editor on a straight carry. In that case, first produce the data model:

  • Dispatch the model-local-data skill via a subagent (so the nested skill runs to completion without interrupting this workflow), pointing it at the source dir + output dir. It runs the scaffold to decide the source: js-array, html-cards, or none. For JS arrays it reads the source JS; for static content cards the scaffold's records come from discoverHtmlCards. It writes <outputDir>/data-model.json (posts/taxonomy + native query loops + a faithful card render).
  • At the same time, start Studio provisioning in the background:
  studio site create --path <studioSitePath> --start --skip-browser --skip-log-details

Start this right after resolving inputs and dispatching the data-model fill. Wait for BOTH the background provisioning command and the model-local-data subagent to finish before Step 1.

  • When that file exists, the convert step below auto-activates the data path: it inserts the items, registers any needed custom type/taxonomy, replaces the mounts/card containers with core/query loops (editable/analyzable in the editor), and neutralizes the JS data-mounts or static card container + rebinds any modal to per-card DOM islands — while keeping styling/animation/filter JS.
  • Step 1 still calls liberate_convert_local_site with createSite: true. The convert handler is idempotent, so it reuses the already-provisioned site and skips re-provisioning; behavior is unchanged, only the orchestration order and wall-clock time change.

Run the scaffold to decide. If discovered.source is none, fall through to the pure carry path (today's behavior): do not use a data model, pass dataModel: false to the convert call to force the data path off even if a model file is present, and let Step 1 provision or reuse the site idempotently.

Step 1 — Convert (one command)

Call liberate_convert_local_site with createSite: true:

liberate_convert_local_site({
  dir:            "<source-dir>",
  studioSitePath: "~/Studio/<slug>",
  outputDir:      "<output-dir>",
  themeSlug:      "<slug>",
  siteTitle:      "<title>",
  createSite:     true,     // provision the Studio site if it doesn't exist (idempotent)
})
  • createSite runs studio site create only when no WP install exists at studioSitePath — a re-run reuses the site (and re-converts idempotently). Admin creds come from env WP_ADMIN_USER / WP_ADMIN_PASS; omit them and Studio auto-generates (fine for convert-only).
  • carryCss / carryJs default ON — the source's own stylesheet + scripts are carried into the theme (the parity mechanism). Pass false for a tokens-only theme.
  • nativeBehaviors (opt-in) swaps carried JS for native Interactivity blocks (reveal/sticky/tabs/slider/modal); unmapped behaviors land in behavior-gaps.json. Default OFF (maintain the source JS).
  • editableIslands (opt-in) converts carried core/html islands into editable dla/editable-html blocks (text+image bindable, static-save, render-anywhere). Default OFF.
  • editorSurface (opt-in) also scores each page in the live block editor canvas — needs WP_ADMIN_PASS in env; warn-only for carry (it does not flip the verdict).

Narrate progress. On isError, surface the message (a missing source dir, a Studio create failure, a roundtrip/compose failure per page) and stop.

Step 2 — Report

From the handler summary + parity-report.json in the output dir, report:

  • the Studio site (created vs reused) + its live URL,
  • pages composed / failed / empty + low-confidence count,
  • parity: per-page desktop/mobile scores, the averages, allPass, and repair rounds/converged,
  • any warnings (carried-asset notes, sticky-not-carried, behavior gaps).

Honest visual assessment: itemize real differences; do not oversell a match (see the match-section skill's anti-gaming rules if doing a polish pass).


Idempotency & resume

Re-running /liberate-local <dir> on the same studioSitePath:

  • skips site creation (the site exists),
  • re-ingests + re-converts deterministically,
  • re-installs pages by _source_url (no duplicates),
  • re-runs the parity loop.

To start completely fresh, point at a new studioSitePath (or delete the old site via studio site delete).


Notes

  • This path needs Studio installed and the studio CLI on PATH (studio site create provisions WP + SQLite and starts the site on a Studio-assigned port; the convert auto-resolves the live URL via wp option get siteurl).
  • The source is trusted (owned). There is no platform adapter, no network extraction, no products path — it's structure + design carry.
  • The theme is a real block theme: pages are block-editable, the carried CSS is the design authority, and per-instance source styles ride fixer-safe lib-i classes (so editor saves don't drop them). Carried CSS is also loaded into the editor canvas (add_editor_style).
  • Measured timing: a fresh convert with provisioning took about 3m36s, while reusing an already-provisioned site took about 2m. On the JS-data path, overlapping the background studio site create with the model fill hides roughly 1.5m of fixed provisioning cost.
  • Deeper overlap of ingest or design capture would require splitting the convert handler. Keep this workflow to the provisioning overlap unless that handler split exists.

How to use it

Copy the folder

Take automattic/liberate-local from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.