AI Enrichment
karaoke-enrich is a CLI tool that fills in missing frontmatter fields on your blog posts and docs pages using AI. It adds:
description— a 1–2 sentence summary of the post.tags— an array of topic tags inferred from the content.reading_time— estimated reading time in minutes (calculated locally, no API call needed).related— slugs of other published posts that share tags.
It is not an Astro module. It runs as a pre-commit git hook, outside the build.
How it works
When you git commit, the pre-commit hook runs karaoke-enrich --staged on any staged .md files inside blog/ or docs/. Files that are already enriched (all fields present, content unchanged) are skipped. Only files with publish: true are processed — private notes are never sent to an AI provider.
The hook is already configured in .githooks/pre-commit. If you cloned the repo and set up the hooks (git config core.hooksPath .githooks), enrichment runs automatically on every commit.
API keys
Set your API key in the environment or in .env.local (gitignored):
# OpenAI (default provider)
OPENAI_API_KEY=sk-...
# or Anthropic
ANTHROPIC_API_KEY=sk-ant-...
If no API key is present, enrichment is silently skipped. The commit still goes through.
Before and after
A new post might start like this:
---
title: "Deploying to Cloudflare Pages"
publish: true
---
After karaoke-enrich runs on commit:
---
title: "Deploying to Cloudflare Pages"
publish: true
description: "A step-by-step guide to deploying a karaoke-cms site to Cloudflare Pages using GitHub Actions."
tags:
- deployment
- cloudflare
- ci-cd
reading_time: 4
related:
- custom-domain
- environment-variables
---
Existing fields are never overwritten. If tags is already set, the AI call is skipped for that file.
Run manually
Enrich all published files in blog/ and docs/:
pnpm exec karaoke-enrich
Enrich specific files only:
pnpm exec karaoke-enrich --staged path/to/file.md
Preview changes without writing (dry run):
DRY_RUN=true pnpm exec karaoke-enrich
Providers
The default provider is openai. Switch to Anthropic by setting ENRICH_PROVIDER:
ENRICH_PROVIDER=anthropic pnpm exec karaoke-enrich
You can also pass a custom provider object when using the library API directly:
import { run, defineProvider } from '@karaoke-cms/enrich';
const myProvider = defineProvider({
async enrich(body: string) {
// call your own API
return { tags: ['example'], description: 'My custom description.' };
},
});
await run({ provider: myProvider });
defineProvider is an identity helper — it exists for autocomplete and type safety, not runtime behavior.
Environment variables
| Variable | Default | Description |
|---|---|---|
ENRICH_PROVIDER | openai | Built-in provider: openai or anthropic |
MAX_ENRICHMENTS_PER_RUN | 20 | Max AI API calls per run (guards against runaway costs) |
RELATED_MAX | 3 | Max related post slugs per file |
DRY_RUN | false | Print enriched frontmatter without writing files |
ENRICH_CACHE_PATH | .enrich-cache.json | Path to the local content-hash cache |
Privacy guarantee
loadPublishedFiles only loads files where publish: true. Files without frontmatter or with publish: false are excluded before any AI call is made. Your private vault notes are never read by the enrichment pipeline.