{"id":51478341,"url":"https://github.com/jsw-teams/pagekiln","last_synced_at":"2026-07-06T23:30:43.989Z","repository":{"id":368194275,"uuid":"1283898178","full_name":"jsw-teams/pagekiln","owner":"jsw-teams","description":"Hexo-inspired static site builder with theme-first customization, site operations assets, and agent discovery.","archived":false,"fork":false,"pushed_at":"2026-06-29T12:12:45.000Z","size":4219,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T13:26:32.341Z","etag":null,"topics":["agent-discovery","astro","llms-txt","markdown","static-site-builder","static-site-generator","webmcp"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsw-teams.png","metadata":{"files":{"readme":"README.en.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-29T10:38:57.000Z","updated_at":"2026-06-29T12:14:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jsw-teams/pagekiln","commit_stats":null,"previous_names":["jsw-teams/pagekiln"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/jsw-teams/pagekiln","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsw-teams%2Fpagekiln","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsw-teams%2Fpagekiln/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsw-teams%2Fpagekiln/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsw-teams%2Fpagekiln/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsw-teams","download_url":"https://codeload.github.com/jsw-teams/pagekiln/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsw-teams%2Fpagekiln/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35209350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-06T02:00:07.184Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-discovery","astro","llms-txt","markdown","static-site-builder","static-site-generator","webmcp"],"created_at":"2026-07-06T23:30:43.869Z","updated_at":"2026-07-06T23:30:43.974Z","avatar_url":"https://github.com/jsw-teams.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pagekiln\n\n[简体中文 README](README.md)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"content/assets/icon-192.png\" alt=\"Pagekiln project icon\" width=\"160\"\u003e\n\u003c/p\u003e\n\nPagekiln is a Hexo-inspired static site and blog builder. The current stable\nnpm version is `1.1.0`. Site settings live in `config.yml`, posts and pages\nlive in `content/`, themes live in `themes/\u003cname\u003e/`, and generated output goes\nto `dist/`.\n\nThe intent is simple: if you only want to publish content, edit `content/` like\na normal Markdown site; if you need customization, move into config, themes, and\nbuilder extensions. Since `1.1.0`, the default starter stays neutral: it does\nnot force blog archives, categories, tags, or search pages. Those surfaces are\ncreated by `content/pages` files and dynamic slots. The default theme still provides\nreusable locales, posts, feeds, sitemap, robots, llms, agent discovery, consent\npreferences, and optional third-party script loading.\n\n## Quick Start\n\nFor a blog or regular content site, you do not need to learn theme development\nfirst. Install pagekiln, initialize the site, update config and site identity\nassets, then use `pagekiln s` for preview, `pagekiln g` for generation, and\n`pagekiln c` for checks.\n\n### How To Install pagekiln\n\nCreate a site directory and install Pagekiln:\n\n```bash\nmkdir my-site\ncd my-site\nnpm i pagekiln\nnpx pagekiln init .\n```\n\n`pagekiln init` uses the neutral root project template bundled in the npm\npackage. The template does not include production or preview secrets. Forks and\ndownstream projects should set their own site name, `siteUrl`, analytics,\nrobots policy, deployment target, and environment configuration.\n\nWhen debugging the Pagekiln framework repository itself, you can call the CLI\ndirectly:\n\n```bash\nnode src/bin/pagekiln.mjs init my-site\n```\n\n### How To Run pagekiln\n\nCommands for Hexo users:\n\n```bash\npagekiln s\npagekiln g\npagekiln c\n```\n\n- `pagekiln s` / `pagekiln server` maps to `hexo server` / `hexo s` and starts local live preview.\n- `pagekiln g` / `pagekiln generate` maps to `hexo generate` / `hexo g` and writes `dist/`.\n- `pagekiln c` / `pagekiln check` verifies `dist/`, theme assets, sitemap, content-driven feeds/search indexes, agent discovery, and WebMCP bootstrap.\n\nLocal preview:\n\n```text\nhttp://127.0.0.1:4173/\n```\n\n`pagekiln s` polls `content/`, `themes/`, and `config.yml` every 10 seconds. It\nrebuilds only after detected changes. Build errors stay visible in the browser\nand do not stop the preview process. Changes to\n`content/pages/\u003cslug\u003e/index.\u003clocale\u003e.md` prefer page-level incremental output.\n\n`pagekiln init` writes compatibility scripts such as `generate`, `server`, and\n`check` into the new project's `package.json`; public docs should prefer\n`pagekiln g/s/c`. The full commands `pagekiln generate/server/check` also work.\n\n### How To Configure Site Config\n\n`config.yml` is the site-level entry point. It is not just a build-parameter\nappendix; it is the main structured source for secondary development and agent\ncollaboration.\n\nMinimal example:\n\n```yaml\nsiteUrl: https://example.com\ndefaultLocale: zh-CN\nactiveLocales:\n  - zh-CN\n  - zh-TW\n  - en\ntheme:\n  name: default\n```\n\nGood `config.yml` responsibilities:\n\n- `siteName`, `description`, and `author`: titles, SEO summaries, JSON-LD, feed titles, and default page copy.\n- `defaultLocale` and `activeLocales`: localized routes, language links, and hreflang.\n- `nav.links` and `nav.utilityLinks`: main navigation, search/archive entries, and site-level links.\n- `footer`, `head`, `pwa`, and `icons`: footer content, head metadata, PWA output, and icon assets.\n- `plugins`, `features`, `featureScripts`, and `featureCategories`: search, comments, analytics, ads, WebMCP, and consent-aware loading.\n- `robots`, `llms`, `feed`, and `discovery`: `robots.txt`, `llms.txt`, `llms-full.txt`, `feed.xml` when public posts exist, `openapi.json`, `.well-known/api-catalog`, `.well-known/mcp/server-card.json`, and `_headers`.\n\nIf a site-operations file can be derived from `config.yml` or root `AGENTS.md`,\ndo not maintain a second handwritten public copy. For robots, llms, OpenAPI, API\ncatalog, MCP server card, `AGENTS.md`, or `_headers`, prefer config, the root\nguide, or dynamic generation.\n\n### How To Configure Site ICO And OG Images\n\nSite identity assets live under `content/assets/`:\n\n- `content/assets/icon-source.png` is the site icon source image. A customized site should replace at least this image. Use a clear square PNG, at least 512x512, and make sure it is still recognizable at 32px favicon size.\n- `content/assets/og-default-source.png` is the default Open Graph source image. A customized site should replace at least this image. Use a 1200x630 PNG/JPG that represents the site and works well in social, chat, and link-preview crops.\n\nAfter replacing `icon-source.png` or `og-default-source.png`, run:\n\n```bash\npagekiln g\n```\n\nThe builder generates the ICO, PWA icons, and OG share images from source\nartwork and writes them into `dist/`, so the site icon and link-preview assets\nmatch the project customization. Derived files may still live in\n`content/assets/` as template resources, but they are not the build entry point;\n`pagekiln g` reads source artwork first.\n\n## Start Writing\n\nWriting primarily happens in `content/pages/`; add `content/posts/` only when\nthe site needs a post collection. The whole `content/pages/` or `content/posts/`\ndirectory may be absent without affecting the build. With no custom pages,\nPagekiln generates a neutral root entry, locale homepage fallback, 404, and\nsite-discovery files instead of inventing a set of blog default pages. When\npublic posts exist, they enter post detail pages, feeds, sitemap, llms, and\nMarkdown mirrors. Pages own the homepage, about page, archive page, category\npage, tag page, search page, and other normal pages.\n\nPage routes are decided by `content/pages/*`. If `content/pages/archive`,\n`categories`, `tags`, or `search` is removed, the builder no longer invents\nthose blog-style pages. Missing `content/pages/` means no custom pages, not a\nbuild error; missing `content/posts/` means no public posts. Search indexes are pulled by the\n`\u003c!-- pagekiln:search-panel --\u003e` slot, so no search index, OpenAPI search path,\nor post-search discovery is generated unless a page uses the search component.\n\n### How To Write Your First Post\n\nPost path:\n\n```text\ncontent/posts/\u003cslug\u003e/index.\u003clocale\u003e.md\n```\n\nFor example:\n\n```text\ncontent/posts/hello-pagekiln/index.zh-CN.md\ncontent/posts/hello-pagekiln/index.en.md\n```\n\nFrontmatter example:\n\n```yaml\n---\ntitle: \"Post title\"\ndescription: \"SEO summary\"\ndate: \"2026-04-27\"\nupdated: \"2026-04-27\"\ntranslationKey: \"welcome\"\ntags: [\"Announcement\"]\ncategory: \"News\"\ndraft: false\nsitemap: true\ncover: \"\"\n---\n```\n\n`draft: true` excludes content from public pages, search indexes, sitemap, feeds,\nand llms files. `sitemap: false` excludes one post or page from the sitemap.\n\n### How To Edit Your Pages\n\nPage path:\n\n```text\ncontent/pages/\u003cslug\u003e/index.\u003clocale\u003e.md\n```\n\nDefault special pages use the same page system:\n\n```text\ncontent/pages/home/index.\u003clocale\u003e.md        # /\u003clocale\u003e/\ncontent/pages/archive/index.\u003clocale\u003e.md     # /\u003clocale\u003e/archive/ when the file exists\ncontent/pages/categories/index.\u003clocale\u003e.md  # /\u003clocale\u003e/categories/ when the file exists\ncontent/pages/tags/index.\u003clocale\u003e.md        # /\u003clocale\u003e/tags/ when the file exists\ncontent/pages/search/index.\u003clocale\u003e.md      # /\u003clocale\u003e/search/ when the file exists\n```\n\nMarkdown under `content/pages` may contain HTML directly. You can write\n`\u003cheader\u003e`, `\u003csection\u003e`, `\u003cimg\u003e`, small page scripts, and place dynamic\ncomponents exactly where they should render:\n\n```html\n\u003c!-- pagekiln:post-list --\u003e\n\u003c!-- pagekiln:pagination --\u003e\n\u003c!-- pagekiln:archive-list --\u003e\n\u003c!-- pagekiln:terms --\u003e\n\u003c!-- pagekiln:search-panel --\u003e\n\u003c!-- pagekiln:languages --\u003e\n```\n\nFor example, the homepage can write its own introduction in\n`content/pages/home/index.zh-CN.md`, then place `\u003c!-- pagekiln:post-list --\u003e`\nwhere the post list belongs. An archive page can write its own heading and then\nplace `\u003c!-- pagekiln:archive-list --\u003e`.\n\nSlot syntax is recognized globally, but context has two levels:\n\n- Base context: `search-panel` only needs the current `locale` and can be used on any page.\n- Homepage list context: `post-list` needs `posts`; `pagination` needs `page`, `totalPages`, and `pageUrl`.\n- Archive page context: `archive-list` needs `groups` and should only be used on archive pages such as `content/pages/archive`.\n- Term index context: `terms` needs `terms` and should only be used on category or tag index pages.\n- Translation context: `languages` needs `translations` for a page or article with localized counterparts.\n\nIf a context-dependent slot is placed on a page without the required data,\n`pagekiln c` reports the unresolved slot instead of silently dropping the\ncomponent.\n\nKeep one data input and one output path when developing slots: page renderers\npass only raw data they own to `replaceSlots()`, such as `posts`, `groups`,\n`terms`, `translations`, and pagination state; slot HTML is rendered only by\n`slotRegistry` in `src/lib/slots.mjs`. Do not pass pre-rendered HTML or renderer\nfunctions back into slot context.\n\nTreat slots as complete components. Do not duplicate what a slot already\nrenders. Avoid this:\n\n```html\n\u003c!-- pagekiln:search-panel --\u003e\n\u003cp\u003eEnter a query to start searching.\u003c/p\u003e\n```\n\nThe search panel owns its input, empty state, result count, and error state. Put\ndurable page explanation in the header, then place the slot.\n\n## Secondary Development\n\nSecondary development is for changing the visual system, page shell, theme\nscripts, plugin loading, or builder behavior. Normal sites should prefer\n`config.yml`, `content/`, and `themes/`; edit `src/` only when the theme API\ncannot express the behavior.\n\n### Understand This Project\n\nProject layout:\n\n```text\nconfig.yml              # Site-level config\ncontent/posts/          # Post Markdown\ncontent/pages/          # Page Markdown/HTML\ncontent/assets/         # Site operations icons, OG images, and derived assets\nbackend/                # Optional backend/cloud-function source, outside static frontend sources\nsrc/bin/pagekiln.mjs    # CLI entry point\nsrc/*.mjs               # Builder-side Node ESM modules\nsrc/scripts/*.mjs       # Internal builder helper commands\nsrc/pages/*.js|*.astro  # Astro routes and generated endpoints\nthemes/default/         # Default theme\ndist/                   # Generated output\n```\n\nThe build generates outputs from content triggers:\n\n- Root entry, locale homepage fallback, and 404; the build should still succeed when `content/pages/` is absent\n- Normal pages that exist under `content/pages/*`; `archive`, `categories`, `tags`, and `search` are optional pages, not builder-invented routes\n- Post detail pages, `feed.xml`, locale feeds, and Markdown mirrors when public posts exist\n- Post lists and pagination when homepage content uses `post-list` / `pagination` slots\n- Term detail pages when `content/pages/categories` or `content/pages/tags` exists and public posts provide term data\n- Search indexes, OpenAPI search paths, and post search discovery when a page uses the `search-panel` slot\n- `sitemap.xml`\n- `robots.txt`\n- `llms.txt` and `llms-full.txt`\n- `openapi.json`\n- `.well-known/api-catalog`\n- `.well-known/mcp/server-card.json`\n- `_headers`\n- `AGENTS.md`\n\nDo not edit `dist/` by hand. Root `AGENTS.md` is the project guide for Codex,\nClaude, and other coding agents. `pagekiln g` copies it to `dist/AGENTS.md` as\nthe deployed site-level agent guide.\n\nPagekiln is static-first, but it can be organized as a frontend + backend\nproject. When a site needs Cloudflare Pages Functions, Cloudflare Workers, a\nNode service, a traditional server, or another backend runtime, the source code\nmust live under `/backend` as an independent boundary instead of being mixed\ninto `content/`, `themes/`, or builder `src/`.\n\nBackend boundary guidance:\n\n- Keep `content/` and `themes/` as static frontend sources. `pagekiln g` writes HTML, CSS, JS, images, slot-pulled search indexes, post-driven feeds, and discovery files to `dist/` so they can keep high CDN cache hit rates.\n- `/backend` is the only place that should touch secrets, database connections, private API tokens, signing keys, privileged permissions, and write-side business logic. Cloudflare Pages Functions, Workers, queue consumers, webhooks, form handlers, login, payments, comment writes, and admin endpoints should follow this boundary.\n- `/backend` is the single source of truth for backend source. If the target platform needs functions inside the deployment output, a build script or deployment adapter may compile, copy, or map `/backend` into the appropriate runtime directory under `dist/`; do not hand-author or maintain backend files inside `dist/`.\n- Frontend files should call backend services through public endpoints like a normal web frontend. Endpoint paths are project decisions; Pagekiln should not require fixed URL naming.\n- `config.yml` should describe only the site-level public calling contract, such as base URL, endpoint keys, methods, consent category, CAPTCHA requirement, cache intent, and whether an endpoint appears in OpenAPI/API catalog/MCP discovery. Never put real secrets in `config.yml`.\n- Browser-side identifiers that third-party frontend services require publicly, such as a Cloudflare Web Analytics token, Google Ads client id, or Giscus repo id, should live in the corresponding theme plugin config, for example `themes/\u003cname\u003e/theme.yml` under `plugins.analytics` or `plugins.advertising`. Fill them only when the matching plugin has been developed and enabled. They are not backend secrets, but reusable default templates should still avoid real production values.\n- Admin endpoints, privileged endpoints, and internal tools should not appear in the public manifest, OpenAPI, API catalog, MCP server card, or frontend discovery.\n\n### How To Develop Themes\n\nSecondary development should not follow a fixed sequence. Identify the kind of\nsite first, then choose the smallest suitable boundary:\n\n- Content sites, documentation, and blogs: start with `config.yml`, `content/pages`, `content/posts`, and slot placement; do not copy a theme when the default theme already expresses the site.\n- Brand sites, product pages, portfolios, and campaign pages: usually copy `themes/default` to `themes/\u003cyour-theme\u003e`, update `theme.name`, then edit templates, CSS, page styles, and small browser scripts.\n- Tools, directories, case-study libraries, and product collections: use `content/pages` for editable page structure, then connect dynamic regions through theme templates and slots; enter `src/` only when a reusable data model or page type is missing.\n- Sites with forms, login, comment writes, payments, webhooks, or admin actions: keep the static frontend in `config.yml`, `content/`, and `themes/`; put all runtime backend and cloud-function source under `/backend`.\n- Pagekiln framework capabilities, reusable slots, site-operations outputs, asset generation, and checks: edit `src/`, then update README, AGENTS, and the check script.\n\nTheme layout:\n\n```text\nthemes/default/theme.yml         # Theme configuration\nthemes/default/theme.example.yml # Copyable reference\nthemes/default/i18n.yml          # Theme UI strings\nthemes/default/style.css         # Global theme CSS\nthemes/default/styles/*.css      # Page or feature CSS\nthemes/default/templates/*.html  # Page templates\nthemes/default/scripts/*.js      # Consent entry and feature scripts\n```\n\nDo not keep `default` as the long-term theme name for a new project. Rename the\ntheme directory to something project-specific, such as `themes/company-docs/`,\n`themes/product-site/`, or `themes/portfolio/`.\n\nCSS placement guidance:\n\n- Put small and reusable theme rules in `themes/\u003cname\u003e/style.css`; avoid loading extra render-blocking CSS files for page differences that are only a few dozen lines.\n- Split CSS into `themes/\u003cname\u003e/styles/*.css` and load it through `theme.yml` `pageStyles` only when the page CSS is substantial, low-reuse, or dedicated to complex layouts, animation, or component states for a small set of pages.\n- Keep clearly bounded feature styles, such as consent, search, comments, ads, gallery, or docs toc styles, separate when that helps feature/page loading.\n- Do not put production CSS in Markdown/HTML. `content/pages` owns content structure and dynamic slot placement; theme CSS owns the visual system.\n\nPlugins are optional. A simple site may use none; a larger site may enable\nsearch, comments, analytics, RUM, ads, maps, forms, commerce, or custom scripts.\nThe default theme loads only `scripts/consent.js` unconditionally. Before the\nuser saves preferences, comments, analytics, ads, and marketing scripts do not\nload. After consent is saved, the builder loads features by categories such as\n`necessary`, `preferences`, `analytics`, and `marketing`.\n\n### How To Develop Dynamic Slots\n\nAdd a new `\u003c!-- pagekiln:xxx --\u003e` slot only when users should control placement\nfrom Markdown/HTML while the builder owns generated output.\n\nPost lists, pagination, archives, term collections, language links, related\nposts, and search panels fit slots. Fixed copy, static links, static layout, and\none-off structures that Markdown/HTML can already express should stay directly\nin `content/pages`.\n\nWorkflow:\n\n1. Use a lowercase slot name such as `\u003c!-- pagekiln:relatedposts --\u003e`; use the matching camelCase key in code, such as `relatedPosts`. Do not keep compatibility aliases for old names.\n2. Declare the marker, context type, required fields, component HTML, and missing-context behavior in `src/lib/slots.mjs`.\n3. Pass only raw page data that each renderer owns, such as `posts`, `page`, `totalPages`, `pageUrl`, `groups`, `terms`, or `translations`.\n4. Do not pass pre-rendered HTML or renderer functions into slot context; the data input is the page renderer and the output path is `slotRegistry.render()`.\n5. Keep `{{{content}}}` in templates so Markdown and slot output flow into the theme.\n6. Put component strings in `themes/default/i18n.yml` and sync defaults in `src/i18n.mjs`.\n7. Attach CSS and JS through `theme.yml` using `pageStyles`, `pageScripts`, `featureScripts`, or `featureStyles`.\n8. Update README and `AGENTS.md`.\n9. Run `pagekiln g` and `pagekiln c`; make sure new context-dependent slots are caught when unresolved.\n\nA slot should own its accessible markup, loading state, empty state, error\nstate, and runtime behavior. It should not require users to add \"results will\nappear here\" style notes after the slot.\n\n### How To Develop The /src Builder\n\nNormal sites use the default `src/` bundled in the npm package and do not need\nto copy or maintain builder source. Enter `src/` only for Pagekiln framework\ndevelopment, maintaining a fork, or adding reusable build-time capabilities.\n\nUse this data flow when developing `src/`:\n\n1. `src/bin/pagekiln.mjs` receives CLI commands and dispatches `g/s/c` to `src/prebuild.mjs`, Astro build, `src/scripts/serve-public.mjs`, or `src/scripts/check-build.mjs`.\n2. `src/lib/content.mjs` reads `config.yml`, theme config, and `content/`, then prepares posts, pages, locales, terms, slot-pulled search, discovery, and site-operations data.\n3. `src/assets.mjs` runs during `pagekiln g` and prepares static assets, including site icons and OG output generated from `content/assets/icon-source.png` and `content/assets/og-default-source.png`.\n4. `src/templates.mjs` and `src/lib/theme-html.mjs` render data into HTML. Page structure that a theme can express should stay in `themes/\u003cname\u003e/templates/`.\n5. `src/pages/` contains Astro endpoints that publish builder data as HTML, XML, JSON, Markdown, OpenAPI, llms, and well-known discovery files.\n\n`src/` file map:\n\n- `src/prebuild.mjs` is the preparation step before `pagekiln g` enters Astro build. It reads site config and post data so content issues fail early.\n- `src/assets.mjs` owns build-time asset preparation: copying theme assets, copying `content/assets` site identity assets, generating favicon / app icons / `site.webmanifest` / default OG images, and writing them into `dist/`.\n- `src/og-images.mjs` crops post covers into 1200x630 share images, supports local and remote covers, and uses a manifest cache so unchanged images are not regenerated every build.\n- `src/i18n.mjs` stores built-in locales, language labels, date formatting, and default UI strings. Theme `i18n.yml` can override or extend these strings.\n- `src/templates.mjs` is the built-in fallback renderer for the HTML shell, SEO meta, Open Graph, JSON-LD, navigation, footer, WebMCP bootstrap, post cards, pagination, search panel, term lists, and default page rendering.\n- `src/lib/content.mjs` is the builder data core. It reads and merges `config.yml` with theme config, normalizes plugin and consent settings, renders Markdown, copies content images, loads posts and pages, and prepares data for localized routes, categories, tags, slot-pulled search indexes, feeds, sitemap, headers, robots, llms, OpenAPI, API catalog, and MCP server card.\n- `src/lib/slots.mjs` centralizes the `\u003c!-- pagekiln:xxx --\u003e` slot registry, context checks, search panel, archive list, and other builder-owned slot components.\n- `src/lib/theme-html.mjs` is the HTML theme adapter. It reads `themes/\u003cname\u003e/templates/*.html`, applies simple `{{ }}` / `{{{ }}}` replacements, and uses `src/lib/slots.mjs` to replace builder-owned slots such as post lists, pagination, archive lists, terms, search panel, and language links.\n- `src/scripts/check-build.mjs` and `src/scripts/serve-public.mjs` are internal commands called by `pagekiln c` and `pagekiln s`; `src/scripts/generate-neutral-assets.mjs` is a site-operations crop helper for framework development.\n- `src/pages/[...route].astro` is the Astro catch-all page output for static HTML routes returned by `buildHtmlPages()`.\n- `src/pages/404.astro` outputs the 404 page.\n- `src/pages/[feed].xml.js` and `src/pages/[locale]/feed.xml.js` output the global feed and locale feeds when public posts exist.\n- `src/pages/robots.txt.js`, `src/pages/llms.txt.js`, `src/pages/llms-full.txt.js`, `src/pages/openapi.json.js`, `src/pages/.well-known/api-catalog.js`, and `src/pages/.well-known/mcp/server-card.json.js` output site-operations and agent discovery files.\n- `src/pages/assets/[file].json.js` outputs JSON assets such as search indexes when a page uses the `search-panel` slot.\n- `src/pages/md/[locale]/posts/[slug].md.js` exposes Markdown mirrors when public posts exist, for agents and external tools.\n\nGuidance:\n\n- Edit `src/` only for reusable page types, site-operations outputs, slots, config merging rules, asset generation rules, or checks.\n- Do not put one site's colors, layout, interactions, third-party scripts, page copy, or images in `src/`; use `config.yml`, `content/`, `themes/`, or `/backend`.\n- After changing `src/`, run `pagekiln g` and `pagekiln c`. If you add a new framework contract, update README, AGENTS, and `src/scripts/check-build.mjs`.\n\n### Agent Collaboration\n\nRoot `AGENTS.md` is the project guide for Codex, Claude, and other coding\nagents.\n\nFor blogging:\n\n```text\nPlease read README.md, AGENTS.md, and config.yml first. I am using Pagekiln as a blog. Prefer config.yml and content/ edits; do not edit themes/ or src/ unless I explicitly ask for theme customization or builder behavior.\n```\n\nFor secondary development:\n\n```text\nPlease read AGENTS.md and config.yml first. Treat config.yml as the structured source for site name, locales, navigation, plugins, consent, footer, robots, llms, OpenAPI, API catalog, MCP server card, headers, and other site operations. Prefer config.yml, content/pages, and theme-level changes before editing src/.\n```\n\nFor sites with Cloudflare Pages Functions, Workers, form handlers, login,\nwebhooks, or another runtime backend:\n\n```text\nPlease read AGENTS.md, README.md, and config.yml first. Keep the static frontend in config.yml, content/, and themes/. All cloud-function and runtime backend source must live under /backend. A build script may generate platform deployment output from /backend into dist/, but do not hand-author dist/.\n```\n\nIf [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) or\n[anthropics/skills](https://github.com/anthropics/skills/) is installed, name\nthe relevant skill in the prompt. They are collaboration constraints, not runtime\ndependencies.\n\nWhen syncing localized pages, treat `zh-CN` as the source of content and\nstructure unless a locale-specific variation is intentional.\n\n### Deployment Notes\n\nPagekiln can be deployed to Cloudflare Pages or any static hosting service that\ncan serve `dist/`. This public repository does not commit real Cloudflare\ncredentials, account IDs, project names, zone IDs, or production deployment\nsecrets.\n\nFor Cloudflare Pages Git integration, configure your own project:\n\n```text\nBuild command: npm install \u0026\u0026 pagekiln g\nBuild output directory: dist\nNode.js version: 22.12 or newer\n```\n\nStable releases should update three places together: `package.json`, the npm\npackage, and GitHub Release notes. `1.1.0` has been published to npm. It covers\ncontent-driven page generation; fully optional `content/pages/` and\n`content/posts/` directories that still build successfully; post detail pages,\nfeeds, and Markdown mirrors only when public posts exist; search indexes pulled\nby the `search-panel` slot; unified slot context checks; the `pagekiln s`\nstartup 404 fix; and standard right-side placement for the consent panel close\nbutton. Before release, it passed `pnpm run build`, `pnpm run check`,\n`git diff --check`, and a preview root HTTP 200 check.\n\nPagekiln is licensed under `AGPL-3.0-or-later`. Modified, redistributed,\npublicly deployed, or downstream versions based on Pagekiln should keep their\ncorresponding source code open under the AGPL.\n\nPlease preserve the original author attribution: `Pagekiln by JSW Teams`, and\nkeep or equivalently display the `NOTICE` information and original repository\nlink.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsw-teams%2Fpagekiln","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsw-teams%2Fpagekiln","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsw-teams%2Fpagekiln/lists"}