承接 hellpat/watson 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

hellpat/watson

最新稳定版本:v0.4.3

Composer 安装命令:

composer require hellpat/watson

包简介

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.

README 文档

README

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.

ci packagist license

composer require --dev hellpat/watson
git diff -W -U99999 origin/main...HEAD | vendor/bin/watson blastradius --format=md

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)

=====================================================================
watson php symfony (root: /abs/path)
=====================================================================

[list-entrypoints]

  3 entry point(s):
    - symfony.route            home              App\Controller\HelloController::home
    - symfony.command          app:ping          App\Command\PingCommand::execute
    - symfony.message_handler  App\Message\Ping  App\MessageHandler\PingHandler::__invoke

--format=md (markdown for PR descriptions / LLM prompts)

# watson — php symfony

_tool watson v0.3.0_

Root: `/abs/path`

## list-entrypoints
_v0.3.0_

**3 entry points**

| kind | name | handler |
|---|---|---|
| `symfony.route` | `home` | `App\Controller\HelloController::home` (`src/Controller/HelloController.php:12`) |
| `symfony.command` | `app:ping` | `App\Command\PingCommand::execute` (`src/Command/PingCommand.php:15`) |
| `symfony.message_handler` | `App\Message\PingMessage` | `App\MessageHandler\PingHandler::__invoke` (`src/MessageHandler/PingHandler.php:13`) |

--format=json (machine contract)

{
  "tool": "watson",
  "version": "0.3.0",
  "language": "php",
  "framework": "symfony",
  "context": {"root": "/abs/path"},
  "analyses": [
    {
      "name": "list-entrypoints",
      "version": "0.3.0",
      "ok": true,
      "result": {
        "entry_points": [
          {
            "kind": "symfony.route",
            "name": "home",
            "handler_fqn": "App\\Controller\\HelloController::home",
            "handler_path": "/abs/path/src/Controller/HelloController.php",
            "handler_line": 12,
            "source": "runtime",
            "extra": {"path": "/", "methods": ["GET"]}
          }
        ]
      }
    }
  ]
}

--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).

# watson 0.3.0 list-entrypoints php/symfony root=/abs/path
# entrypoints=3
# kinds: sc=symfony.command smh=symfony.message_handler sr=symfony.route
# fields: kind\tname\thandler\tpath:line\textra
sr	home	App\Controller\HelloController::home	src/Controller/HelloController.php:12	GET /
sc	app:ping	App\Command\PingCommand::execute	src/Command/PingCommand.php:15	
smh	App\Message\PingMessage	App\MessageHandler\PingHandler::__invoke	src/MessageHandler/PingHandler.php:13

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

# 1. Auto-review focused only on what changed
#    LLM is told the affected entry points; flags risky areas.
git diff -W -U99999 origin/main...HEAD | vendor/bin/watson blastradius --format=md | llm \
  --system "Review this PR. Focus only on the affected entry points listed below.
Flag anything risky around auth, money handling, or user-visible behaviour."


# 2. Generate a manual testing guide
#    Turns the blast radius into a concrete click-through checklist for QA.
git diff -W -U99999 origin/main...HEAD | vendor/bin/watson blastradius --format=md | llm \
  --system "You are a senior dev. Given these affected entry points, write a
concise manual testing guide: list the scenarios a reviewer must click through,
the edge cases most likely to break, and any data shape that needs verifying."


# 3. Coverage gap check — is the change covered by e2e / feature tests?
#    `--scope=all` includes phpunit.test entries so the LLM can cross-reference.
git diff -W -U99999 origin/main...HEAD | vendor/bin/watson blastradius --scope=all --format=json | llm \
  --system "The JSON contains affected entry points (routes / commands / jobs /
message handlers) AND every phpunit.test in the repo. Cross-reference: which affected
entry points have at least one test that exercises them, and which don't?
Output a markdown table; flag gaps as 'NEEDS COVERAGE'."

For a plain list of affected routes / commands / risk surface, the --format=md (or text / tok) 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.

# 1. Latency regression on routes that shipped in the last release
#    Diffs two release tags so you only ask about routes that actually changed.
git diff -W -U99999 v1.4.0..v1.5.0 | vendor/bin/watson blastradius --scope=routes --format=md \
  --base=v1.4.0 --head=v1.5.0 | llm \
  --system "These routes shipped in v1.5.0. Use Better Stack MCP:
