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.
Scanned 6/5/2026
Install via CLI
openskills install event4u-app/agent-config---
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**:
```bash
# 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-complete` rule — timing: run quality tools ONCE at the end, not after each edit
- `php-coding` rule → PHPStan section — inline ignores, PHPDoc rules
- `verify-before-complete` rule — must run quality checks before claiming work is done
- [`testing-anti-patterns`](../testing-anti-patterns/SKILL.md) and
[`process-anti-patterns.md`](../testing-anti-patterns/process-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
```bash
# 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
```bash
# 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
```bash
# 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:
```bash
# 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
1. Run PHPStan — fix all errors
2. Run Rector + ECS with auto-fix — fix style + refactoring
3. 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.dist` instead of `phpstan.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 `disallowedFunctionCalls` banning `var_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.neon` by 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:
1. **Fix the code** — always the first choice. Fix the actual type issue.
2. **Add type hints / PHPDoc** — if the code is correct but PHPStan can't infer the type.
3. **Inline ignore (last resort)** — only for confirmed false positives:
```php
// @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 `$this` at runtime, PHPStan can't resolve `artisan()`,
`get()`, etc. on `PHPUnit\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 `ignoreErrors` for individual files or specific code issues that should be fixed
- Use `@phpstan-ignore-next-line` without 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 use `UnitTestCase` as the base class (configured in `tests/Pest.php`).
- PHPStan cannot fully resolve Pest's runtime bindings — this is handled via `ignoreErrors` patterns in `phpstan.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.json` or `biome.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
```bash
# 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:
```bash
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
```bash
# Type check without emitting files
npx tsc --noEmit
# Via npm script (preferred)
npm run tscheck
```
### Config
- Config file: `tsconfig.json` (may have `tsconfig.app.json`, `tsconfig.node.json` for different targets)
- `strict: true` should be enabled in all projects
- Check `compilerOptions.paths` for import aliases
## Jest / Vitest — Testing
### Commands
```bash
# 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:
1. Check if a `Makefile` / `Taskfile.yml` has targets for linting/testing.
2. Check if `docker-compose.yml` has a Node service.
3. If neither → run on the host directly.
## Output format
1. Tool exit code and error count summary
2. 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 `-T` flag to avoid TTY issues in non-interactive mode.
## Do NOT
- Do NOT run `vendor/bin/phpstan` or `vendor/bin/ecs` directly — 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 `--write` if 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.
No comments yet. Be the first to comment!