jekyll-agent-markdown
Export Jekyll content as Markdown siblings and curated llms.txt and llms-full.txt indexes.
AI agents and LLM crawlers read Markdown better than rendered HTML. This plugin publishes a Markdown copy of each exported post, page, and collection document next to its HTML. It also writes the index files described by the llms.txt convention.
Installation
Add the gem to your site's Gemfile.
group :jekyll_plugins do
gem "jekyll-agent-markdown"
end
Install dependencies.
bundle install
The :jekyll_plugins group already enables the plugin.
If you keep the gem outside that group, list it under plugins: instead.
plugins:
- jekyll-agent-markdown
Quick Start
Configure the sources you want and build the site.
url: https://example.com
collections:
guides:
output: true
agent_markdown:
pages: true
collections:
- guides
bundle exec jekyll build
The build now writes:
/llms.txt, an index of the exported documents- a Markdown sibling for every post, Markdown page, and
guidesdocument, such as/guides/start.mdnext to/guides/start/
Usage
Enable the sources you want.
- Posts export by default.
- Markdown-backed pages need
pages: true. - Markdown collection documents need
collectionsandoutput: true. - Non-Markdown pages, non-Markdown collection documents, and generated pages are ignored.
- A missing or non-output collection fails the build.
- A selected collection with a Markdown document lacking a public document URL fails.
- The reserved
postscollection cannot appear incollections.
Use site-level agent_markdown: true to enable the defaults.
Use site-level agent_markdown: false to disable every export.
Boolean settings accept true and false scalars.
They also accept "false", "no", and "off" as false.
False-style strings are case-insensitive and work in front matter.
Unknown keys, wrong types, and invalid combinations fail the build.
Duplicate collection names also fail the build.
Markdown siblings follow the permalink.
| Post URL | Markdown URL |
|---|---|
/foo/ |
/foo.md |
/foo.html, /foo.htm |
/foo.md |
/foo.html/ |
/foo.html.md |
/foo/index.html |
/foo.md |
/foo |
/foo.md |
/ |
/index.md |
Each post export ends with a metadata footer built from the available dates and the site author.
Disable it with include_dates: false and include_author: false.
Post body.
---
Published at: 2026-01-01 | Updated at: 2026-01-05 | Author: Example Author
Set per-document settings in front matter.
agent_markdown: false
agent_markdown:
export: true
index: true
section: Documentation
optional: false
include_document_header: true
Use optional: true without section for the Optional section.
Front matter cannot enable a globally excluded source kind.
export: false also disables index.
Do not combine export: false with index: true.
Do not combine optional: true with section.
Add include_document_header: true to prepend a small header.
That setting needs a valid absolute url.
A valid url uses HTTP(S) without credentials, a query, or a fragment.
The header holds the title, the front matter description when present, and a Source: link to the HTML page.
The body below the divider stays untouched.
# About this site
A one-line description from front matter
Source: https://example.com/about/
---
## What we do
Raw page body.
Use llms_txt: true for the compact index.
llms.txt has two layouts.
With the posts-only defaults it keeps the original single-list layout, byte-for-byte identical with releases before 0.4.0.
Enabling pages, collections, or descriptions, or using section or optional in front matter, switches to the sectioned layout shown below.
Add include_descriptions: true for sanitized descriptions.
Descriptions fall back to the document excerpt.
Use sort: asc or sort: desc for each section.
Set include_author: false or include_dates: false to trim metadata.
Mark a document optional: true to move it under Optional.
Default sections appear before custom sections.
Custom sections keep their first-occurrence order.
The Optional section always appears last.
# Example Site
> A short description
Author: Example Author
## Articles
- [First article](https://example.com/articles/first.md) | Published at: 2026-01-01
## Pages
- [About](https://example.com/about.md): About page
## Guides
- [Getting started](https://example.com/guides/start.md)
## Optional
- [Reference](https://example.com/reference.md)
Use llms_full_txt: true for the full index.
It uses the same document selection as llms.txt.
It still works when llms_txt: false.
Each entry expands into a full Markdown document block.
Set url to a valid absolute HTTP(S) URL first.
The generator warns once when the rendered file exceeds 1 MiB.
# Example Site
## Articles
### [First article](https://example.com/articles/first.md)
Source: https://example.com/articles/first/
Body of the first article.
Add the alternate link from a layout.
{% agent_markdown_link %}
It renders a discovery link for the current page.
<link rel="alternate" type="text/markdown" href="/about.md">
The tag is baseurl-aware.
It is empty for opted-out or collided documents.
Use page.agent_markdown_url when custom markup needs the generated path.
On collision, existing destinations win.
Generated claims run through posts, then pages, then configured collections.
Later exports skip with a warning and receive no agent_markdown_url.
They are also omitted from both indexes.
A committed llms.txt or llms-full.txt also wins on collision.
Collision detection only sees files Jekyll knows about when this plugin runs at priority :low.
Another plugin generating files at priority :lowest runs later and can still claim the same destination.
Options
Boolean settings accept true, false, and false-style strings.
Unknown keys and invalid values raise Jekyll::Errors::FatalException.
These are the complete defaults.
agent_markdown:
posts: true
pages: false
collections: []
llms_txt: true
llms_full_txt: false
include_descriptions: false
include_document_header: false
include_author: true
include_dates: true
sort: desc
| Option | Default | Notes |
|---|---|---|
posts |
true |
Export posts. |
pages |
false |
Export pages. |
collections |
[] |
Export output collections named here. |
llms_txt |
true |
Write llms.txt. |
llms_full_txt |
false |
Write llms-full.txt. |
include_descriptions |
false |
Append sanitized descriptions to llms.txt. |
include_document_header |
false |
Prepend document headers to Markdown exports. |
include_author |
true |
Include author metadata. |
include_dates |
true |
Include published and updated dates. |
sort |
desc |
Order each section by normalized publish date. |
The default llms_txt warns and skips when url is missing or invalid.
Explicitly configuring llms_txt makes an invalid url fatal.
llms_full_txt needs a valid absolute url whenever enabled.
Document headers enforce the same URL rules only when enabled.
Deployment
The plugin writes static files; any host can serve them as-is.
Content negotiation is optional and happens at the host, not in Ruby.
A negotiating host serves the Markdown sibling when a request prefers text/markdown.
It falls back to HTML when the sibling does not exist.
examples/ ships dependency-free recipes that share one negotiation contract:
All three negotiate only GET and HEAD requests and skip static assets.
See the deployment guide for the full negotiation contract and host setup.
Compatibility
Ruby 3.2 or newer is required.
Jekyll 4.3 or newer is required, but Jekyll 5 is unsupported.
The plugin writes files only.
There is no automatic injection into rendered HTML.
Layouts must invoke {% agent_markdown_link %} explicitly.
It does not handle content negotiation.
It does not generate crawler policy, analytics, middleware, response headers, or crawler permissions.
It publishes raw Markdown without rendering Liquid.
Post exports append enabled date and author metadata.
Document headers prepend content only when enabled.
Destination ownership is normalized across case, Unicode normalization, encoded aliases, and file-versus-directory conflicts.
Limitations
Page and collection exports require authored Markdown and public URLs. Generated pages and non-Markdown page or collection sources are ignored. Raw Liquid may expose source directives to Markdown readers. GitHub Pages safe mode may require an external build pipeline.
Development
Run the tests and RuboCop.
bundle install
bundle exec rake
License
Apache License 2.0.