Getting started with PirateCMS
PirateCMS is a markdown-first CMS for marketing pages, managed by your AI agent. Every page is one file that humans read like a document and machines read like data. This guide gets you from install to a shipped page, then covers the config basics.
Install
Section titled “Install”One command. The wizard asks where (a new folder, or . for the one you’re in), which
framework (Astro or Next.js), and which package manager, then builds the ship and verifies
every step:
npx -y @piratecms/create my-site # or run it with '.' inside your folder | | | )_) )_) )_) )___))___))___)\ )____)____)_____)\\ _____|____|____|____\\\__ ---------\ P I R A T E /--------- ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^^^ ^^^ ^^^^ ^^^
a markdown-first CMS, crewed by yer AI agent
AARRR! Let's get ye a ship. Three questions, then I do the work.
▐█▙ Ship's inspection ✓ Node 24.3.0 ✓ git aboard
▐█▙ The shipyard ✓ template built from the latest PirateCMS → my-site/ standalone: no ties to the source repo ✓ hull: Astro — Next.js adapter left ashore
▐█▙ Loading provisions ✓ provisions loaded (npm install)
▐█▙ The crew ✓ crew aboard in .claude/skills — /page-new, /post-new, /seo-audit, /ship
▐█▙ The ship's log ✓ fresh git history with a first commit — this ship is yers alone
Yer ship is seaworthy.
? Set sail now? (↑/↓ + enter) ❯ Aye — launch the dev server Not yetChoose Aye and your site opens at localhost:4321 (Astro) or :3000 (Next) — the
wizard shows you the command it ran so you know it next time. The dev server is your
preview: edit any file, the browser updates instantly.
Your first page
Section titled “Your first page”Everything is already aboard — nothing to install. One prerequisite: start Claude Code
inside your site folder (cd my-site && claude) — the skills load from the repo’s
.claude/skills/. Then it’s one command:
/page-new yourproduct.com # or: /page-new — a landing page for my time-tracking appThe agent reads your site (or your description), asks nine short positioning questions — pre-filled, you mostly confirm — and writes your homepage as real, schema-validated sections:
{/* src/content/pages/index.mdx — generated, draft-first */}---title: Track time without thinking about itdescription: Tracklee watches git and your calendar so invoices write themselves.draft: truesections: - type: hero variant: with-proof headline: Track time without thinking about it primaryCTA: { text: Start tracking free, href: /signup } - type: features-grid features: - { title: "…", description: "…" }---Three things just happened:
- Your positioning was saved to
src/data/positioning.yaml— no future page, post, or SEO pass re-asks; the voice stays consistent site-wide. - The page was born a draft: visible at
http://localhost:4321in dev, invisible to production builds until you say “ship it”. - Every prop was validated against the section schemas — a generated page can’t be an invalid page.
From here on, changes are conversations: “make the headline more direct”, “swap testimonials for a how-it-works”, “add a plan”.
Deploy
Section titled “Deploy”One command in Claude Code — the publishing counterpart to the install wizard:
/shipThe first run sets up your harbor. The agent checks what’s connected — GitHub repo, Vercel or Netlify — and walks you through whatever’s missing (logins included). One-time.
Every run after that just ships. The agent takes what you’ve been working on —
including pages still marked draft: true (it asks, flips them, and publishes) — opens a
PR, and hands you a preview URL. Look at it, say “make it live”, and production
updates:
you: ship itagent: AARRR — yer new pricing page is on the preview: https://my-site-git-ship….vercel.app say "make it live" and I'll merge ityou: make it liveagent: She's live, captain: https://my-site.vercel.app/pricingUnderneath it’s plain git + static hosting: every push builds and deploys on your host, every PR gets a preview URL — you never build anything yourself.
Prefer doing it by hand?
gh repo create my-site --private --source . --push# then either:npx vercel link && npx vercel git connect # Vercelnpx netlify-cli init # Netlify# vercel.json + netlify.toml ship in the repo; `pirate use` keeps them matchedAdding pages
Section titled “Adding pages”Ask the agent (/page-new — a features page for my time tracker) or create a file
yourself. File path = URL, always:
src/content/pages/└── features.mdx → /features---title: Featuresdescription: What the app does.layout: composed # composed (default) · prose · docssections: - type: features-grid columns: 3 features: - title: Track time description: One keystroke to start a timer. - title: Bill clients description: Turn tracked hours into invoices.---A page is MDX with a sections: list in frontmatter — each item is a type: plus that
section’s props, pure data. Markdown below the frontmatter renders after the sections.
The navbar and footer are site chrome — rendered on every page from
pirate.config.yaml, never written into page content. Putting the new page in the menu is
one entry in that same file (nav.links: [{ label: Features, href: /features }]) — or just
say “add Features to the nav”.
layout: picks the shell that wraps your content:
| Layout | What it is | Use for |
|---|---|---|
composed | Sections + markdown, full-width | Marketing pages |
prose | Pure markdown, branded typography | About, legal, posts |
docs | Prose + a sidebar | Documentation |
Two conventions worth knowing: draft: true hides a page from production builds — dev still
shows it; /ship (or npx pirate publish) flips it. And Pascal-cased JSX section tags in
the body (<Hero … />) still work as a hand-authoring escape hatch — but sections:
frontmatter is the canonical form, and the only one agents write.
Configuration basics
Section titled “Configuration basics”The single source of truth is one file, pirate.config.yaml: site name and brand,
navigation, footer, SEO defaults, colors, fonts, framework. Change the nav there (or say
“add Pricing to the menu”) and every page updates — pages never carry their own chrome.
Content itself lives next to it, one file per page:
my-site/├── pirate.config.yaml settings: brand · nav · footer · SEO · framework└── src/ └── content/pages/*.mdx the content — one file = one page (data lives inline)Structured data — pricing plans, testimonials, feature lists — lives inline in the page, as section props in the frontmatter:
---title: Pricingsections: - type: pricing plans: - name: Starter price: $9 features: [10 projects, Email support] - name: Pro price: $29 features: [Unlimited projects, Priority support]---Need the same list on several pages (testimonials, logos, plans)? Move it to
src/data/<name>.yaml and reference it as plans: { $data: plans } — the engine inlines
the file at build and validates it with the section’s own schema. Inline stays the default;
$data exists to stop copying one list across pages.
Branding is that same file: brand name, nav, footer, SEO defaults, fonts, and a brand
color ramp (50–900) every section uses. Colors mirror into Tailwind’s @theme block in
src/styles/global.css — or just ask the agent: “change our brand color to emerald” edits
both.
No lock-in, ever: everything is plain files you own — MDX, YAML, git. Change frameworks
later with npx pirate use next (or use astro) — it switches config, dependencies, and
deploy settings in one command. Leave entirely and your content travels: it’s markdown in
your repo.