Skills reference
Every skill in the mdlake-builder plugin, what it does, when to use it, where it stops for your approval, and the hooks the plugin adds to Claude Code. For developers.
On this page
- How the skills are ordered
- Starting a site
- check-lake
- plan-site
- scaffold-site
- generate-copy
- Changing a site
- regenerate
- add-page
- add-component
- refresh-content
- upgrade-ruleset
- Servers, forms and hosting
- add-form
- add-api-route
- add-backend
- setup-hosting
- Getting changes live
- publish
- The plugin’s hooks
- Approval stops, in one list
- What’s next
A skill is a short, written procedure that Claude Code follows. The mdlake-builder plugin has 14. You start one by asking in plain words (“add a contact form”) or by naming it: /mdlake-builder:add-form. Claude then follows the steps of that skill, runs the commands in them, and stops where the skill says to stop so that you can approve.
This page describes each skill: what it is for, what it changes, and where it stops. For how skills fit into daily work, see Working with AI. For the commands they call, see the CLI reference.
Note
A skill only describes the steps. The checks that keep the site safe are in the kit and in npm run check, not in the skill. If Claude skips a step, npm run check still fails when the result is wrong.
How the skills are ordered
The skills for a new site run in this order, each one handing over to the next:
check-lake: what the lake has.plan-site: what the site should be.scaffold-site: build it.generate-copy: write the copy that needs writing (called byscaffold-site).
After that you use the others as the site changes.
| Skill | Use it to | Changes hosting? |
|---|---|---|
check-lake | See what the lake can fill before planning | no |
plan-site | Interview, compose plan.md, settle design.md | no |
scaffold-site | Create the site from the approved plan | no |
generate-copy | Write a headline or intro from the lake, with sources | no |
regenerate | Change one module, page, copy or the design | no |
add-page | Add a hand-written page | no |
add-component | Add a component or override a kit module | no |
add-form | Add a form | server model |
add-api-route | Add a server route | server model |
add-backend | Add a separate Node or .NET backend | split model |
setup-hosting | Write pipelines and Bicep | no, writes for the current model |
refresh-content | Move to newer lake content | no |
upgrade-ruleset | Move to a newer plugin and kit | no |
publish | Run the check, commit and push to main, which deploys | no |
Starting a site
check-lake
Finds which lake content can fill a site type’s slots before anything is planned. Use it first on every new site, and again when a site’s bindings change.
It chooses the lake (the CMS’s mirror, a clone you keep, or another Git address), checks that the clone is fresh, loads _manifest.json, and for each slot of the site type lists candidate documents, collections and assets with the reason it matched. It reports missing content as an editor task, and a folder that has no order while its siblings do (it sorts last) as a hint for editors, on a Folder order: line. For an existing site it reports how far lake.lock is behind and never moves it. It never asks for tags or folders to be changed in the lake, and never invents content to fill a gap.
Stops: after the report, before planning. See Plan a site.
plan-site
Interviews you (about eight questions, each with a recommended answer) after reading the lake, then writes plan.md with a brief, the pages and modules, the content kind of each module (rendered, shaped or generated), the generated-copy jobs, content gaps and open questions. It also settles design.md: it uses the one you supply, or drafts one from the brief and the lake’s brand colours and checks the contrast. A documentation request proposes the docs site type.
Stops: it always ends with a stop to approve plan.md and design.md together. A drafted design.md stays draft until you approve it, and scaffold-site refuses to start while it is a draft.
scaffold-site
Creates the site from the template and builds the pages in the approved plan. It checks the design gate, copies the template, vendors the kit and the loader, writes the ruleset (CLAUDE.md, .mdlake/ruleset.json), .env.example, puts the lake at the plan’s commit, runs plan-to-config to write lake.config.ts, src/pages.config.ts and the generate jobs, writes lake.lock, runs the generate jobs, applies design.md to the theme and runs npm run check. Snapshot sites then get a first site-snapshot.json. It offers to make the first Git commit.
Stops: at each generated copy (with an “approve all” option for the first run) and before the commit. It tells you to run npm run dev in your own terminal, because the dev server writes JSON log lines when it detects an AI agent.
generate-copy
Writes marketing copy (a hero headline, a subline, an intro) from the lake’s documents, with provenance. It asks for a key, a brief and the fields the copy feeds, finds sources, writes strictly from them (no invented facts or numbers), and writes src/content/generated/<key>.md with the source hashes, the lake commit and an output hash. Snapshot sites only. See Generated copy.
Stops: twice. First to confirm the two to five source documents, before any copy is written. Then to review the result in npm run dev. To change copy, change the brief or the sources and run it again; never edit the generated file by hand. With --promote it writes a draft document for editors to import, so the words can become lake content.
Changing a site
regenerate
Changes one part of a composed site: regenerate a copy, change a module’s source or rules, add or remove a module, change a content kind, change a component, or change the design. It first reads the brief and the design and names any conflict (“this change drops the primary CTA from home”, “this document is outside the scope”). You decide, and an accepted conflict updates the brief in the same change. It respects locks and adopted files: a file you adopted is not overwritten; the script prints what it would have changed and exits with code 3.
Stops: for conflicts, and in the skills it calls (generate-copy).
add-page
Adds a hand-written Astro page at a free route. The page reads lake content through getLakeDocument, getLakeCollection and getLakeImage from @mdlake/site-kit/content, uses the kit layout and the layout primitives, and never copies lake text. The file is site-owned: no skill rewrites it later. If the content is not bound yet, it binds it in lake.config.ts. That file is generated, so a binding added by hand needs npx mdlake adopt lake.config.ts, or the binding goes through plan.md. See Custom pages.
Stops: before committing, when the ruleset’s approval rules say so.
add-component
Adds a site-owned component in src/components/, or overrides a kit module through site.config.ts. Components use the design tokens, react-aria-components for interactive elements and lucide-react for icons; content arrives as props. No server code. See Custom components.
Stops: before committing, and npm run check (including the accessibility check) must pass first.
refresh-content
Moves a snapshot site to newer lake content as a reviewable change. It finds the newest commit, reports what changed for this site (bound documents, collection items, assets, stale generated copy), lists the site-owned files that read a changed key, and compares the result against the brief. It runs the checks against the new commit without moving the lock (LAKE_LOCK_CANDIDATE), builds a candidate and compares it with site-snapshot.json. Fixes are proposed, never applied silently. See Refresh content.
Stops: before moving the lock. You review the site in npm run dev (it shows the banner “Previewing lake … — not deployable”). On approval it moves lake.lock and site-snapshot.json together and offers a commit message that lists the content changes. On rejection it puts the clone back at the locked commit and leaves the files untouched.
upgrade-ruleset
Moves a site to a newer plugin and kit version on a branch: swap the vendored packages, run npx mdlake migrate --check and then npx mdlake migrate, record the new versions, run the checks, for a docs site coming from before 0.20.0 list the sections that change position (ordering-changes.mjs), offer to replace button overrides with theme tokens, and handle what the checks leave. Sites older than 0.9.0 use migrate --from-template. See Upgrade and migrate.
Stops: after listing the sections that change position (the lake belongs to the editors), before replacing button overrides, before running adopt, and before changing a locked file.
Servers, forms and hosting
add-form
Adds a contact or enquiry form through the forms module. It asks for the name, route, whether to use Turnstile and an optional intro, runs npx mdlake add-form, then edits src/forms/<name>.ts with you. It reminds you of the settings by name, never by value. On a split site it writes the definition and page but no route, and reminds you to add the form to the backend. See Forms.
Stops: before the site changes to the server model (it costs an App Service plan), and before committing.
add-api-route
Adds an on-demand Astro route in src/pages/api/. It first asks whether a hosted service or a client-side call would do instead, then explains that the site becomes the server model and waits for approval. On a split site it refuses, because the endpoint belongs in the backend. See API routes.
Stops: before the model changes, and before committing.
add-backend
Adds a separate backend (Node or .NET) when a site’s server needs outgrow Astro’s routes. It checks the model, asks what the backend does and which runtime the team prefers, and explains what will be written. For a site on the server model it explains what moving means (the kit’s forms route and @astrojs/node go, other routes stay until you move them) and waits for approval. It then runs backend init, helps write the endpoints, generates the OpenAPI document and the client, and runs dev:all. See A separate backend.
Stops: before writing api/, before moving a server site, and before committing. Moving a site to split is never automatic.
setup-hosting
Writes the pipelines (GitHub Actions, Azure DevOps or both) and the Bicep for the site’s hosting model with npx mdlake hosting init, and prints the manual steps. It validates the Bicep when the CLI is installed. It has no Azure access, so it goes through the steps with you: resource group, deployment, secrets, deploy credentials, pipeline variables and the custom domain. See Azure and Bicep.
Stops: before it writes files, and when a hand-edited file would be overwritten (you choose to adopt or overwrite). It never changes the model.
Getting changes live
publish
Gets a site’s changes live. A site deploys when main is pushed. The pipelines from setup-hosting run npm run check and then deploy to Azure. They run on pushes to main (GitHub Actions also by hand), so there are no pull request previews and no separate lint, test or performance-budget step. npm run check is the gate.
The skill looks for a pipeline with npx mdlake hosting. If there is none, or the Azure resources do not exist, it stops and points to setup-hosting. Then it runs npm run check and stops if anything fails. It shows git status, the diff and the branch, and commits with commit-site.mjs. It then asks you to approve the push, because a push to main deploys to production with no preview in between. After the push it tells you where to watch the run.
Stops: when no pipeline exists, when the check fails, and before the push. See Deploying a site.
The plugin’s hooks
Besides the skills, the plugin adds hooks to Claude Code sessions in a site. They run by themselves.
| Hook | When | What it does |
|---|---|---|
block-locked.mjs | Before Claude edits or writes a file | Refuses edits to files the ownership record forbids: files marked locked, and kit files (the vendored packages, .mdlake/*, lake.lock, builder.json, site-snapshot.json). It tells Claude to override instead through site.config.ts. Generated files and site files stay editable. |
check-ruleset.mjs --hook | At the start of a session | Tells you and Claude when the site is behind the plugin, and suggests /mdlake-builder:upgrade-ruleset. |
report-usage.mjs | At the start and end of a session, and when Claude stops | Sends the token counts of the session to the CMS, never content, when MDLAKE_API_URL and MDLAKE_TOKEN are set and the token was created with Can report AI usage. Set MDLAKE_USAGE_REPORT=0 to turn it off. See npx mdlake usage in the CLI reference. |
The plugin also starts an MCP server called lake (scripts/lake-mcp.mjs). It lets Claude look at the CMS’s published content, drafts and media with the site’s token. It is for looking; the lake clone is what the site builds from. See How sites work.
Approval stops, in one list
You approve these things, always:
plan.mdanddesign.mdtogether, before scaffolding.- Copy sources, and the copy, before it is bound.
- A change of hosting model:
add-formandadd-api-route(to server),add-backend(to split). - The files
setup-hostingwrites. - The move of
lake.lockinrefresh-content. - Replacing button overrides, and running
adopt, inupgrade-ruleset. - Every commit, in skills that commit.
