@voicenter-team/nuxt-llms-generator
| Install | |
|---|---|
npm install @voicenter-team/nuxt-llms-generator |
|
| Latest Version: | 0.2.1 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 14, 2026 |
| Links: | npm |
🤖 LLMS Documentation Generator for Nuxt 3 and Nuxt 4
Turn Umbraco CMS content into AI-optimized documentation following the llms.txt standard
The generator writes llms.txt, llms-full.txt and one markdown file per page from your UmbracoData.json. Each page is rendered with a Mustache template written by Claude, one template per page type (TemplateAlias).
The work is split between two entry points:
| Entry point | When it runs | What it does | Calls AI |
|---|---|---|---|
| Nuxt module | nuxt build |
Renders the llms files from the templates committed to git | Never |
CLI npx nuxt-llms-generator templates |
Locally, by hand or from a commit hook | Generates missing or outdated page-type templates | Yes |
The build is therefore fast, deterministic, needs no API key and never fails because of this module. A new page type gets an AI template the next time a developer runs the CLI and commits the result; until then it is rendered with a built-in fallback template.
🔄 How It Works
flowchart LR
subgraph DEV["Developer machine (CLI)"]
DATA1[UmbracoData.json] --> CHECK{Page type has a<br/>template covering<br/>its fields?}
CHECK -->|yes| SKIP[Nothing to do]
CHECK -->|no| AI[Claude Code / Anthropic API<br/>writes the type template]
AI --> VALIDATE[Validate against every<br/>page of the type]
VALIDATE --> STORE[".llms/{site}/{Alias}.mustache<br/>+ templates.json"]
end
STORE -->|git commit| BUILD
subgraph CI["nuxt build (module)"]
BUILD[Committed templates] --> RENDER[Render every page]
DATA2[UmbracoData.json] --> RENDER
RENDER --> OUT[llms.txt<br/>llms-full.txt<br/>llms/*.md]
end
One template per page type
All pages that share a TemplateAlias share one template:
- The CLI merges every page of the type into a union schema of the fields that are filled on at least one page, including array item fields and content block types (up to 3 levels deep).
- Claude receives a synthetic sample built from that union, the list of fields that are filled on every page, and up to three example URLs. Optional fields must be wrapped in Mustache sections, so pages that leave them empty render cleanly.
- A new template is accepted only if it renders every page of the type without errors and without producing an empty document.
- Before rendering, each page is normalized against its type: fields the page lacks become
''. Without this, Mustache would look a missing field up in the parent context and repeat content from a neighbouring block.
Grow-only change detection
Each template is stored with the list of field paths it covers (templates.json). The CLI regenerates a template only when:
- the page type has no template yet;
- the stored template is broken (it no longer parses);
- a page of the type has a new filled field or a new content block type that the template does not cover.
Removing a field, emptying it, or changing content values never triggers regeneration. Running the CLI when nothing changed takes about a second and makes no AI calls.
🚀 Quick Start
Installation
npm install @voicenter-team/nuxt-llms-generator
The module supports Nuxt ^3.0.0 and ^4.0.0. Production builds have been verified with Nuxt 3.19.2 and 4.4.8.
1. Create the shared config file
Both entry points read llms.config.ts (or .mts, .js, .mjs, .cjs, .json) from the project root:
// llms.config.ts
import { defineLLMSConfig } from '@voicenter-team/nuxt-llms-generator/config'
export default defineLLMSConfig({
umbracoDataPath: 'public/UmbracoData.json',
templatesDir: '.llms',
finalOutputDir: 'public',
baseSiteUrlUmbracoDataKey: 'domain'
})
2. Register the module
// nuxt.config.ts
export default defineNuxtConfig({
modules: [ '@voicenter-team/nuxt-llms-generator' ]
})
Options set under llmsGenerator in nuxt.config.ts override the config file.
3. Generate the templates and commit them
npx nuxt-llms-generator templates
git add .llms
4. Build
npm run build
The build log reports how each page was rendered:
Rendered 422 pages: 406 with page-type templates, 0 with 0.1.x per-page templates, 16 with the built-in fallback; 0 skipped
No template for 2 page type(s), rendered with the fallback template: SupportPage (14 pages), SupportPages (1 page). Run `npx nuxt-llms-generator templates` and commit the result.
🏢 Multiple Sites
One config file can describe several sites. Shared options sit at the top level, per-site options sit in sites, and {site} in umbracoDataPath, templatesDir and finalOutputDir is replaced with the selected site name:
// llms.config.ts
import { defineLLMSConfig } from '@voicenter-team/nuxt-llms-generator/config'
export default defineLLMSConfig({
templatesDir: '.llms/{site}',
finalOutputDir: 'public',
umbracoDataPath: 'public/UmbracoData.json',
baseSiteUrlUmbracoDataKey: 'domain',
sites: {
'voice-he-prod': {},
'voice-en-prod': {},
'voice-landings-he-prod': {
missingTemplate: 'skip'
}
}
})
The site is selected with --site in the CLI and with the site option in Nuxt:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [ '@voicenter-team/nuxt-llms-generator' ],
llmsGenerator: {
site: process.env.SITE_ENV
}
})
npx nuxt-llms-generator templates --site voice-he-prod --data /tmp/voice-he-prod.json
Options are merged in this order, later sources win: built-in defaults, top-level options of the config file, sites[site], then nuxt.config.ts options or CLI flags. A path containing {site} without a selected site is a configuration error.
🖥 CLI
npx nuxt-llms-generator <command> [options]
| Command | Description |
|---|---|
templates (default) |
Generate missing or outdated page-type templates with AI |
render |
Render llms.txt, llms-full.txt and the page .md files from the stored templates, without Nuxt |
| Option | Description |
|---|---|
--config <file> |
Config file (default: llms.config.* in the current directory) |
--site <name> |
Site section of the config; replaces {site} in paths |
--data <file> |
Umbraco data JSON (overrides umbracoDataPath) |
--templates-dir <dir> |
Templates directory (overrides templatesDir) |
--out <dir> |
Output directory for render (overrides finalOutputDir) |
--type <alias> |
Only this page type; repeatable |
--force |
Regenerate the --type templates even when they are up to date |
--provider <name> |
AI provider to try (claude-code, anthropic-api); repeatable, in order |
--dry-run |
Show what would be generated or removed without calling AI or writing files |
-h, --help |
Show the help |
Behaviour that matters for scripts and commit hooks:
- stdout contains only the paths of written and removed files, relative to the current directory, one per line. Everything else goes to stderr.
- The exit code is 0 when templates are up to date, when they were generated, and also when no AI provider is available. It is 1 only for usage errors, invalid configuration, unreadable data, or unexpected failures.
- The CLI loads
.envfrom the config file's directory, soCLAUDE_CODE_OAUTH_TOKENandANTHROPIC_API_KEYcan live there. - It does not need Nuxt and does not start a Nuxt build.
Examples:
# See what is missing without calling AI
npx nuxt-llms-generator templates --dry-run
# Regenerate one page type
npx nuxt-llms-generator templates --type BlogArticlePage --force
# Use only the Anthropic API
npx nuxt-llms-generator templates --provider anthropic-api
Commit hook
Because stdout lists only changed files and the exit code is 0 when AI is unavailable, the CLI can run in a pre-commit hook without ever blocking a commit:
#!/bin/sh
# .husky/pre-commit
npx nuxt-llms-generator templates --site voice-he-prod | xargs -r git add
When nothing changed, the hook finishes in about a second without calling AI.
🧠 AI Providers
The CLI tries the providers in aiProviders order (default ['claude-code', 'anthropic-api']). A provider that fails at the account or connection level is skipped for the rest of the run; a template that fails validation is retried with the next provider.
If no provider is available, the CLI prints a warning, keeps the existing templates, and exits with 0. The affected page types keep rendering with their previous or fallback templates.
claude-code (default)
Uses the locally installed Claude Code CLI in headless mode with a Claude subscription instead of API credits. It is meant for developer machines, not CI.
The call has no tools, no MCP servers and no session persistence, runs in an empty temporary directory, and is a single turn:
claude -p --model claude-opus-5-5 --output-format json --tools "" --strict-mcp-config \
--system-prompt-file <prompt> --no-session-persistence --max-turns 1
ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are removed from the child process environment, so Claude Code always bills the subscription and never the API key.
Token setup
-
Install Claude Code:
npm install -g @anthropic-ai/claude-codeorbrew install --cask claude-code. The default modelclaude-opus-5-5needs version 2.1.280 or newer; update withclaude update(orbrew upgrade claude-code). -
Create a long-lived token:
claude setup-token, and sign in with the team account. -
Put the token in the project's local
.env:# .env (never commit this file) CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... -
Check it:
claude auth statusmust exit with 0.
⚠️
CLAUDE_CODE_OAUTH_TOKENis a personal subscription credential. Keep it in the local.envonly: never commit it, and never add it to Cloudflare or any other CI/CD environment.
Token renewal
Tokens from claude setup-token are valid for one year and are not refreshed automatically.
| Token | Account | Expires |
|---|---|---|
| Voicenter shared token | serhii.ko@voicenter.com | 2027-10-14 (created 2026-10-14) |
When it expires, the CLI warns that Claude Code is not signed in and exits with 0. To renew, run claude setup-token again, replace CLAUDE_CODE_OAUTH_TOKEN in .env on every machine that uses it, and update the date above.
anthropic-api
Uses the Anthropic Messages API with anthropicApiKey. It is used only when a key is configured; without one, the provider is skipped with a warning.
// llms.config.ts
export default defineLLMSConfig({
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
anthropicModel: 'claude-sonnet-5-5'
})
The key is never needed for nuxt build and is stripped from the Nuxt runtime config.
⚙️ Configuration Options
Core
| Option | Type | Default | Description |
|---|---|---|---|
umbracoDataPath |
string |
required | Path to UmbracoData.json |
templatesDir |
string |
.llms-templates |
Directory with the page-type templates and templates.json; commit it |
finalOutputDir |
string |
public |
Output directory for llms.txt, llms-full.txt and llms/*.md |
baseSiteUrl |
string |
- | Base URL for links, e.g. https://example.com; this or baseSiteUrlUmbracoDataKey is required |
baseSiteUrlUmbracoDataKey |
string |
- | SiteData key that holds the base URL |
missingTemplate |
'fallback' | 'skip' |
'fallback' |
What the build does with a page whose type has no template: render it with the built-in fallback template, or leave it out |
enableIndividualMd |
boolean |
true |
Write one .md file per page |
enableLLMSFullTxt |
boolean |
true |
Write llms-full.txt |
enableHtmlToMarkdown |
boolean |
true |
Convert HTML content to markdown with node-html-markdown |
Nuxt module only
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Turn the module off |
configFile |
string | false |
llms.config.* |
Config file relative to the Nuxt root; false ignores config files |
site |
string |
- | Selects sites[site] and replaces {site} in paths |
Template generation (CLI)
| Option | Type | Default | Description |
|---|---|---|---|
aiProviders |
('claude-code' | 'anthropic-api')[] |
['claude-code', 'anthropic-api'] |
Providers to try, in order |
claudeCodeModel |
string |
claude-opus-5-5 |
Model for claude-code |
claudeCodeCommand |
string |
claude |
Claude Code executable |
claudeCodeTimeoutMs |
number |
600000 |
Time limit for one Claude Code call (minimum 10000) |
anthropicApiKey |
string |
- | Key for anthropic-api |
anthropicModel |
string |
claude-sonnet-5-5 |
Model for anthropic-api |
anthropicTemperature |
number |
auto | Applied only on models that still accept it; omitted on newer models, where the API rejects it |
anthropicThinking |
'auto' | 'disabled' | 'adaptive' |
'auto' |
'auto' disables thinking on models where it is on by default, so it does not consume the output budget |
anthropicMaxOutputTokens |
number |
16000 |
max_tokens of the generation call; responses that hit it are rejected |
maxTokens |
number |
65000 |
Limit for the page-type sample sent to the model; larger samples are truncated |
maxConcurrent |
number |
3 |
Page types generated in parallel (1-10) |
enableAutoCleanup |
boolean |
true |
Remove stale template files after generation |
cleanupOrphaned / cleanupHidden |
boolean |
true |
Remove templates of page types that no longer have visible pages |
📁 Template Store
.llms/voice-he-prod/
├── templates.json # manifest: covered fields, page count, provider and model per type
├── BlogArticlePage.mustache # one template per page type
├── HomePage.mustache
└── ...
Commit the whole directory. The build reads it and never writes to it.
When rendering a page, the module uses the first of these that works:
- the page-type template
{TemplateAlias}.mustache; - a 0.1.x per-page template
{TemplateAlias}_{nodeID}.mustache(see Migration); - the built-in fallback template, unless
missingTemplateis'skip'.
A stored template that fails to render a page falls back to the next option instead of failing the build.
🔁 Migrating from 0.1.x
0.2.0 stops calling AI during nuxt build. To migrate:
- Update the package to
^0.2.0. - Move the options from
nuxt.config.tstollms.config.ts(optional, but the CLI needs either the file or flags). - Remove
anthropicApiKeyand anyenabled: Boolean(process.env.NUXT_ANTHROPIC_API_KEY)fromnuxt.config.ts, and delete the API key from the CI/CD environment. The build no longer needs it. - Run
npx nuxt-llms-generator templatesfor every site and commit the template directory.
Existing 0.1.x per-page templates ({Alias}_{nodeID}.mustache) keep rendering until their page type gets a type template. The CLI then deletes them, and deletes the old cache.json once no per-page templates remain. A full regeneration of all page types is a one-time manual step; it is never done by the build.
Removed in 0.2.0: template generation during the build, the per-page structure-hash cache (cache.json), and stale-template failover. Generation options such as anthropicModel and maxConcurrent still exist but only affect the CLI.
📊 Output
llms.txt
# Business Communication Solutions | Voicenter
> Thousands of organizations manage their business communications through our cloud platform
## Services
- [Call Center Solutions](https://github.com/VoicenterTeam/nuxt-llms-generator/blob/HEAD/llms/call-center-solutions.md): Complete call center management tools
- [Smart PBX for Business](https://github.com/VoicenterTeam/nuxt-llms-generator/blob/HEAD/llms/smart-pbx-business.md): Advanced business telephony
llms-full.txt
All page documents combined in one file.
llms/*.md
One clean markdown document per visible page. Pages with hidePage: "1" are skipped.
🧪 Development
npm install
npm test # run the test suite
npm run lint
npm run prepack # build dist/
🐛 Troubleshooting
The build says a page type has no template. Run npx nuxt-llms-generator templates (with --site for multi-site projects) and commit the template directory.
The CLI says no AI provider is available. Check claude auth status. If it fails, the token is missing or expired; see Token renewal. Alternatively set anthropicApiKey.
"templatesDir" contains {site} but no site is selected. Pass --site to the CLI or set site in nuxt.config.ts or the config file.
A template keeps being regenerated. Run with --dry-run to see the reason. Each regeneration must be caused by a new filled field or block type; if it is not, report it with the printed reason.
📄 License
MIT License - see LICENSE file for details.
Related Packages
Nuxt module that helps you to easily load your Umbraco data directly into your g...
Nuxt module that helps you to easily load your Umbraco data directly into your g...
This is Vue 3.x + Typescript component library made for Voicenter systems.
Version History
| Version | Released | License |
|---|---|---|
| 0.2.1 | MIT | |
| 0.2.0 | MIT | |
| 0.1.18 | MIT | |
| 0.1.17 | MIT | |
| 0.1.16 | MIT | |
| 0.1.15 | MIT | |
| 0.1.14 | MIT | |
| 0.1.13 | MIT | |
| 0.1.12 | MIT | |
| 0.1.11 | MIT | |
| 0.1.10 | MIT | |
| 0.1.9 | MIT | |
| 0.1.8 | MIT | |
| 0.1.7 | MIT | |
| 0.1.6 | MIT | |
| 0.1.5 | MIT | |
| 0.1.4 | MIT | |
| 0.1.3 | MIT | |
| 0.1.2 | MIT | |
| 0.1.1 | MIT | |
| 0.1.0 | MIT |