A coding agent is a generalist, and your codebase is not general
This is me
Latest articles
- GDPR cookie consent in Laravel with Wirecookies (opens on another site)
- Localized routes in Laravel with Laralang (opens on another site)
- Collecting user feedback in Laravel with Wirebug (opens on another site)
- The same operation ends up in three places, and none of them agree
- Laravel makes you choose between readable templates and stable translation keys
- Model observers are the wrong home for derived fields
- A coding agent is a generalist, and your codebase is not general
- Translating a Laravel app is two problems, and only one of them is about words
- The tags table is simple until the fourth requirement arrives
Claude Code is a strong pair for writing code. Out of the box it is also a generalist, and a codebase is the opposite of general.
It does not know that in this project a modal follows one specific pattern. It does not know the migrations have piled into three hundred one-column ALTERs, or that a relationship touched inside a @foreach is about to fire two hundred queries against a table with no index on the foreign key.
None of that is a shortcoming of the model. It is missing context, and the way I was supplying it was retyping it. Every session started with the same paragraphs about how this app is put together, and every session that context died when the window closed.
Anything you explain more than twice is a thing you should have written down. So I wrote it down in the form the tool can execute: skills.
Read-only by default, fix as a separate word
The first decision, and the one that took the most thought, is what a command does when you run it with no arguments.
An agent that can edit your code is useful in proportion to how much it can change, and dangerous in exactly the same proportion. The tempting default is to fix: you ran /lc:security-audit, so presumably you want the security problems gone.
I think that is wrong, and the reason is about trust rather than safety. The first time you run an unfamiliar analysis you do not yet know whether it is any good. If it edits immediately, you are reviewing a diff while simultaneously deciding whether the tool was right to produce it, which is two jobs at once and the point where people give up and uninstall.
So the shape is three steps, and the verb is always explicit:
/lc:security-audit # read-only report
/lc:security-audit fix --dry-run # preview the changes
/lc:security-audit fix # apply, with confirmation
You can always look before you leap, and the tool has to earn the fix from you. The cost is one extra word to type on every invocation, forever. Worth it: the failure mode of the other default is unreviewed edits in a codebase, discovered later.
Conventions, not best practices
The second decision is what a skill should actually encode, and it is easy to get backwards.
A skill that says "generate a Livewire component following Laravel best practices" is nearly worthless, because that is already in the model. It knows Laravel conventions better than most of us. Writing it down again is describing water to a fish.
What it cannot know is this project's conventions: that modals use a specific x-modal component, that user-facing strings go through @text() with an English default inline, that business logic belongs in an action class rather than a controller. That is local knowledge, it is arbitrary) someone chose it (and it is exactly what I was pasting into every chat.
Which is why the useful skills are the ones that look opinionated. /lc:volt-component does not generate a generic Volt component; it generates one shaped the way this codebase shapes them. /lc:generate-action produces a Laractions action and registers it on the model. /lc:blade-audit flags hardcoded strings that should be going through Laratext.
The general rule I ended up with: if the model would do it right without the skill, the skill should not exist. Every skill has to earn its place by encoding something the model has no way to know.
The analyses that need a running app
One family sits apart from the rest and is worth calling out, because it marks a limit of the whole approach.
Most skills read source files. That is enough for a lot of questions and structurally not enough for others: whether a foreign key is actually orphaned depends on rows, not on migrations. Whether the live schema still matches database/migrations/ is a question about a database. Static analysis can only guess at both, and a confident guess about production data is worse than no answer.
So /lc:boost-inspect goes through the Laravel Boost MCP server and reasons about the running app) real schema, real counts, recent errors (falling back to Docker and tinker when Boost is not installed.
That skill exists as an admission: reading the code is not the same as knowing the system, and a tool that blurs the two will be confidently wrong about the things that matter most.
The format outlived the tool
A skill is a plain Markdown file following the Agent Skills standard, which was not a decision I agonised over and turned out to be the one with the longest shelf life. The same files work in other tools that implement the standard.
Encoding project knowledge in a vendor's proprietary format would mean re-encoding it when the vendor changes, and the thing being encoded, how this codebase does things, long outlives whichever agent is reading it this year.
The full skill list, install and compatibility are on the LaraClaude page. Plugin on GitHub.