model_tier: medium name: quality-tools description: "Use when PHPStan, Rector, or ECS output appears — "phpstan says mixed", type errors, "fix code style", "run rector" — even when Eloquent/Laravel/model code is also mentioned." domain: quality execution: type: assisted handler: shell allowed_tools: [] recommended_for_user_types: [developer] workspaces: - engineering packs: - engineering-base
quality-tools
When to use
Use this skill whenever running or configuring code quality tools:
- PHP: PHPStan (static analysis), Rector (automated refactoring), ECS (coding standards)
- JS/TS: Biome (linting + formatting), TypeScript compiler (type checking), Jest/Vitest (tests)
Language detection
Detect which tools to run based on what files were changed:
# Check changed file extensions (diff against base branch)
git diff --name-only origin/main..HEAD | grep -E '\.(php)$' # → PHP tools
git diff --name-only origin/main..HEAD | grep -E '\.(js|ts|tsx)$' # → JS/TS tools
If both PHP and JS/TS files changed → run both pipelines.
Related rules and guidelines
verify-before-completerule — timing: run quality tools ONCE at the end, not after each editphp-codingrule → PHPStan section — inline ignores, PHPDoc rulesverify-before-completerule — must run quality checks before claiming work is donetesting-anti-patternsandprocess-anti-patterns.md— test-side rationalizations these tools cannot catch (e.g. "CI is red, patch first, test later").
PHP Quality Tools
Tool Detection
Check composer.json to determine which tools are available and how to run them.
Use the first matching command style:
composer.json contains |
Command style | Example |
|---|---|---|
A project-specific quality wrapper (exposes quality:* commands) |
php artisan quality:* or composer quality:* |
php artisan quality:phpstan |
phpstan/phpstan or larastan/larastan |
vendor/bin/phpstan |
vendor/bin/phpstan analyse |
rector/rector |
vendor/bin/rector |
vendor/bin/rector process |
symplify/easy-coding-standard |
vendor/bin/ecs |
vendor/bin/ecs check --fix |
Priority: If a project ships a quality:* wrapper (Artisan or Composer script), always prefer it —
wrappers typically add git-aware execution, caching, automatic baseline regeneration, and memory
management on top of the native tools.
If none of the above is installed → skip quality checks, inform the user.
All commands run inside the Docker container if Docker is used (docker compose exec or make console).
Commands
PHPStan — Static Analysis
# Native:
vendor/bin/phpstan analyse # Analyse all configured paths
vendor/bin/phpstan analyse --memory-limit=512M # With memory limit
vendor/bin/phpstan analyse --error-format=github # CI-friendly format
# With a project-specific quality wrapper:
php artisan quality:phpstan # Laravel
composer quality:phpstan # Composer
Native flags:
| Flag | Description |
|---|---|
--memory-limit=SIZE |
Set memory limit (e.g. 512M, 1G) |
--debug |
Activate debug mode |
--error-format=FORMAT |
Output format: table (default), github, gitlab |
--pro |
Toggle PHPStan Pro |
Wrapper-only flags (typical of a quality:* wrapper — verify in the project):
| Flag | Description |
|---|---|
--baseline |
Generate/update phpstan baseline file |
--ignore-git |
Skip Git check, analyse all files |
--xdebug |
Activate Xdebug mode |
ECS — Easy Coding Standard
# Native:
vendor/bin/ecs check # Dry-run
vendor/bin/ecs check --fix # Auto-fix
# With a project-specific quality wrapper:
php artisan quality:ecs --fix # Laravel
composer quality:ecs -- --fix # Composer
Native flags:
| Flag | Description |
|---|---|
--fix |
Fix errors automatically |
--clear-cache |
Clear the ECS cache |
Wrapper-only flags (typical of a quality:* wrapper — verify in the project):
| Flag | Description |
|---|---|
--ignore-git |
Skip Git check, check all files |
--paths-to-scan[=PATHS] |
Custom paths, e.g. --paths-to-scan='["./core"]' |
--source-branch[=BRANCH] |
Source branch (default: HEAD) |
--target-branch[=BRANCH] |
Target branch to compare against |
Rector — Automated Refactoring
# Native:
vendor/bin/rector process # Auto-fix
vendor/bin/rector process --dry-run # Preview changes
# With a project-specific quality wrapper:
php artisan quality:rector --fix # Laravel
composer quality:rector -- --fix # Composer
Native flags:
| Flag | Description |
|---|---|
--dry-run |
Preview changes without applying |
--clear-cache |
Clear the Rector cache |
Wrapper-only flags (typical of a quality:* wrapper — verify in the project):
| Flag | Description |
|---|---|
--ignore-git |
Skip Git check, check all files |
--paths-to-scan[=PATHS] |
Custom paths |
--source-branch[=BRANCH] |
Source branch (default: HEAD) |
--target-branch[=BRANCH] |
Target branch to compare against |
Combined Commands
There is no native single command for running all three tools. Run them in sequence:
# Full pipeline (native):
vendor/bin/rector process && vendor/bin/ecs check --fix && vendor/bin/phpstan analyse
# With a project-specific quality wrapper (if it ships these combined commands):
php artisan quality:refactor --fix # Rector + ECS
php artisan quality:finalize # Rector + ECS + PHPStan (full pipeline)
Procedure: Run quality checks
- Run PHPStan — fix all errors
- Run Rector + ECS with auto-fix — fix style + refactoring
- Run PHPStan again — verify step 2 didn't introduce new issues
If step 3 finds errors → fix and repeat from step 2.
Detect commands from project (see Tool Detection above).
Configuration
Config files are typically in the project root. Do NOT modify without explicit user permission.
Detect config location:
- Check project root first:
phpstan.neon,ecs.php,rector.php - Some projects use
phpstan.neon.distinstead ofphpstan.neon
Standard config files
| File | Tool | Purpose |
|---|---|---|
phpstan.neon |
PHPStan | Level, paths, extensions, ignoreErrors, disallowed calls |
phpstan-baseline.neon |
PHPStan | Baseline for existing errors (auto-managed, do NOT edit) |
ecs.php |
ECS | Code style: rule sets, configured rules, skip list |
rector.php |
Rector | Refactoring: rule sets, PHP version sets, skip list |
Understanding the project's config
Read the actual config files to understand what rules are active:
- phpstan.neon: Check
level,paths,ignoreErrors,includes(baselines, extensions) - ecs.php: Check which rule sets are active (PSR-12, PHP-CS-Fixer sets), skip list
- rector.php: Check PHP version sets, active rule sets, skip list
Common patterns across projects:
- PHPStan at high levels (8-9) with
disallowedFunctionCallsbanningvar_dump(),dd() - ECS with PSR-12 base, trailing commas, Yoda style
- Rector with PHP version migration sets and naming conventions
Baseline policy
- NEVER edit
phpstan-baseline.neonby hand. - NEVER add errors, update counts, or regenerate manually.
- If the project ships a quality wrapper (exposing
quality:*commands), it may regenerate the baseline automatically.
PHPStan error handling
Priority order for dealing with PHPStan errors:
- Fix the code — always the first choice. Fix the actual type issue.
- Add type hints / PHPDoc — if the code is correct but PHPStan can't infer the type.
- Inline ignore (last resort) — only for confirmed false positives:
// @phpstan-ignore-next-line — false positive: reason here
When phpstan.neon changes ARE allowed
Adding ignoreErrors entries to phpstan.neon is allowed when:
- The error is a structural limitation of the toolchain (e.g., Pest tests bind
$thisat runtime, PHPStan can't resolveartisan(),get(), etc. onPHPUnit\Framework\TestCase) - The pattern applies broadly to a category of files (e.g., all test files), not just one specific line
- The fix would require abandoning the project's conventions (e.g., rewriting Pest tests as PHPUnit classes)
If unsure whether a phpstan.neon change is appropriate → ask the user before making the change.
NEVER do these
- Add entries to baseline files (
phpstan-baseline.neon) - Add
ignoreErrorsfor individual files or specific code issues that should be fixed - Use
@phpstan-ignore-next-linewithout a reason comment
See also: php-coding rule → PHPStan section.
Testing framework
- Always write tests in Pest, not PHPUnit class syntax — unless the user explicitly asks for PHPUnit.
- Pest tests in
tests/Unit/automatically useUnitTestCaseas the base class (configured intests/Pest.php). - PHPStan cannot fully resolve Pest's runtime bindings — this is handled via
ignoreErrorspatterns inphpstan.neon.
Git-aware execution
By default, all tools only check files changed since the last commit.
| Flag | Effect |
|---|---|
| (default) | Only changed files (fast, for iterative work) |
--ignore-git |
All files (cache still applies) |
--clear-cache |
Changed files, but ignore cache |
--ignore-git --clear-cache |
Complete fresh run of all files |
JS/TS Quality Tools
Detection
Check package.json for available tools:
| Indicator | Tool |
|---|---|
@biomejs/biome in devDependencies |
Biome — linting + formatting |
typescript in devDependencies |
TypeScript — type checking |
jest or vitest in devDependencies |
Test runner |
eslint in devDependencies |
ESLint — legacy linting (check if Biome replaces it) |
prettier in devDependencies |
Prettier — legacy formatting (check if Biome replaces it) |
Biome — Linting + Formatting
Biome replaces ESLint + Prettier in one tool.
Config
- Config file:
biome.jsonorbiome.jsonc - Includes formatter settings (indent style, line width, trailing commas)
- Includes linter rules (recommended + custom overrides)
- Includes import sorting (via
assist.actions.source.organizeImports)
Commands
# Check (dry-run) — shows errors without fixing
npx biome check .
# Fix — auto-fix all fixable issues (formatting + linting + imports)
npx biome check --write .
# Format only
npx biome format --write .
# Lint only
npx biome lint .
Via npm scripts (preferred)
Check package.json scripts — projects typically define:
npm run biome # Check (dry-run)
npm run biome:fix # Auto-fix
Always prefer npm scripts over raw npx commands when they exist.
TypeScript — Type Checking
Commands
# Type check without emitting files
npx tsc --noEmit
# Via npm script (preferred)
npm run tscheck
Config
- Config file:
tsconfig.json(may havetsconfig.app.json,tsconfig.node.jsonfor different targets) strict: trueshould be enabled in all projects- Check
compilerOptions.pathsfor import aliases
Jest / Vitest — Testing
Commands
# Run all tests
npm test
# Run specific test file
npx jest path/to/test.spec.ts
# Run with coverage
npx jest --coverage
# Watch mode
npx jest --watch
JS/TS Quality Workflow
After JS/TS code changes, run this sequence:
1. npx biome check --write . → Auto-fix formatting + linting
2. npx tsc --noEmit → Verify type safety
3. npm test → Run test suite
Or via npm scripts:
1. npm run biome:fix → Auto-fix
2. npm run tscheck → Type check
3. npm test → Tests
If step 2 finds type errors → fix them in code, then re-run step 1 (Biome may reformat).
Execution environment
PHP tools
All PHP commands run inside the Docker container (make console or docker compose exec).
JS/TS tools
JS/TS commands run on the host or in a Node container, depending on the project setup:
- Check if a
Makefile/Taskfile.ymlhas targets for linting/testing. - Check if
docker-compose.ymlhas a Node service. - If neither → run on the host directly.
Output format
- Tool exit code and error count summary
- Fixed issues or remaining errors to address
Auto-trigger keywords
- quality check
- quality fix
- PHPStan
- Rector
- ECS
- code style
- lint
- Biome
- type check
- tscheck
Gotcha
- Always check exit code first — if 0, don't read output (saves tokens).
- Rector + ECS can introduce PHPStan errors — always re-run PHPStan after fixing.
- A project-specific
quality:*wrapper may expose different flags than the native tools — check the project's wrapper before assuming flags. - Docker commands need
-Tflag to avoid TTY issues in non-interactive mode.
Do NOT
- Do NOT run
vendor/bin/phpstanorvendor/bin/ecsdirectly — use the wrapper. - Do NOT manually edit
phpstan-baseline.neon— it's auto-managed. - Do NOT skip type checking (
tsc --noEmit) for TypeScript projects. - Do NOT run Biome without
--writeif the intent is to fix (otherwise it's dry-run only). - Do NOT mix ESLint + Biome in the same project — check which one is active.