Performance Doctor
Automated performance audit → report → (optional) diagnosis & fix loop for frontend projects. Generates a self-contained HTML report with scoring, iteration tracking, and methodology reference.
Prerequisites
- Node.js: Version 18+
- pnpm: Package manager
- Project: A Vite-based React project with
react-router-domroutes
Workflow Overview
Phase 0: SETUP → Install deps, discover routes, start dev server
Phase 1: EVALUATE → Run Lighthouse on all pages, analyze, generate baseline report
── CHECKPOINT: Present report to user, ASK whether to proceed with optimization ──
Phase 2: ITERATE → (Only after user confirms) Diagnose + fix + re-audit (up to 3 rounds)
Phase 3: UPDATE → Regenerate report with iteration data
IMPORTANT: After Phase 1 you MUST stop and wait for user confirmation before entering Phase 2. Do NOT automatically start fixing code.
Phase 0: Setup
Step 0.1: Initialize Scripts
# Copy scripts into the target project
cp -r <skill-dir>/performance-doctor/scripts ./perf-doctor-scripts
cd perf-doctor-scripts && pnpm install && cd ..
Step 0.2: Discover Routes
node perf-doctor-scripts/discover-routes.mjs --src ./src
This scans the project's route config (supports react-router-dom v6 useRoutes / createBrowserRouter patterns) and outputs perf-doctor-scripts/process/routes.json.
AI Task: Review the discovered routes. Remove any that should be skipped (e.g. catch-all *, auth-only pages that can't render without session). Confirm the final route list before proceeding.
Step 0.3: Detect Dev Server URL
AI Task: Before starting the dev server, read the project's vite.config.ts (or vite.config.js, vite.config.mts) to detect the port and base path. Look for:
- Port:
server.portin the Vite config (default: 5173) - Base path:
baseproperty in the Vite config (default:/)
Example patterns to look for:
// vite.config.ts
export default defineConfig({
base: '/kernelswift', // → base path is /kernelswift
server: {
port: 8080, // → port is 8080
},
})
Also check for other config files if Vite config is not present:
webpack.config.js→devServer.portandoutput.publicPathnext.config.js→basePath(port usually 3000)package.json→ scripts section may specify--port
Construct the base URL as: http://localhost:{port}{base} (strip trailing slash from base).
Example: port=8080, base=/kernelswift → http://localhost:8080/kernelswift
If no config is found, default to http://localhost:5173.
Store the detected URL — you will use it as --base-url in all subsequent audit commands.
Step 0.4: Start Dev Server
# In a separate terminal, start the project dev server
pnpm dev
Verify: Dev server is running and accessible at the detected URL from Step 0.3.
Phase 1: Evaluate & Report
Step 1.1: Run Lighthouse Audit
# Use the base URL detected in Step 0.3 (example shows default; replace with actual detected URL)
node perf-doctor-scripts/run-audit.mjs \
--routes ./perf-doctor-scripts/process/routes.json \
--base-url <DETECTED_BASE_URL> \
--output ./perf-doctor-scripts/process/baseline.json \
--round 0 \
--headers '{"id":"99"}' # optional: extra headers for auth bypass
Options:
--headers '<JSON>'— Extra HTTP headers sent with every request (e.g. auth bypass)--cookies <file>— Cookie JSON file for session injection--max-wait <ms>— Max wait for FCP before marking as error (default 30000)
Verify: perf-doctor-scripts/process/baseline.json exists with audit data for all routes.
Step 1.2: Baseline Analysis & Overall Diagnosis
AI Task — This is critical. You must produce TWO outputs:
Output 1: Baseline Diagnosis (Markdown)
SAVE: Write to perf-doctor-scripts/process/diagnosis-round-0.md
Content requirements:
- Overall Performance Summary — A concise paragraph describing the project's overall performance health
- Systemic Issues — Performance problems that affect multiple pages (e.g. large shared bundle, render-blocking resources, heavy framework overhead)
- Per-Page Breakdown — For each page rated C or below:
- Which metrics are failing and their values
- Probable root cause mapped to specific code (file path + line number)
- Priority: critical / high / medium / low
- Optimization Roadmap — Ordered list of recommended fixes by expected impact
Output 2: Structured Iteration Data (JSON)
SAVE: Write to perf-doctor-scripts/process/iterations.json
{
"overallAnalysis": {
"summary": "One paragraph describing the project's overall performance posture",
"systemicIssues": [
{
"issue": "Description of the systemic problem",
"impact": "Which metrics are affected (e.g. LCP, FCP)",
"affectedPages": ["/", "/chat"],
"severity": "critical | high | medium | low"
}
],
"recommendations": [
"Ordered list of recommended optimizations"
]
},
"rounds": []
}
Step 1.3: Generate Baseline Report
node perf-doctor-scripts/generate-report.mjs \
--process-dir ./perf-doctor-scripts/process \
--output ./perf-doctor-scripts/process/performance-report.html
Verify: Open perf-doctor-scripts/process/performance-report.html in a browser and confirm it renders correctly.
Step 1.4: CHECKPOINT — Present Results & Ask User
⚠️ MANDATORY STOP POINT ⚠️
You MUST stop here and present the evaluation results to the user. Include:
- A summary table of all pages with their scores and grades
- The key systemic issues found
- Top recommended optimizations
- The path to the full HTML report
Then ask the user explicitly:
"性能评估报告已生成,是否需要进入优化迭代阶段?我会创建独立分支进行代码修改。"
Do NOT proceed to Phase 2 unless the user explicitly confirms they want optimization iterations.
If the user says no or wants to stop here, the workflow is complete — the baseline report is the final deliverable.
Phase 2: Iterate & Fix (only after user confirms)
Step 2.0: Create Fix Branch
Before making any code changes, create a dedicated git branch to preserve all modifications:
git checkout -b perf-doctor/fix-$(date +%Y%m%d-%H%M%S)
IMPORTANT: All code fixes in Phase 2 MUST be committed to this branch. After each round of fixes, commit the changes:
git add -A
git commit -m "perf(doctor): round {N} - <brief description of fixes>"
This ensures the user can review, cherry-pick, or revert any optimization changes. Do NOT skip this step.
Repeat the following for each round (1, 2, 3). Stop early if:
- All pages reach grade A or above, OR
- A round produces no meaningful improvement (< 3 point gain on any page)
Step 2.N.1: Diagnose
AI Task:
- READ: Latest audit JSON (
process/audit-round-{N-1}.jsonorprocess/baseline.jsonfor round 1) - READ: Source code of the worst-performing pages (components, route files, data fetching logic)
- INSTRUCTION: For each underperforming page:
- Identify the root cause of each poor metric (LCP, FCP, CLS, TBT, SI)
- Map the cause to specific code locations (file path + line number)
- Propose a concrete fix with code changes
- Prioritize fixes by expected impact
SAVE: Write diagnosis to perf-doctor-scripts/process/diagnosis-round-{N}.md with the following structure:
# Round {N} Diagnosis
## Summary
One paragraph summarizing what was found and what will be fixed.
## Findings
### [Page Route] — [Metric] — [severity]
- **Root Cause**: What is causing the poor metric
- **Code Location**: `file/path.tsx:line`
- **Proposed Fix**: What will be changed and why
- **Expected Impact**: Which metrics should improve and by how much
## Planned Changes
1. File: `path/to/file.tsx` — Description of change
2. File: `path/to/file.tsx` — Description of change
Step 2.N.2: Apply Fixes
AI Task: Apply the proposed code fixes to the project source code. Only change what is necessary — do not refactor unrelated code.
IMPORTANT: After applying fixes, record what was changed. You will need this in the next step.
Step 2.N.3: Re-Audit & Record Iteration
node perf-doctor-scripts/run-audit.mjs \
--routes ./perf-doctor-scripts/process/routes.json \
--base-url <DETECTED_BASE_URL> \
--output ./perf-doctor-scripts/process/audit-round-{N}.json \
--round {N} \
--headers '{"id":"99"}' # same headers as baseline
AI Task — After re-audit, you MUST update perf-doctor-scripts/process/iterations.json:
Append a new round entry to the rounds array:
{
"round": 1,
"timestamp": "ISO timestamp",
"diagnosis": {
"summary": "One paragraph summarizing this round's diagnosis",
"findings": [
{
"page": "/route",
"metric": "LCP",
"rootCause": "Concise description of the root cause",
"codeLocation": "src/pages/Home.tsx:42",
"severity": "critical"
}
]
},
"changes": [
{
"file": "src/pages/Home.tsx",
"description": "What was changed and why",
"linesChanged": "42-58",
"targetMetric": "LCP"
}
],
"results": {
"avgScoreBefore": 39,
"avgScoreAfter": 54,
"delta": 15,
"pageDeltas": [
{ "page": "/", "before": 36, "after": 52, "delta": 16 }
],
"effectiveChanges": ["Description of changes that improved scores"],
"ineffectiveChanges": ["Description of changes that had no effect"]
}
}
Also update overallAnalysis if new systemic insights are discovered during this round.
Phase 3: Update Report
Step 3.1: Final Analysis Update
AI Task: Before regenerating the report, review the final state of iterations.json:
- Ensure
overallAnalysis.summaryreflects the complete journey (baseline → final state) - Ensure all rounds have complete data (diagnosis, changes, results)
- Add any final recommendations that weren't addressed
Step 3.2: Regenerate HTML Report
node perf-doctor-scripts/generate-report.mjs \
--process-dir ./perf-doctor-scripts/process \
--output ./perf-doctor-scripts/process/performance-report.html
This regenerates the report, now including iteration data in addition to the baseline:
Tab 1: Results
- Overview cards: page count, average score, grade distribution, improvement delta
- Per-page table: route / score / grade / all metrics / delta from baseline
- Overall Analysis section: summary paragraph + systemic issues list
Tab 2: Iterations
- If no optimization rounds were performed (user chose not to iterate), shows a message indicating baseline-only evaluation
- If iterations were performed, shows timeline view of each round:
- Diagnosis: What problems were found, mapped to code locations
- Changes: What files were modified, what was changed, why
- Results: Before/after score comparison per page, delta indicators
- Effective vs Ineffective: Which changes worked and which didn't
Tab 3: Methodology
- Metric definitions and measurement methods
- S/A/B/C/D grade standards (Core Web Vitals + internal S-tier)
- Lighthouse scoring algorithm and weights
- Common performance anti-patterns quick reference
Verify: Open the updated perf-doctor-scripts/process/performance-report.html in a browser and confirm iteration data is visible.
Grade System
| Grade | Lighthouse Score | Positioning |
|---|---|---|
| S | ≥ 95 | Exceptional (internal benchmark) |
| A | ≥ 90 | Excellent (industry Good) |
| B | ≥ 70 | Acceptable |
| C | ≥ 50 | Needs optimization |
| D | < 50 | Poor |
Per-Metric Thresholds
| Metric | S (Exceptional) | A (Excellent) | B (Acceptable) | C/D |
|---|---|---|---|---|
| LCP | ≤ 1.5s | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| FCP | ≤ 1.0s | ≤ 1.8s | ≤ 3.0s | > 3.0s |
| CLS | ≤ 0.05 | ≤ 0.1 | ≤ 0.25 | > 0.25 |
| TBT | ≤ 100ms | ≤ 200ms | ≤ 600ms | > 600ms |
| SI | ≤ 2.0s | ≤ 3.4s | ≤ 5.8s | > 5.8s |
A page gets grade S only when ALL core metrics meet S thresholds. Any single metric below S downgrades to A or below.
Data Files Reference
All intermediate data is stored in perf-doctor-scripts/process/:
| File | Format | Purpose |
|---|---|---|
routes.json |
JSON | Discovered routes list |
baseline.json |
JSON | Round 0 Lighthouse audit data |
audit-round-{N}.json |
JSON | Round N Lighthouse audit data |
diagnosis-round-{N}.md |
Markdown | Human-readable diagnosis for round N |
iterations.json |
JSON | Structured iteration data (fed into report) |
performance-report.html |
HTML | Final self-contained report |
Troubleshooting
| Issue | Solution |
|---|---|
| Chrome not found | Run npx playwright install chromium in perf-doctor-scripts/ |
| Dev server not responding | Ensure pnpm dev is running and the port matches --base-url. Re-check vite.config.ts for the correct port and base path |
| Route requires auth | Add auth cookies via --cookies cookies.json, or add extra headers via --headers '{"id":"99"}' |
| Lighthouse timeout | Increase --timeout flag (default 60s) or check if page has infinite loading |
| No improvement after 3 rounds | Some bottlenecks (server response, third-party scripts) can't be fixed in frontend code alone — document them in the report |