@krantho/tools
The SEO command line and the legacy build-time pipeline that writes structured data, FAQ pages and llms.txt.
The build-time SEO tooling. It holds the unified seo command line, which reads and writes the archetype configuration, and the legacy pipeline, which reads CMS content and YAML files and maps them deterministically to Schema.org structured data, FAQ pages and llms.txt.
@krantho/tools is a private package: it runs inside the krantho repository, not as a package you install. The commands on this page run from the repository root; Project status explains how to work from source.
Configuration#
Every script resolves the project's krantho.config.ts through @krantho/config, the same tree the running site resolves, layered as defaults, then krantho.config.ts, then process.env. The tools read the LLM provider, model and key from that tree. The running site's enrichment reads its provider and model from the settings stored in the admin, which the server seeds from the same tree.
pnpm run seo:enrich:llms --root ../site # run against another project--root <dir> defaults to the working directory. The API key is the configured provider's own: KRANTHO_LLM_ANTHROPIC_API_KEY for the default provider (or llm: { anthropic: { apiKey } } in the config file), and the error names the variable the provider reads. The claude-code provider instead needs KRANTHO_LLM_CLAUDE_CODE_OAUTH_TOKEN, the token claude setup-token prints, for development on your own machine, and runs Haiku only; no other provider reads the token (see @krantho/ai). Parallel calls come from tools.concurrency (CLAUDE_CONCURRENCY, or --concurrency <n> on the command line), and the content, generated and database paths from paths and persistence. The root seo* scripts pass --root examples/nextjs-shadcn; a later --root overrides it. They run through tsx and do not load .env, so export the credential in the shell before an enrichment run.
SEO command line#
The seo command works with the runtime archetype system:
pnpm run seo config show [target] # show archetype configuration (all, a collection, or col/slug)
pnpm run seo config set-site <Type> # set the site-level Schema.org type
pnpm run seo config set <col> <Types> # set collection archetype types, comma-separated (Article,FAQPage)
pnpm run seo config reset <target> # remove one configuration override
pnpm run seo config clean # remove every SEO configuration (clean slate)
pnpm run seo preview <col/slug> # preview the effective configuration for a document
pnpm run seo validate --all # check rich result eligibility (or validate <col/slug>)
pnpm run seo --root ../site config show # run against another projectThe SEO command line reads and writes the project's SQLite database (persistence.sqlite.path); it exits with an error on any other persistence provider.
Legacy pipeline#
pnpm run seo:generate # generate structured-data.json, llms.txt and faq-pages.json
pnpm run seo:enrich:llms # preview LLM-written llms.txt link descriptions; --apply writes business.yamlmapBusiness#
Reads business.yaml and CMS content, and generates ProfessionalService (a LocalBusiness subtype) and Person structured data. It parses opening hours, address, services and contact details.
import { loadConfig, mapBusiness, generateBusinessData, setToolPaths } from '@krantho/tools'
// Point the mappers at a project; without this they use the working directory
setToolPaths(await loadConfig('/path/to/project'))
// Get the structured data object
const data = mapBusiness()
// Or write structured-data.json to the project's generated SEO directory
generateBusinessData()mapFAQ#
Reads FAQ YAML files, resolves route-based inheritance, and generates FAQPage structured data per route.
import { mapFAQ, generateFAQData } from '@krantho/tools'
const faqData = mapFAQ() // FAQ entries per route
generateFAQData()
// Writes faq-pages.json to the project's generated SEO directorymapLlmsTxt#
Renders the llms.txt text, with link descriptions for language models. pnpm run seo:generate writes it to the generated SEO directory.
import { mapLlmsTxt } from '@krantho/tools'
const text = mapLlmsTxt()Site configuration#
The legacy mappers also read site-config.json from the project's SEO directory. This file is for @krantho/tools only; the CMS, admin and API never read it.
import { loadSiteConfig } from '@krantho/tools'
const config = loadSiteConfig()
// { domain, siteName, language, serviceIds, appRoutes, ... }YAML utilities#
import { writeYAMLWithHeader, YAML_WRITE_OPTIONS } from '@krantho/tools'
writeYAMLWithHeader('path/to/file.yaml', data, '# Generated file')Next steps#
- Use the runtime archetype system: @krantho/seo
- Configure SEO from the admin: @krantho/admin