Skip to content

feat(seo): config-driven llms.txt generator in seo-static plugin - #4274

Merged
PierreBrisorgueil merged 3 commits into
masterfrom
feat/4269-llms-txt-generator
Jun 8, 2026
Merged

PierreBrisorgueil merged 3 commits into
masterfrom
feat/4269-llms-txt-generator

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Closes #4269

What

Adds a generic, config-driven buildLlmsTxt(llmsConfig, app) builder to src/lib/plugins/seo-static.js, mirroring buildRobotsTxt / buildSitemapXml / buildManifestJson, and emits llms.txt from the generateBundle hook (prod build only, gated on config).

Why

llms.txt is the emerging LLM/answer-engine site entry point (AEO). Keeping it in the stack plugin lets every downstream project ship its own llms.txt from config, consistent with how robots/sitemap/manifest are already generated — instead of a hand-maintained public/llms.txt.

Rendering

# title (fallback app.title'App'), > summary, intro, per-section ## title with items as - [label](url): note (link) or - label: note (bullet), then body passthrough; single trailing newline. Sections without a title and items without a label are skipped (no undefined in output); app is null-guarded.

Tests

src/lib/plugins/tests/seo-static.unit.tests.jsdescribe('buildLlmsTxt'): disabled→null, undefined→null, title fallback, summary/intro, link-vs-bullet + note, body passthrough + trailing newline, app=undefined fallback, malformed-entry skipping, testConfig fixture render. Plugin emit-count test updated 3→4. Generic — zero project-specific strings.

Scope

  • Stack (devkit Vue) only. Generic generator + unit test + test fixture.
  • Out of scope (downstream): the Trawl seo.llms content + /update-stack propagation land on GTM driver C1-1 (board comes-io/projects/3) in comes-io/trawl_vue.

🤖 First run of the "GTM Engineer generates the issue → the dev flow dépiles it" pattern. Driver: comes-io/projects/3 item C1-1. Plan: infra/docs/superpowers/plans/2026-06-08-trawl-llms-txt.md. Draft — human merge gate (do not self-merge).

Summary by CodeRabbit

  • New Features

    • Added automatic generation of llms.txt file during production builds with configurable options
    • Support for customizing content structure including summary, introduction, sections, and body
    • Ability to enable/disable LLM content generation via configuration settings
  • Tests

    • Added comprehensive test coverage for the new file generation functionality

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f432a7ba-c2f3-428d-9413-b8643983788a

📥 Commits

Reviewing files that changed from the base of the PR and between 2e5bad4 and 19ef70b.

📒 Files selected for processing (3)
  • src/config/defaults/test.config.js
  • src/lib/plugins/seo-static.js
  • src/lib/plugins/tests/seo-static.unit.tests.js

Walkthrough

This PR introduces config-driven llms.txt generation to the SEO static plugin. A new buildLlmsTxt builder generates LLM-friendly Markdown with title, summary, intro, sections with optional links, and body content. The plugin's generateBundle hook emits llms.txt when enabled, following the existing pattern for robots.txt, sitemap.xml, and manifest.json.

Changes

LLMs.txt Generation Feature

Layer / File(s) Summary
LLMs.txt builder function
src/lib/plugins/seo-static.js
Exports buildLlmsTxt(llmsConfig, app) that returns null when disabled, or a Markdown string with title, summary, intro, sections with items (rendered as links or bullets with notes), and body. JSDoc updated to document llms.txt as an emitted asset.
Plugin hook integration
src/lib/plugins/seo-static.js
Extends generateBundle to call buildLlmsTxt and conditionally emit llms.txt asset when the builder returns non-null content.
Test configuration and unit tests
src/config/defaults/test.config.js, src/lib/plugins/tests/seo-static.unit.tests.js
Test fixture defines example llms config structure. Updated imports and generateBundle test to expect four emitted assets. New buildLlmsTxt test suite validates null behavior when disabled, markdown structure, link/bullet formatting, notes, body passthrough, and fixture rendering.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • pierreb-devkit/Vue#3658: Extends the same seo-static.js plugin to add llms.txt generation alongside existing SEO asset emission.

Suggested labels

