{
  "id": "astro-static-site",
  "type": "loadout",
  "title": "Astro static site",
  "summary": "Astro conventions for fast, zero-JS-by-default sites with content collections, plus a skill for deploying to Cloudflare Pages with headers and redirects.",
  "description": "Astro is a great fit for portfolios, docs, blogs and landing pages, but agents love to turn it into a\nReact app with a hydrated component on every line. This loadout keeps pages static and fast, uses\ncontent collections for anything that looks like content, and finishes with a reliable Cloudflare\nPages deploy. (This very site is built on the same approach.)\n",
  "highlights": [
    "Zero JavaScript by default; islands only where there is real interactivity",
    "Typed content collections with schemas instead of ad-hoc markdown imports",
    "Images through astro:assets, semantic HTML and accessibility basics",
    "Cloudflare Pages deploy skill: build settings, _headers, _redirects and custom domains"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "categories": [
    "web-app"
  ],
  "stacks": [
    "astro",
    "typescript",
    "cloudflare"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Astro static site rules.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/astro-static-site/latest/AGENTS.md",
      "bytes": 2630,
      "lang": "markdown",
      "content": "# AGENTS.md: Astro static site\n\nStatic output (`output: \"static\"`, the default). The built site is plain HTML/CSS in `dist/`.\n\n## Principles\n- **Ship no JavaScript unless a feature needs it.** `.astro` components render to HTML at build time.\n  A UI framework component (React, Svelte, Vue) only runs in the browser with a `client:*` directive — use\n  `client:visible` or `client:idle` over `client:load`, and only for genuinely interactive parts.\n- Small inline `<script>` tags in `.astro` files are fine for simple interactivity (menus, copy buttons). Astro bundles them.\n- Do not add a UI framework integration just for one widget.\n\n## Layout\n```\nsrc/pages/          routes (file-based). [slug].astro + getStaticPaths for dynamic pages\nsrc/layouts/        page shells (Base.astro holds <head>, meta tags, header, footer)\nsrc/components/     .astro components\nsrc/content/        markdown/MDX/JSON content\nsrc/content.config.ts   collection definitions with zod schemas\nsrc/styles/         global CSS (tokens as custom properties)\npublic/             copied as-is: favicon, robots.txt, _headers, _redirects\n```\n\n## Content collections\n- Anything list-like (posts, projects, docs) is a collection defined in `src/content.config.ts` with a `glob()` loader and a `schema`.\n- Read with `getCollection(\"blog\")` / `getEntry(...)`; render markdown with `render(entry)`.\n- Add new frontmatter fields to the schema first. The build should fail on bad content, not render `undefined`.\n\n## Pages and SEO\n- Every page sets a unique `<title>` and `<meta name=\"description\">` via the layout props.\n- Set `site` in `astro.config.mjs` so canonical URLs, sitemaps and RSS work.\n- Use `@astrojs/sitemap` for a sitemap; add `robots.txt` in `public/`.\n- Internal links are root-relative (`/about/`). Respect the configured `trailingSlash` setting.\n\n## Images and assets\n- Local images live in `src/assets/` and go through `<Image />` / `<Picture />` from `astro:assets` (sizes, formats, lazy loading).\n- Always set meaningful `alt` text (empty `alt=\"\"` for decorative images).\n- Self-host fonts or use system fonts; preload only the one font file used above the fold.\n\n## HTML and CSS\n- Semantic HTML: one `<h1>`, landmarks (`header`, `main`, `nav`, `footer`), real `<button>`s and `<a>`s.\n- Component styles in `<style>` (scoped by default). Global tokens (colours, spacing, fonts) as CSS custom properties.\n- Check colour contrast and visible focus states. Respect `prefers-reduced-motion`.\n\n## Before you say \"done\"\n`npm run build` passes with no warnings you introduced, and `npm run preview` shows the change. Mention any new page or route.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/astro-static-site/latest/CLAUDE.md",
      "bytes": 378,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n- Skills from this loadout live in `.claude/skills/`: `cloudflare-pages-deploy`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Astro static site\" v1.0.0 — https://loadout.jnw-it.com/loadouts/astro-static-site/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/astro-static-site/latest/GEMINI.md",
      "bytes": 379,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n- Skills from this loadout live in `.agents/skills/`: `cloudflare-pages-deploy`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Astro static site\" v1.0.0 — https://loadout.jnw-it.com/loadouts/astro-static-site/ -->\n"
    },
    {
      "path": ".cursor/rules/astro-components.mdc",
      "role": "rules",
      "description": "Rule applied to .astro files.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": ".cursor/rules/astro-components.mdc",
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/.cursor/rules/astro-components.mdc",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/astro-static-site/latest/.cursor/rules/astro-components.mdc",
      "bytes": 815,
      "lang": "markdown",
      "content": "---\ndescription: Astro component conventions\nglobs: \"**/*.astro\"\nalwaysApply: false\n---\n\n- Frontmatter (between `---`) runs at build time only. Never put secrets in client `<script>` tags; frontmatter is fine.\n- Type props: `interface Props { title: string; tags?: string[] }` then `const { title, tags = [] } = Astro.props;`.\n- Use `class:list={[...]}` for conditional classes and `set:html` only with trusted, sanitised HTML.\n- Framework components need a `client:*` directive to be interactive; omit it when the component is static.\n- Prefer `<Image />` from `astro:assets` over `<img>` for local images.\n- Keep components small and composable; use `<slot />` (and named slots) instead of passing HTML strings as props.\n- Scoped `<style>` by default; `:global()` only when styling slotted or third-party markup.\n"
    },
    {
      "path": "skills/cloudflare-pages-deploy/SKILL.md",
      "role": "skill",
      "description": "Deploys a static build to Cloudflare Pages.",
      "skill": "cloudflare-pages-deploy",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/cloudflare-pages-deploy/SKILL.md",
        "codex": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
        "cursor": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
        "gemini-cli": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
        "copilot": ".agents/skills/cloudflare-pages-deploy/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/skills/cloudflare-pages-deploy/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/astro-static-site/latest/skills/cloudflare-pages-deploy/SKILL.md",
      "bytes": 2336,
      "lang": "markdown",
      "content": "---\nname: cloudflare-pages-deploy\ndescription: Deploy a static site (Astro, Vite or plain HTML) to Cloudflare Pages, with headers, redirects and a custom domain. Use when the user asks to deploy, host or publish a static site on Cloudflare.\n---\n\n# Deploy a static site to Cloudflare Pages\n\n## 1. Confirm the build\n- Build command and output folder: Astro `npm run build` → `dist`; Vite `npm run build` → `dist`; plain HTML: no build, output is the folder.\n- Run the build locally and check the output folder has `index.html`.\n- Set the Node version Cloudflare should use: add a `.nvmrc` (e.g. `22`) or the `NODE_VERSION` environment variable in the project settings.\n\n## 2. Add Pages config files to `public/` (copied into the output)\n`public/_headers`:\n```\n/*\n  X-Content-Type-Options: nosniff\n  Referrer-Policy: strict-origin-when-cross-origin\n  X-Frame-Options: DENY\n\n/_astro/*\n  Cache-Control: public, max-age=31536000, immutable\n```\n`public/_redirects` (only if needed):\n```\n/old-page /new-page/ 301\n```\n\n## 3. Choose a deploy route (ask the human which they prefer)\n**A. Git integration (recommended for ongoing sites):** the human connects the repo in the Cloudflare dashboard\n(Workers & Pages → Create → Pages → Connect to Git), sets the build command and output directory from step 1.\nEvery push to the production branch deploys; other branches get preview URLs.\n\n**B. Direct upload with Wrangler:**\n```sh\nnpx wrangler login                     # human does this once, in a browser\nnpx wrangler pages project create <project-name> --production-branch main\nnpx wrangler pages deploy dist --project-name <project-name>\n```\nNever paste or store a Cloudflare API token in the repo. In CI, use the `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` secrets.\n\n## 4. Custom domain\nIn the Pages project → Custom domains → add `sub.example.com`. If the zone is on the same Cloudflare account, the DNS record is\ncreated automatically; otherwise the human adds a CNAME to `<project>.pages.dev`. Certificates take a few minutes.\n\n## 5. Verify\n- Open the `*.pages.dev` URL and the custom domain. Check a deep link, the 404 page, and response headers\n  (`curl -sI https://sub.example.com/ | grep -i x-content-type`).\n\n## 6. Report\nProject name, production URL, preview URL pattern, and anything the human still needs to click.\n"
    }
  ],
  "requires": {
    "tools": [
      "node",
      "wrangler"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": true,
    "scripts": [],
    "notes": "The deploy skill uploads your built site to Cloudflare when you ask it to."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-15",
  "published": "2026-10-15",
  "updated": "2026-10-15",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 2,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/astro-static-site/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/astro-static-site.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-16T22:54:03Z",
    "commit": "ad36e852567aa3c87821d2a9e2fe4052fd983374",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 5 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 5 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 5 agents; layouts re-discovered",
        "details": [
          "Claude Code: 3 files, 1 skill in .claude/skills/",
          "Codex CLI: 2 files, 1 skill in .agents/skills/",
          "Cursor: 3 files, 1 skill in .agents/skills/, 1 rule",
          "Gemini CLI: 3 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 2 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network yes (listing says yes)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "No cross-file references to check",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "5 files, 6.4 KB total; largest AGENTS.md 2.6 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "Static Astro site in the loadout's layout (content collection, layout, components) builds with the loadout applied and ships no client JavaScript",
        "details": [
          "Applied 6 loadout files at every agent's paths",
          "✓ install: npm ci --no-audit --no-fund",
          "✓ astro build: npm run build",
          "✓ check dist: node check-dist.mjs",
          "Covers: src/content.config.ts collection with a glob() loader and zod schema; getCollection + render (AGENTS.md)",
          "Covers: Typed Props, class:list and scoped styles in a component (Cursor rule)",
          "Covers: Every page has a unique <title> and meta description; no <script> shipped (\"ship no JavaScript unless a feature needs it\")"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/cloudflare-pages-deploy/SKILL.md",
          "target": ".claude/skills/cloudflare-pages-deploy/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/skills/cloudflare-pages-deploy/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/astro-static-site/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/cloudflare-pages-deploy/SKILL.md",
          "target": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/skills/cloudflare-pages-deploy/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/astro-static-site/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": ".cursor/rules/astro-components.mdc",
          "target": ".cursor/rules/astro-components.mdc",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/.cursor/rules/astro-components.mdc",
          "role": "rules"
        },
        {
          "path": "skills/cloudflare-pages-deploy/SKILL.md",
          "target": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/skills/cloudflare-pages-deploy/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/astro-static-site/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/cloudflare-pages-deploy/SKILL.md",
          "target": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/skills/cloudflare-pages-deploy/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/astro-static-site/gemini-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/cloudflare-pages-deploy/SKILL.md",
          "target": ".agents/skills/cloudflare-pages-deploy/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/astro-static-site/1.0.0/skills/cloudflare-pages-deploy/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/astro-static-site/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/astro-static-site/source.zip"
}