Tools

Repository Author Commit message Committed SHA  
 
claude-code by dg php-fixer: added the dresscode skill on running and configuring DressCode

The skill covers DressCode run on purpose, beyond the edited file the hook
fixes: the commands and exit codes, reading the report, risky fixes and
what decides them, suppressions, the baseline, CI and Git hooks, and what
needs the user's consent. It leans on the tool's own explain, config and
catalogue instead of bundling the manual; the configuration and the
upgrades to newer PHP and library versions are in its references.

php-auto-fixer points to it.

command-line by dg Prompt: the default choice stands out in white where the console has colors

A capital marked the default, which a digit does not have, so in [1/2] the
default could not be told.

b65ee535-a726-4a86-868b-c52fc150b75b

eslint-plugin by dg released version 0.4.0
eslint-plugin by dg fixed and expanded readme

- TypeScript install no longer lists typescript-eslint (it is a direct
dependency); only the typescript compiler is needed.
- customize() docs now separate the base entry (browser/jsx) from the
/typescript entry (which adds typescript).
- the Latte example registers the @nette plugin so processor
'@nette/latte' resolves.
- documented the previously undocumented processor configuration
(replacementFunction / removeLatteErrors).

eslint-plugin by dg fixed suggestions in Latte templates pointing to wrong positions

The processor remapped a message's position and its fix, but left the
ranges of its suggestions in the coordinates of the processed code, so
past the first tag an editor applied them at the wrong place. They are
now mapped like the fix, and a suggestion whose fix reaches into a
Latte tag is dropped.

c68af278-2ee3-4de8-b8a9-b7100137c8ff

eslint-plugin by dg fixed autofix corrupting Latte tags

A fix whose range reached into a Latte tag was mapped onto the original
template, but its text had been computed from the placeholder, so it
rewrote the Latte code: no-extra-parens turned `({$y})` into `[1]` and
no-multiple-empty-lines deleted `{/if}`. Such a fix is now dropped and
only the problem is reported. That covers a fix overlapping a
placeholder and one touching the position of a removed tag, where it is
ambiguous on which side of the tag it belongs.

Messages before the first tag were passed through unmapped, so a fix
starting there and spanning the tag escaped any mapping; they now take
the same path, which leaves their positions as they are.

c68af278-2ee3-4de8-b8a9-b7100137c8ff

eslint-plugin by dg corrected the Latte processor docs

AGENTS.md and internals described per-file contexts in a Map and a
fix-range end mapped to the end of the tag, neither of which the code
does or should do. ESLint calls postprocess right after the preprocess
of the same file, so files never interleave, and mapping a fix-range
end to the end of the tag would make an insertion right before a tag
(space-infix-ops turning `x={$y}` into `x = {$y}`) delete it.

AGENTS.md also named configs.latte and configureLatte(), which do not
exist; the processor is configured by its configure().

eslint-plugin by dg added meta.name to the Latte processor

ESLint needs it to serialize a config that passes the processor object
itself rather than the '@nette/latte' string, as --cache and
--print-config do.

eslint-plugin by dg added support for Latte comments {* *} in the processor

Latte comments were left untouched and broke the extracted JavaScript.
They are now stripped (both {* *} and the double-brace {{* *}} form),
including comments that contain tag-like content: both tag regexes
match a whole comment first, so a tag inside it is never picked up.

eslint-plugin by dg added docs URLs to rules

Both rules point meta.docs.url at their readme sections.

eslint-plugin by dg renamed rule file to match the rule name

no-this-in-arrow-except-nested.js -> no-this-in-arrow-except.js, so the
file name matches the `@nette/no-this-in-arrow-except` rule id. The
package `exports` map only exposes the two entry points, so deep
imports were never part of the public API.

eslint-plugin by dg fixed no-this-in-arrow-except false positives

`this` inside an arrow now correctly resolves through class field
initializers, static blocks and object/member methods:

- `class Foo { handler = () => this.x }` (bound-method pattern),
- `class Foo { static { arr.map(() => this.x) } }`,
- `const o = { foo() { arr.map(() => this.x) } }`,
- `() => class { x = this.y }`, where `this` is not the arrow's at all.

Arrows used as object-literal property values still report, because
there `this` is the outer scope, not the object.

eslint-plugin by dg fixed prefer-line-comments corrupting code

The autofix blindly rewrote a block comment to //, which:
- swallowed code following the comment on the same line
(`a; /* c */ b;` became `a; // c b;`),
- broke ESLint directives (`/* eslint-disable */`) and tooling
annotations (webpack/vite magic comments, `#__PURE__`, `@license`,
preserved-license `/*! */`).

Such comments are now left untouched.

eslint-plugin by dg dropped support for Node.js 20, requires Node.js 22+ (BC break)

Node.js 20 reached end of life in April 2026. The CI matrix now runs
22 and 24 and package.json declares engines >= 22.13.0, the floor
ESLint 10 itself has on the 22 line. Also bumped actions/setup-node
from v4 to v7.

eslint-plugin by dg removed package-lock.json from version control

npm never publishes the lock file, so it only ever affected local
development and CI, not consumers of the package. Like composer.lock in
a PHP library, it now stays on disk but out of git, so CI resolves
dependencies the same way a user of the package does and breaking
releases surface early.