Feat

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a config-driven llms.txt generator to the seo-static plugin.
Description check ✅ Passed The description provides what changed, why it matters, rendering details, test coverage, and scope—all required template sections are addressed comprehensively.
Linked Issues check ✅ Passed The PR implements all acceptance criteria from #4269: buildLlmsTxt returns null when disabled, renders markdown with proper structure, emits llms.txt from generateBundle, includes unit tests, and contains zero project-specific strings.
Out of Scope Changes check ✅ Passed All changes are directly aligned with #4269 scope: the generic builder function, plugin integration, unit tests, and test fixture. Downstream GTM propagation is correctly marked as out-of-scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/4269-llms-txt-generator

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: one or more packages not found in the registry.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.58%. Comparing base (2e614db) to head (19ef70b).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4274   +/-   ##
=======================================
  Coverage   99.57%   99.58%           
=======================================
  Files          31       31           
  Lines        1182     1200   +18     
  Branches      340      352   +12     
=======================================
+ Hits         1177     1195   +18     
  Misses          5        5           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@PierreBrisorgueil
PierreBrisorgueil marked this pull request as ready for review June 8, 2026 12:28
Copilot AI review requested due to automatic review settings June 8, 2026 12:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class llms.txt generation to the existing seo-static Vite plugin, following the same config-driven pattern as robots.txt, sitemap.xml, and manifest.json, so downstream apps can ship llms.txt via config rather than a static public file.

Changes:

  • Add buildLlmsTxt(llmsConfig, app) builder and emit llms.txt as a production-build asset when enabled.
  • Add unit tests covering buildLlmsTxt rendering, null behavior, skipping malformed entries, and fixture rendering.
  • Extend test.config.js fixture with a generic app.seo.llms example used by the tests.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/lib/plugins/seo-static.js Implements buildLlmsTxt and emits llms.txt from generateBundle when enabled.
src/lib/plugins/tests/seo-static.unit.tests.js Adds buildLlmsTxt unit tests and updates the plugin emit-count assertion from 3 → 4.
src/config/defaults/test.config.js Adds a generic seo.llms fixture used by the new tests.

Comment on lines +163 to +165
* @param {object|undefined} llmsConfig - llms configuration object
* @param {object} app - app-level config (title fallback)
* @returns {string|null} llms.txt content or null if disabled
@PierreBrisorgueil
PierreBrisorgueil merged commit e9cedad into master Jun 8, 2026
8 checks passed
@PierreBrisorgueil
PierreBrisorgueil deleted the feat/4269-llms-txt-generator branch June 8, 2026 19:43
PierreBrisorgueil pushed a commit that referenced this pull request Jul 1, 2026
# [2.2.0](v2.1.0...v2.2.0) (2026-07-01)

### Bug Fixes

