Download the PHP package hellpat/watson without Composer
On this page you can find all versions of the php package hellpat/watson. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download hellpat/watson
More information about hellpat/watson
Files in hellpat/watson
Package watson
Short Description PR blast-radius analyser for PHP. Standalone dev-only CLI that introspects Symfony / Laravel apps from the outside. Reports which routes / commands / jobs / listeners / tests a git diff reaches.
License MIT
Homepage https://github.com/HellPat/watson
Informations about the package watson
watson
PR blast-radius analyzer for PHP. Standalone dev-only CLI that introspects Symfony / Laravel apps from the outside. Reports which routes, commands, jobs, message handlers, and tests a diff actually reaches.
watson does not shell out to git. You pipe a unified diff in, watson tells you which framework entry points are reached — at method granularity, with comment-only and whitespace-only edits dropped at the AST layer. -W keeps each changed method whole inside the hunk; -U99999 makes the hunk carry the full file so watson can AST-diff the two halves in memory.
Output formats
Same data, four shapes. Pick the one that fits the consumer.
--format=text (default — human terminal)
--format=md (markdown for PR descriptions / LLM prompts)
`
--format=json (machine contract)
--format=tok (token-optimized for LLM pipes)
Tab-separated, no JSON keys, no whitespace padding. Header lines start with #. Per-row layout: kind \t name \t handler_fqn \t relative/path:line \t extra (extra is HTTP-method + path for routes, message FQN for handlers, empty otherwise).
Roughly half the token cost of pretty-printed JSON.
Recipes
Each block below is a description followed by the command. All examples assume composer require --dev hellpat/watson is done.
Pre-merge — review prompts piped to an LLM
For a plain list of affected routes / commands / risk surface, the
--format=tok(ortext/md) output is the answer on its own — no LLM needed. Pipe to an LLM only when you want something the raw output can't give you: subjective judgement (risk, regression severity), cross-referencing with an external source (tests, coverage, observability), or written prose (testing guides).
Post-release — observability MCP correlation
After a deploy, pipe the just-shipped entry points into an LLM that has an observability MCP server wired up — e.g. Better Stack MCP (claude mcp add betterstack --transport http https://mcp.betterstack.com). The LLM gets the surface that changed and live metrics — it can correlate the two without you copy-pasting route names into a dashboard.
Install
No bundle, no service provider, no config/bundles.php entry. watson auto-detects Symfony vs Laravel by walking up from CWD looking for bin/console or artisan.
Requirements: PHP 8.4+. Symfony 6.4 / 7.x / 8.x or Laravel 10 / 11 / 12. git is not a watson dependency — watson only reads the diff you pipe in. If your diff source is git, you'll have it for that reason.
Commands
watson blastradius
Reads a unified diff from stdin and reports which entry points reach the changed methods. watson does not run git; the caller picks the diff source. There is one input shape — looser modes (name-only, explicit file lists) were removed so the engine has a single contract to support.
-W expands every hunk to the whole function it touches; -U99999 pads context to ∞ so the hunk carries the full file. watson reconstructs old + new file content in-memory from the diff, AST-parses both halves, and hashes each Class::method body (docblocks + comments stripped, whitespace normalised). Only methods with a different hash become ChangedSymbols — comment-only and whitespace-only edits never reach the reach engine.
Variants of the same pipe:
- Staged-only:
git diff --cached -W -U99999 | watson blastradius --format=tok - Two-tag delta:
git diff -W -U99999 v1.4.0..v1.5.0 | watson blastradius --format=tok
When run with no pipe (interactive shell), watson exits with a usage hint instead of silently producing zero results.
Each affected entry point comes back with an affected by changed column listing the trigger Class::method symbols. Reach kind is one of:
🎯 direct— the entry point's own handler file holds a changed symbol.🔗 indirect— the handler reaches a changed file through its imports,new, static calls, or type hints. The reverse-BFS is unbounded by default (the call graph saturates on its own); set--max-depth=N(N >= 1) to truncate.
Flags
| flag | default | what it does |
|---|---|---|
--format=text\|md\|json\|tok |
text |
Output shape. md is tuned for PR descriptions / LLM prompts; json is the machine contract; tok is tab-separated for token-cheap LLM piping. |
--scope=routes\|all |
all |
routes skips commands / jobs / message handlers / tests. Faster startup, smaller signal. |
--max-depth=N |
0 (unbounded) |
Hops the reverse-BFS walks from each entry-point handler before stopping. 0 = let the call graph saturate; set N >= 1 to tighten the signal. |
--app-env=ENV |
dev |
Value passed to bin/console / artisan when collecting routes. |
--project=PATH |
walks up from cwd |
Force the project root rather than autodetecting. |
--base=REF / --head=REF |
none | Cosmetic labels shown in the rendered envelope so consumers can correlate output to a diff range. |
watson list-entrypoints
Snapshot every entry point the framework has registered: routes, commands, message handlers, jobs (Laravel), tests. Same flags as blastradius, minus the diff-input one.
watson <cmd> --help
How watson reads your app
| kind | source |
|---|---|
symfony.route |
bin/console debug:router --format=json |
symfony.command |
bin/console debug:container --tag=console.command --format=json (vendor filtered) |
symfony.message_handler |
bin/console debug:container --tag=messenger.message_handler --format=json (vendor filtered, message inferred via reflection on the handler's first param when the tag's handles is null) |
laravel.route |
php artisan route:list --json |
laravel.command |
inline php -r runner that boots Laravel and dumps Artisan::all() (vendor filtered) |
laravel.job |
AST scan of app/Jobs/ for ShouldQueue implementers |
phpunit.test |
AST scan of the project's autoload-dev.psr-4 roots for PHPUnit\Framework\TestCase subclasses |
watson is a CLI binary, not a bundle/provider. AST scans go through nikic/php-parser — watson never require_onces your app's source. (Earlier versions used roave/better-reflection for per-class inheritance walks; that turned out to be a perf trap on large Laravel apps, so discovery is now hash-map BFS over a single-pass AST class index.)
Pipeline
License
MIT. See LICENSE.
All versions of watson with dependencies
ext-json Version *
phpstan/phpdoc-parser Version ^2.0|^1.30
roave/better-reflection Version ^6.0
sebastian/diff Version ^7.0|^6.0|^5.0
symfony/console Version ^6.4|^7.0|^8.0
symfony/process Version ^6.4|^7.0|^8.0
twig/twig Version ^3.10