Without the lock file CI installs with `npm install` instead of
`npm ci`, and setup-node no longer caches npm, as both need it.

phpstan-rules by dg nette/database row type narrowing (table/related/ref/insert)

Narrows return types based on a configurable table-to-entity-class
mapping (nette.database.mapping.tables), which is not yet part of the
nette/database distribution. Kept as the topmost commit so the whole
feature can be excluded or rebased independently.

Includes:
- TableRowTypeResolver shared resolver
- Explorer::table() -> Selection<EntityRow>
- ActiveRow::related() -> GroupedSelection<EntityRow>
- ActiveRow::ref() -> ?EntityRow
- Selection::insert() -> mapped EntityRow

phpstan-rules by dg old Nette class names kept as aliases are reflected as the classes they stand for
phpstan-rules by dg TypeAssert: opcache is switched off before analysis

A type test could see *ERROR* in place of a perfectly declared type, and
whether it did depended on which other tests happened to run beside it.
In nette/http, TypesTest failed under php-cgi on Windows only when run in
parallel with a test that touched FileUpload: getImageSize() declared as
?array{int, int} came out as *ERROR*. Alone, with -j 1, under the CLI or
with opcache disabled it passed.

The cause lies in how PHPStan finds the source of a class. For a class
outside the analysed paths, its AutoloadSourceLocator calls the autoloader
under FileReadTrapStreamWrapper, a stream wrapper that intercepts the
include, records the path and returns empty content, so the file is never
executed. Opcache defeats the trap: a script it already has in the cache
is served without the file being opened, the wrapper never sees the read
and the file really runs. Its class gets defined natively in the middle of
the analysis, and any trait or parent it needs is in turn loaded under the
trap, which fails (here "Trait Nette\SmartObject not found"), so the
reflection breaks and the type becomes *ERROR*.

Why it looked random: the CLI usually has opcache off (opcache.enable_cli),
while php-cgi has it on by the php.ini. On Windows the opcache memory is
shared by the php-cgi processes that run at the same time, so a script is
already cached in the test that analyses it exactly when a parallel test
has loaded it first. Calling opcache_compile_file() on the file before
assertTypes() reproduces it deterministically in a single process.

Opcache can be switched off at runtime (only off, never on), and once off
the scripts are compiled from the files again, so the trap works. It is
done in createContainer(), the common entry of every assertion, before
anything is analysed.

phpstan-rules by dg CI: DressCode checks with the dependencies installed

Without vendor/ it knows nothing of the classes of the libraries, so a call of a deprecated method passes unnoticed.

command-line by dg Runner: added, which runs the program in the process: help, version, usage errors and the exit code

It adds --help with -h and, given a version, --version with -V, placed with the
options of the program, a flag of that name the program has being answered as
it is. A usage error is printed with the help of its command and returns 2 by
default. On request it prints the help for an empty line (helpWhenEmpty, before
the required arguments are demanded), adds --no-color, read before parsing so
that a mistake of the line is plain too (colorFlag), and reports an exception
of the handler by its message (errorExitCode). A version may be a closure
writing it for the console it goes to.

command-line by dg CI: DressCode checks with the dependencies installed

Without vendor/ it knows nothing of the classes of the libraries, so a call of a deprecated method passes unnoticed.

mcp-inspector by dg fixed server ending by itself after 13.5 hours without input on Windows

PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking
mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then
reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an
idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as
stream_select() on Windows reports an idle pipe as readable before that.

The run loop then also runs on an idle connection, and mcp/sdk 0.9 only reads the session there, so the
first read past its TTL of an hour deletes it and every later request gets "Session not found or has
expired" without an id. A stdio session lasts as long as the process, so the server gives it no TTL.

See php/php-src#25204, modelcontextprotocol/php-sdk#671 and modelcontextprotocol/php-sdk#672.

mcp-inspector by dg uses the Runner of nette/command-line
mcp-inspector by dg added AGENTS.md
mcp-inspector by dg added Tracy toolkit: log files by level and exception report summaries
mcp-inspector by dg added DI container toolkit: services, autowiring, tags, aliases and masked parameters over Container::getServiceDescriptors()
mcp-inspector by dg added database toolkit: schema introspection and db_query with read-only policy enforced by validation and a read-only transaction
mcp-inspector by dg added router toolkit: routes, URL matching and link generation
mcp-inspector by dg added presenters toolkit: the mapping, presenters found on disk with their actions, and presenter details
mcp-inspector by dg added application toolkit: app_get_info with versions, packages, directories and database driver
mcp-inspector by dg added installer command

The command line is built on nette/command-line 2.0: init and call are real
commands, call has its tool and JSON as arguments, an extra word or an unknown
command is refused by the parser, --php belongs to init, and the help of each
command lists its own options apart from the global ones.

What a person reads goes through a Console of the library, one over stdout and
one over stderr, so a redirected stdout does not decide the colors of stderr and
an error is red where the terminal takes it. Nothing echoes, which is what keeps
stdout clean while the MCP protocol runs over it, and the help draws into the
same console instead of building one of its own.

mcp-inspector by dg added inspector core: own DI container, live-reloading AppContainer, Server

Server::call() passes stdClass when a tool has no arguments, as SchemaValidator of mcp/sdk validates an
empty array as a JSON array, which an input schema of type object rejects.

Last synchronization: 2026-11-10 08:03:12