govigilant/statamic-gpt-spell-corrector
A Statamic addon that uses OpenAI's GPT models to proofread and spell-check your content, helping editors catch typos, grammar issues, and awkward phrasing before publishing.
Install and register. Pull the addon in with Composer — Statamic auto-discovers the service provider via the package's extra.statamic manifest, so there is normally no manual registration step:
composer require govigilant/statamic-gpt-spell-corrector
Then publish whatever config the addon ships (API key, model, temperature, which fieldtypes it applies to) and confirm the tag name from the package README:
php artisan vendor:publish --tag="statamic-gpt-spell-corrector-config"
php artisan config:clear
Set the OpenAI credential in .env (OPENAI_API_KEY=...) rather than in the published config file, and commit only the config skeleton.
Where to look first. In this order:
vendor/govigilant/statamic-gpt-spell-corrector/README.md — the canonical usage surface.src/ServiceProvider.php — this tells you what kind of addon it is: a Control Panel action/button, a Fieldtype, an Antlers modifier, or an Artisan command. That single file determines your whole integration strategy.config/ and resources/js/ — config keys, and whether the CP component needs a build/publish step.First use case. Don't start with automation. Start with the narrowest editorial win: a single entry, a single field. Open an entry in the Control Panel, run the corrector against the prose field, read the diff, and save only if you accept it. That gets you a real cost-per-entry and quality signal before you invest in wiring it into a workflow or a queue.
Human-in-the-loop before anything else. Spelling/grammar correction via an LLM is a rewrites-everything operation, not a find-and-replace. The only safe default pattern is: generate → show diff → editor approves → save. Never hook this into saving/saved events on day one; a single bad pass can silently rewrite an author's voice across a whole collection, and Statamic's revision history is your only undo.
Two integration shapes, pick deliberately.
Batch backfill, if you need it. Wrap it yourself:
Entry::query()->where('collection', 'articles')->chunkById(50, function ($entries) {
foreach ($entries as $entry) {
CorrectEntryJob::dispatch($entry)->onQueue('content');
}
});
Give the job $tries, a backoff, and Illuminate\Queue\Middleware\RateLimited so you degrade gracefully on OpenAI 429s instead of hammering the API. Support --dry-run so you can eyeball the output for twenty entries before committing to twenty thousand.
Send text, not storage format. Bard fields in Statamic are ProseMirror JSON; Markdown fields carry fences and shortcodes. Sending the raw stored value to the model wastes tokens and invites the model to "fix" your JSON structure. Extract the text nodes, correct those, and write back into the original node tree. Same principle for HTML: strip tags, correct, re-wrap.
Protect non-prose. Statamic content is full of {{ ... }} Antlers, {{% ... %}} tags, shortcode attributes, and partial references. Replace them with sentinel placeholders before sending and restore them after — otherwise the model will confidently "correct" a variable name and break rendering.
Locale and glossary. Pass the entry/site locale through so the model doesn't anglicise localised content, and maintain a small blocklist of brand names, product names, and proper nouns it must not touch. This is the single highest-leverage configuration knob for output quality.
Test with a faked HTTP client. Http::fake() with a recorded response, assert on the job/modifier output against a fixture entry. You do not want your test suite billing you or failing when OpenAI has an incident.
The 1.1.0 changelog is only a compare link. There are no release notes describing behaviour changes — just 1.0.1...1.1.0. Before rolling this across multiple sites, read the actual delta:
composer update govigilant/statamic-gpt-spell-corrector --dry-run -v
# or, in the package checkout:
git log --oneline 1.0.1..1.1.0
git diff 1.0.1..1.1.0 -- config/ src/
It's a minor bump, so treat it as low-risk — but the two things that bite on minor releases of Statamic addons are (a) changed or renamed config keys (your published config silently keeps old keys and the addon ignores them) and (b) updated CP assets/JS that don't get re-published. If the Control Panel button misbehaves after upgrading, re-publish and clear:
php artisan vendor:publish --tag="statamic-gpt-spell-corrector-config" --force
php artisan config:clear && php artisan view:clear
php artisan statamic:stache:clear
Always diff the freshly published config against your committed one rather than blind-overwriting it.
Silent no-ops usually mean the API key isn't reaching the process. Queued jobs inherit the worker's environment, not your web request's. php artisan config:cache with an unset OPENAI_API_KEY freezes null into the cached config. Check the config value directly from php artisan tinker before debugging anything else.
Long entries get truncated or blow the context window. Chunk by paragraph/heading, not by character count, so you never split a sentence across two API calls. Also budget for latency: a 3,000-word entry is many seconds to tens of seconds — that's another reason this belongs in a queue, not a synchronous CP request.
Pin the model. If the addon defaults to a specific model name, set it explicitly in config anyway. Provider-side model deprecations are the most common cause of an addon that "just stopped working" with a 404 from the API and no error in the CP.
Idempotency and rollback. Running the corrector twice should not produce a different result. If it does, your temperature is too high for this use case — set it to 0 and treat the operation as deterministic. Pair every automated run with Statamic revisions so an editor can revert a specific entry without a database restore. For backfills, snapshot the collection (export or a Git commit) first; a bad batch job over an existing collection is otherwise unrecoverable.
Watch the cost line. Log token usage per entry per run, even if it's just to a log channel. The failure mode here is not a single expensive call — it's a scheduled job that quietly reprocesses every entry every hour.
Extension points. If the addon's correction logic is a resolvable class rather than a closure, bind your own implementation in a service provider to add glossary injection, caching of identical inputs, or a different provider. Favour that over forking the package; it keeps upgrades to 1.1.x (and 1.2.x) painless.
How can I help you explore Laravel packages today?