# PerfLens 🔍

**Lighthouse finds the slow pages. PerfLens finds the slow code.**

[![CI](https://github.com/moiz-imran/perf-lens/actions/workflows/ci.yml/badge.svg)](https://github.com/moiz-imran/perf-lens/actions/workflows/ci.yml)
[![npm version](https://badge.fury.io/js/perf-lens.svg)](https://badge.fury.io/js/perf-lens)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Node Version](https://img.shields.io/node/v/perf-lens)](https://nodejs.org)

PerfLens runs Lighthouse against your dev server, then puts Claude to work on your _source code_ with those results as evidence. The report reads like a code review: "this `useEffect` in `ProductGrid.tsx:47` refetches on every render," with severity, impact, and corrected code for each finding. Every file/line location is checked against the actual file before it reaches the report, and `--fail-on critical` turns the scan into a CI gate.

![perf-lens HTML report](https://raw.githubusercontent.com/moiz-imran/perf-lens/main/docs/report.png)

## How it works

Two analysis modes share the same finding schema and report pipeline:

**Scan mode (default)** — files are prioritized, batched, and sent to Claude with the Lighthouse context. Findings come back through [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) (a zod schema enforced by the API), so results are typed and validated rather than scraped out of prose. Every reported file/line location is verified against the actual file before it reaches the report.

**Agent mode (`--agent`)** — instead of being handed files, the model investigates the codebase itself through a hand-rolled tool-use loop (`list_files`, `read_file`, `grep`), forms hypotheses from the Lighthouse data, reads the relevant code, and submits findings via a final `report_findings` tool call. Tool access is sandboxed to the target directory.

Under the hood:

- **Structured outputs** via `messages.parse()` + zod — schema-guaranteed JSON findings
- **Agentic tool use** — multi-turn investigation loop with strict tool schemas and a forced final report
- **Prompt caching** — the stable system prefix (expert prompt + Lighthouse context) is cached across batch calls
- **Result caching** — unchanged file batches are never re-analyzed (`.perflens-cache.json`, content-hash keyed)
- **Cost tracking** — every scan ends with a token + estimated cost summary

## Installation

```bash
npm install --save-dev perf-lens
# or globally
npm install -g perf-lens
```

Requires Node.js ≥ 20 and Chrome (for Lighthouse).

## Quick start

1. Set your Anthropic API key (one of):

```bash
perf-lens config set-key YOUR_API_KEY
# or
export ANTHROPIC_API_KEY=YOUR_API_KEY
```

2. Start your dev server, then run a scan from your project root:

```bash
perf-lens scan
```

PerfLens auto-detects the dev server port (from `package.json` scripts or common ports), runs Lighthouse, analyzes your code, and writes `performance-report.md`.

3. Try agent mode:

```bash
perf-lens scan --agent
```

### CI gate

Fail the pipeline when serious issues appear:

```bash
perf-lens scan --fail-on critical   # exit 1 if any critical finding
perf-lens scan --fail-on warning    # exit 1 on critical or warning
```

## CLI reference

```
perf-lens scan [options]

  -c, --config <path>          Path to config file
  -p, --port <number>          Development server port
  -t, --target <directory>     Directory to scan (default: current directory)
  -f, --max-files <number>     Maximum number of files to analyze
  -b, --batch-size <number>    Files per batch
  -s, --max-size <number>      Maximum file size in KB
  -d, --batch-delay <number>   Delay between batches (ms)
  -o, --output <path>          Report output path
  --format <type>              Output format: md or html
  --agent                      Agentic analysis (model investigates via tools)
  --fail-on <severity>         Exit 1 if findings at/above severity exist (CI gate)
  --no-cache                   Skip the analysis result cache
  --mobile                     Mobile emulation for Lighthouse
  --cpu-throttle <number>      CPU throttle multiplier
  --network-throttle <type>    slow3G | fast3G | 4G | none
  --timeout <number>           Lighthouse timeout (ms)
  --verbose                    Verbose output

perf-lens config set-key <key>   Save your Anthropic API key
perf-lens config get-key         Show the configured key (masked)
```

## Configuration

PerfLens loads config via [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig): `.perflensrc`, `.perflensrc.json|yaml|js`, `perflens.config.js`, or a `perflens` field in `package.json`.

```js
// perflens.config.js
export default {
  ai: {
    model: 'claude-opus-4-8', // default; use 'claude-haiku-4-5' for cheaper scans
    maxTokens: 16000,
  },
  thresholds: {
    performance: 90,
    lcp: 2500,
    cls: 0.1,
  },
  analysis: {
    targetDir: '.',
    maxFiles: 200,
    batchSize: 20,
    ignore: ['**/generated/**'],
  },
  lighthouse: {
    mobileEmulation: true,
    throttling: { cpu: 4, network: 'fast3G' },
  },
  output: {
    format: 'html',
    directory: './reports',
  },
};
```

A `.perflensignore` file (gitignore syntax) is also honored.

### Model selection

The default model is `claude-opus-4-8` (deepest analysis). For faster/cheaper scans set `ai.model` to `claude-haiku-4-5`; `claude-sonnet-5` sits in between. Cost is printed after every scan so you can decide with real numbers.

### API key resolution

`PERF_LENS_ANTHROPIC_API_KEY` → `ANTHROPIC_API_KEY` → `~/.perf-lens/config.json` (written by `config set-key`).

## Reports

- **Markdown** — metrics summary, Lighthouse insights, and findings grouped by severity (🚨 critical / ⚠️ warning / 💡 suggestion), each with description, impact, solution, and a corrected code example.
- **HTML** — the same content as a self-contained styled dashboard.

## FAQ

**Why not just hand my Lighthouse report to Claude Code or Cursor?**
For a one-off investigation, do exactly that. It works, and it's the same idea this tool is built on. PerfLens exists for the runs after the first one. A fresh agent explores differently every time and produces different findings in different formats, which makes it a flaky CI check. PerfLens pins that down: schema-enforced structured output, every reported file/line verified before it hits the report, content-hash caching so unchanged code produces identical results instead of a re-roll, severity-based exit codes for gating, and per-scan cost you can see and cap. Build that layer around your own agent pipeline and you also own the glue code, the prompt drift, and the pager.

**How is this different from Lighthouse alone?**
Lighthouse measures and points at _resources_ (bundles, requests, images). PerfLens continues the investigation into the _code_ that produced them, and proposes the fix.

**What does a scan cost?**
You bring your own Anthropic API key and pay per scan; there's no service in the middle. Exact token usage and estimated cost are printed after every scan. Result caching means re-scans only pay for changed files, and you can set `ai.model` to `claude-haiku-4-5` for cheap scans.

**Where does my code go?**
Directly to the Anthropic API, nowhere else. In agent mode, the model's file access is sandboxed to the target directory. Use `.perflensignore` to exclude anything you don't want analyzed.

## Development

```bash
npm install
npm run build      # tsc + prompt templates
npm test           # vitest
npm run typecheck
npm run lint
```

Releases are automated with semantic-release on `main`.

## License

MIT © Moiz Imran