for each route, query p50 / p95 latency since the deploy timestamp
and compare to the previous 24h baseline. Flag any route whose p95
grew >20% or whose error rate doubled."


# 2. Error / exception regression after deploy
#    Wider scope so jobs and message handlers are also checked for new exceptions.
git diff -W -U99999 v1.4.0..v1.5.0 | vendor/bin/watson blastradius --format=md \
  --base=v1.4.0 --head=v1.5.0 | llm \
  --system "These entry points (routes / commands / jobs / message handlers)
shipped in v1.5.0. Use Better Stack MCP error tracking to:
- list new exception classes seen on any affected handler since deploy,
- count occurrences vs the prior 24h,
- group by handler FQN and rank by impact."


# 3. Open incidents touching the changed surface
#    Uses list-entrypoints (the full registry) since incidents may not align
#    with a specific release window.
vendor/bin/watson list-entrypoints --scope=routes --format=md | llm \
  --system "Use Better Stack MCP to list all currently-open incidents.
For each incident, identify which (if any) of the entry points below
is involved. Output a markdown table mapping incident → affected
entry point with a one-line summary."

Install

composer require --dev hellpat/watson

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.

git diff -W -U99999 origin/main...HEAD | watson blastradius

-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
  • Two-tag delta: git diff -W -U99999 v1.4.0..v1.5.0 | watson blastradius

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. Depth is bounded by --max-depth=N (default 3).

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 3 Hops the indirect-reach BFS walks from each entry-point handler before stopping. Lower = tighter signal, higher = more recall.
--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

$ watson blastradius --help

Description:
  Report which routes, commands, jobs, and listeners are reached by the
  unified diff piped on stdin.

Usage:
  blastradius [options]

Options:
      --base=BASE          Cosmetic label shown as the diff base in rendered output.
      --head=HEAD          Cosmetic label shown as the diff head in rendered output.
      --project=PROJECT    Project root (defaults to walking up from CWD).
      --format=FORMAT      text | md | json | tok [default: "text"]
      --scope=SCOPE        routes | all [default: "all"]
      --app-env=APP-ENV    APP_ENV passed to bin/console / artisan [default: "dev"]
      --max-depth=MAX-DEPTH  BFS hops the indirect-reach pass walks [default: 3]
$ watson list-entrypoints --help

Description:
  Snapshot every route, command, job, message handler, and test the framework
  has registered.

Usage:
  list-entrypoints [options]

Options:
      --project=PROJECT  Project root (defaults to walking up from CWD)
      --format=FORMAT    text (human terminal) | md (PRs/LLMs) | json (machine)
                         | tok (token-optimized for LLM pipes) [default: "text"]
      --scope=SCOPE      routes (cheapest) | all (+ commands / jobs / message
                         handlers / tests) [default: "all"]
      --app-env=APP-ENV  APP_ENV passed to bin/console / artisan [default: "dev"]

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 tests/ for PHPUnit\Framework\TestCase subclasses

watson is a CLI binary, not a bundle/provider. AST scans go through roave/better-reflection — watson never require_onces your app's source.

Pipeline

flowchart TD
  GD["git diff -W -U99999"] --> CFR["ChangedFilesReader<br/>(reads stdin)"]
  CFR --> ADM["AstDiffMapper<br/>old AST vs new AST per method<br/>(comments / whitespace ignored)"]
  ADM --> CS["changed symbols<br/>Class::method (or Class::*)"]

  APP["target app"] --> ASK["ask framework<br/>(debug:router, debug:container,<br/>route:list, Laravel boot)"]
  ASK --> EP["entry points<br/>Class::method"]
  EP --> AST["AST → handler file:line<br/>(Composer ClassLoader + Better Reflection)"]

  AST --> RX(("reach BFS<br/>(file-graph, method-aware in Phase B)"))
  CS --> RX
  RX --> AEP["affected entry points<br/>+ trigger Class::method"]
  AEP --> R["render<br/>text / md / json / tok<br/>('affected by changed' column,<br/>🔗 indirect badge)"]
Loading

License

MIT. See LICENSE.

统计信息

  • 总下载量: 46
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 3
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-04

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固