AI Agents: Claude Code and Codex
An AI coding agent writes PHP code fast and in its own way. After each of its edits, DressCode fixes the file to match your project's coding standard and, like a good tailor, tells the agent right away what it altered: a diff of the rewrite, the imports it removed, and the findings that remain, with advice on what to do about them.
Installation
In Claude Code, two commands are enough:
/plugin marketplace add nette/agent-plugins
/plugin install php-fixer@nette
From then on, after every edit of a PHP file, the php-fixer plugin runs DressCode with the
project's configuration (the nearest dresscode.neon), or with the Nette standard where there is none. Codex uses the
same plugin, installed with codex plugin add php-fixer@nette; you approve its hooks once in /hooks. When
DressCode is not installed in the project or globally, the install-php-fixer skill installs it.
The hook covers files written by the Edit and Write tools in Claude Code and by
apply_patch in Codex. It does not see a file the agent writes with a shell command (Bash); such a file
is fixed only by the next edit or by dresscode fix.
What the Agent Gets
When nobody tells the agent what changed in the file it is editing, a well-known loop starts: the agent writes a
use, the hook deletes it as unused, the agent adds the code that needed the import and wonders where the error came
from. Or it edits another place based on the text it remembers, and the edit fails because the file looks different now. The hook
therefore uses the agent output format, which tells the model in plain sentences what happened:
DressCode changed `src/Cart.php`; the changes below are already in the file:
- removed the import of `Acme\Shop\Payment\Gateway`: nothing used it; if you meant to use it, add the import in the same edit as the use.
```diff
@@ -12,7 +10,7 @@
public function add(string $name, int $price): void
{
- $this->items[] = ["name" => $name, "price" => $price];
+ $this->items[] = ['name' => $name, 'price' => $price];
}
```
Left in `src/Cart.php`, at the lines of the file as it is now:
- line 19: the `@` operator must not be used (`mistakes.errorSuppression`). Fix it; where the current code is intended and you know why, keep it with a comment on its line, `// @ <why>`, giving the real reason for `<why>`; otherwise tell the user.
- For a clean file, the hook prints nothing, so it does not flood the context.
- A rewrite comes as a diff of what is already in the file, so the next edit targets the right text. When the rewrite is too long to fit in the message, the agent gets no diff, only an instruction to read the file again.
- A removed import is named, with the advice to add it in the same edit as its use.
- Every violation says what to do with it. A risky fix says why it was not made and that the change must keep the
intended behavior. Where the project allows exceptions approved by a comment (the key
suppressionComments), the agent gets an example of that comment.dresscode:ignoreis never offered to the agent. - The format leaves out warnings about the configuration, because the agent can do nothing with them.
The Style of the File
The hook also passes DressCode the text of the file before the edit (--before). Any decision the file broke
already before the edit is the style of the file, not the agent's mistake. DressCode leaves it as it is and tells the agent to
follow the style of the file there:
Before the edit `src/Legacy.php` did not follow `indentation.unit`, `literals.quotes`, so DressCode leaves them as the file writes them; write the code there in the style of the rest of the file.
So when the agent edits one method of a file that does not meet the standard yet, the rest of the file is not reformatted.
A small commit gets nothing the agent did not write, and a file indented with spaces stays indented with spaces. On the other
hand, whatever the edit broke is fixed, wherever it is: an import whose last use the edit removed, or lines the edit wrapped in a
new block. A new file belongs entirely to the agent, so it is fixed completely. The rest of the project is a job for
dresscode fix, which you run deliberately and commit separately.
Without a Hook
An agent or a tool that can run a command after writing a file can run the same command itself:
dresscode fix src/Cart.php -f agent --skip-excluded --before /tmp/Cart.before.php --lines 12-14
Thanks to --skip-excluded, the command does not touch a file that the configuration excludes in
excludePaths, for example a test fixture the agent wrote to. Without --before, the whole file is fixed;
--lines only narrows the output.
Upgrading with an Agent
After an upgrade of libraries, you are left with a list of places that a human
must rewrite, and every message in it says what to write instead of the old API. A list like that is a perfect job for an agent.
With the agent format, the agent gets a diff of what DressCode rewrote and, for every remaining finding, what to do
with it:
dresscode upgrade today -f agent
Unlike the terminal output, the agent format does not suggest another run, so run the command again until it
rewrites nothing. The agent gets the estimate and
the plan of an upgrade as data, for example from dresscode upgrade plan php 8.4 --format json; the same goes for
running the plan, for upgrade tasks and for upgrade status. These commands have no agent
format. The document carries the root of the project in the key root and, for a saved plan, the key
planIdentity: a fingerprint of the plan file that tells the agent which plan the document is about. In the
json format, DressCode asks no questions, not even in a terminal. So turn on the types with --types, and
allow upgrade run to raise the versions in composer.json with --yes; without it, the run
does not even call Composer and ends with exit code 4. init and migrate give the agent data too:
dresscode init --format json measures the configuration, takes the recommended answers, and returns the questions it
would have asked under instructions.
Fewer Risky Fixes
The hook does not make risky fixes; it only reports them. To keep as few of them as possible, the key nameResolution: certain belongs
in dresscode.neon too: with it, the fixes that depend on a function being global are no longer risky, and the hook
makes them itself. Types do the same for the fixes that depend on
the type of a value.