* **auth:** failed login no longer shows a 'success: Signed out' toast ([#4309](#4309)) ([464e022](464e022)), closes [#4305](#4305) [#4305](#4305)
* **billing:** nav compute gauge — admin ∞ display + login refresh ([#4260](#4260), [#4261](#4261)) ([#4268](#4268)) ([2e614db](2e614db))
* **billing:** update VProgressLinear snapshots for Vuetify 4.1.0 ([#4257](#4257)) ([549bb39](549bb39))
* **configGuard:** warn-only by default, throw only on opt-in strict flag ([#4263](#4263)) ([f37050f](f37050f)), closes [#4258](#4258)
* **core:** center footer version badge + show backend API version ([#4275](#4275)) ([a17795b](a17795b))
* **docs:** left-align /docs home search hero + uncap block trigger width ([#4323](#4323)) ([b701c3d](b701c3d))
* **e2e:** resolve flaky Playwright webServer boot-race ([#4378](#4378)) ([0fc1886](0fc1886)), closes [#4372](#4372)
* **invitations:** inert referrals state when public signup is open ([#4306](#4306)) ([b1cb7d5](b1cb7d5)), closes [pierreb-devkit/Node#3833](pierreb-devkit/Node#3833)
* **invitations:** pre-P9 UI hardening — revoked status, admin error banner, canonical verifyInvite ([#4293](#4293)) ([d977558](d977558)), closes [#4291](#4291)
* **legal:** patch happy-dom Node.prototype.nodeName for DOMPurify 3.4.8 ([#4240](#4240)) ([#4256](#4256)) ([f0b0e41](f0b0e41))
* **skills/update-stack:** switch drift gate scan to git diff (catches missing-locally case) ([#4244](#4244)) ([32bd2d3](32bd2d3)), closes [#4233](#4233)
* **theme:** re-apply OS theme post-hydration when dark='auto' (prerender light-lock) ([#4262](#4262)) ([9aa74cf](9aa74cf)), closes [#4230](#4230)
* **ui:** invitations/org surfaces polish — app-level nudge, toolbar slot, card rhythm ([#4301](#4301)) ([d203d84](d203d84)), closes [#toolbar](https://github.com/pierreb-devkit/Vue/issues/toolbar) [#toolbar](https://github.com/pierreb-devkit/Vue/issues/toolbar)
* Vue low-severity security hardening bundle (tabnabbing, href scheme, console creds, headers, npm ci, stale key) ([#4316](#4316)) ([9b21450](9b21450))

### Features

* **admin:** activation-aware tabs, readiness badge, activity search, invite copy-link ([#4308](#4308)) ([da419ac](da419ac)), closes [#4295](#4295) [#4297](#4297) [pierreb-devkit/Node#3836](pierreb-devkit/Node#3836) [#4296](#4296) [pierreb-devkit/Node#3834](pierreb-devkit/Node#3834)
* **app/router:** add registerDownstreamRoutes extension hook for downstream route injection ([#4242](#4242)) ([54358f0](54358f0))
* **auth:** add beta seat getters to auth store ([#4229](#4229)) ([0560aab](0560aab))
* **auth:** reframe post-signup org-setup step as friendly workspace setup ([#4373](#4373)) ([27ec893](27ec893))
* **billing:** grouped feature sections + plan inheritance on pricing cards ([#4382](#4382)) ([ded622b](ded622b))
* **billing:** surface capacity equivalences on the nav compute gauge ([#4350](#4350)) ([dfeef01](dfeef01)), closes [#4349](#4349)
* **configGuard:** block dev-host/port leak into production config ([#4235](#4235)) ([10c3f17](10c3f17)), closes [#949](#949) [pierreb-projects/infra#38](https://github.com/pierreb-projects/infra/issues/38) [#949](#949) [trawl_vue#949](https://github.com/trawl_vue/issues/949)
* **core+home+tasks:** promote 4 generic improvements from trawl downstream ([#4239](#4239)) ([a00caa5](a00caa5))
* **docs:** cross-guide #anchor link rewrite in the article renderer ([#4343](#4343)) ([a89331c](a89331c)), closes [#anchor](https://github.com/pierreb-devkit/Vue/issues/anchor) [#4334](#4334)
* **docs:** in-app docs module (/docs) + docs-aware SEO (flag-gated, default off) ([#4319](#4319)) ([32e3948](32e3948)), closes [#1](#1) [#2](#2) [#3](#3) [#4](#4) [#5](#5) [#6](#6) [#7](#7) [#8](#8) [#9](#9) [#11](#11) [#4310](#4310)
* **docs:** link the OpenAPI reference from the docs nav ([#4330](#4330)) ([1b5c8dd](1b5c8dd))
* **footer:** display app version from DEVKIT_VUE_app_version build-arg ([#4259](#4259)) ([#4267](#4267)) ([14fe314](14fe314))
* **home+auth:** promote external-link safety + org-setup error UX from trawl ([#4237](#4237)) ([8e0be0f](8e0be0f)), closes [pierreb-projects/infra#38](https://github.com/pierreb-projects/infra/issues/38)
* **invitations:** referrals summary + rewards placeholder on account view (P8b) ([#4292](#4292)) ([1c0db9d](1c0db9d)), closes [#5](#5) [#4282](#4282)
* **invitations:** standalone Vue module + account Referrals tab + router gap-fixes (P6) ([#4289](#4289)) ([4366fa2](4366fa2))
* **organizations:** add-member UI + pending-invitations list + accept (P5b) ([#4288](#4288)) ([96b5b14](96b5b14)), closes [#4281](#4281)
* **organizations:** owner_add lifecycle surfaces + signup error detail ([#4307](#4307)) ([e70dbf7](e70dbf7)), closes [pierreb-devkit/Node#3831](pierreb-devkit/Node#3831) [pierreb-devkit/Node#3832](pierreb-devkit/Node#3832)
* **seo:** config-driven llms.txt generator in seo-static plugin ([#4274](#4274)) ([e9cedad](e9cedad)), closes [#4269](#4269)
* **seo:** per-route self-referential canonical + og:url ([#4338](#4338)) ([94234ff](94234ff))
* **skills/update-stack:** drop ledger condition + auto-derive scan list ([#4232](#4232)) ([7be1220](7be1220)), closes [#4231](#4231) [infra#37](https://github.com/infra/issues/37)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Generic config-driven llms.txt generator in seo-static.js

2 participants