feat: make blog content consumable by AI agents - #15
Conversation
- Markdown twin of every post at /blog/<slug>.md, generated at build time from the content collection - /llms.txt index and /llms-full.txt full-content file (llmstxt.org) - RSS feed at /rss.xml with autodiscovery link - JSON-LD BlogPosting structured data and rel=alternate markdown link on every post - Unit tests for the markdown/llms.txt generators (vitest) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
web | 201281e | Commit Preview URL Branch Preview URL |
Jul 17 2026, 01:06 AM |
- Escape < in JSON-LD so set:html cannot break out of <script> - Format agent markdown dates in America/New_York (not UTC day) - Restore valid package name workingdevshero-web - Add unit test for late-evening offset pubDate calendar day Co-Authored-By: Grok <noreply@x.ai>
|
Fixed review body nit on package name in
Also addressed the two inline suggestions in the same commit:
Verification: |
bobbyg603
left a comment
There was a problem hiding this comment.
Summary
Solid, well-scoped PR that makes the blog consumable by AI agents via /llms.txt, /llms-full.txt, per-post .md twins, RSS, and BlogPosting JSON-LD plus discovery links. Core logic is cleanly centralized in agentContent.ts with good unit coverage; static build output confirms routes, draft filtering, sorting, and content shape all work. Residual risks are small hardening gaps (JSON-LD injection edge case, UTC calendar-date formatting) and an incidental invalid package name change, not structural flaws.
Issue counts by severity
- bugs: 0
- suggestions: 2
- nits: 1
Issues outside the diff
These findings reference lines that are not present in the diff and could not be posted as inline comments:
- [nit] package.json:2 -- Package
namechanged fromworkingdevshero-webtoworkingdevshero/web. An unscoped npm name cannot contain/; scoped packages must be@scope/name. This is unrelated to the agent-content feature and can confuse tooling that validates package names. The same edit also flipslicensefrom ISC to MIT in the lockfile root metadata.- Suggestion: Keep
workingdevshero-web, or use@workingdevshero/webif scoping is intentional. Revert the name/license drive-by if it was accidental.
- Suggestion: Keep
| <BaseLayout title={title} description={description} image={heroImage} type="article"> | ||
| <Fragment slot="head"> | ||
| <link rel="alternate" type="text/markdown" href={markdownURL} title="Markdown version of this article" /> | ||
| <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} /> |
There was a problem hiding this comment.
[suggestion] JSON-LD is injected with set:html={JSON.stringify(jsonLd)}. JSON.stringify does not escape <, so a title/description containing </script> (or similar) would break out of the <script type="application/ld+json"> tag in HTML. Content is author-controlled today, so risk is low, but this is a classic footgun if frontmatter ever includes that sequence.
Suggestion: Serialize safely, e.g. JSON.stringify(jsonLd).replace(/</g, '\\u003c'), or use a small helper that always Unicode-escapes < before set:html.
There was a problem hiding this comment.
Fixed in 201281e. JSON-LD is now serialized with < escaped to \\u003c before set:html, so a title/description containing </script> cannot break out of the <script type="application/ld+json"> tag.
| } | ||
|
|
||
| function formatDate(date: Date): string { | ||
| return date.toISOString().split('T')[0]; |
There was a problem hiding this comment.
[suggestion] formatDate uses date.toISOString().split('T')[0], which reports the UTC calendar day. Posts with evening offset timestamps (e.g. 2026-07-11T21:00:00-04:00 → 2026-07-12T01:00:00.000Z) would publish as the next day in markdown/llms output. Current posts happen to stay on the intended day, but the site already uses offset-aware pubDates, so this can drift.
Suggestion: Format a stable calendar date in a fixed zone (e.g. America/New_York via Intl.DateTimeFormat with timeZone + year/month/day), or document that pubDate must be UTC-midnight / date-only so UTC truncation matches author intent. Add a unit test for a late-evening offset timestamp.
There was a problem hiding this comment.
Fixed in 201281e. formatDate now uses Intl.DateTimeFormat with timeZone: 'America/New_York' (en-CA → YYYY-MM-DD) so late-evening offset timestamps stay on the local calendar day. Added a unit test for 2026-07-11T21:00:00-04:00.
Summary
Makes every blog article (current and future) directly consumable by AI agents, with zero per-post maintenance — everything derives from the content collection at build time.
/blog/<slug>.md— title, metadata, canonical URL, and the raw article body served astext/markdown(~5-10x fewer tokens than the HTML page)/llms.txt(llmstxt.org) — site overview plus a linked index of every post's.mdtwin, newest first/llms-full.txt— full text of all articles in one fetch (~80 KB today)BlogPostingstructured data on every post — author, dates, publisher, keywords, for answer-engine attribution/rss.xmlvia@astrojs/rss, with an autodiscovery<link>on every pagerel=alternate type=text/markdownlink on each post pointing at its.mdtwinImplementation notes
src/utils/agentContent.ts; the Astro endpoints (llms.txt.ts,llms-full.txt.ts,blog/[...slug].md.ts) are thin wrappers, so future posts are picked up automatically on every buildBaseLayoutgained aheadslot so pages can inject head tags (used byBlogPostfor JSON-LD + alternate links).wrangler/(localwrangler devcache) to.gitignoreTesting
npm test— 10 new vitest unit tests covering the markdown/llms.txt/llms-full.txt generators (newtestscript)npm run build— verifieddist/containsllms.txt,llms-full.txt,rss.xml, and a.mdfile per post; JSON-LD and alternate links present in built post HTML; sitemap not polluted by the new endpointsdist/throughnpx wrangler dev(the production serving layer) and confirmed 200s with correct content types:text/markdownfor.md,text/plainfor the llms files,application/xmlfor RSS🤖 Generated with Claude Code