Skip to content

Repository files navigation

PR Lens

Understand a pull request before you read a line of it.
PR Lens draws every pull request as animated architecture and data-flow diagrams,
posted as a comment inside the pull request itself

Install the GitHub App The CLI on npm Join the Discord prlens.dev MIT licence

Free for open source   ·   Or let your coding agent draw it: npx skills add coldteadotai/pr-lens

Three steps: install the GitHub App on any repository, open a pull request, the diagram appears and is redrawn on every push

A PR Lens bot comment in a pull request: stats chips, an architecture diagram of a small change, and the view-option checkboxes

Features

Architecture blast radius

What the pull request touches, drawn against the system around it: the components involved, and the calls that run between them.

Colour carries the delta: green new, amber changed, red gone

An architecture diagram of a real refactor: three lanes, ten components, the changed ones picked out in colour

Data flow you can watch

The ordered pipeline of the change as an animated sequence: one dot crosses one arrow at a time, in the order the steps happen.

An animated sequence diagram: seven steps taking one cycle in turn

See the payload on every data flow

Click any arrow and the payload opens beside the graph: request and response, each as a declared shape or a sample body. New keys land green, dropped ones red, the same colours the graph uses.

Clicking the enqueue broadcast job arrow: a side panel opens with the request body, its added keys green and a dropped key red, then the Response tab and the declared shape

Drill down without leaving the page

The comment nests <details> sections, each carrying its own diagram scoped to one part of the change: the whole blast radius on top, then the new path, then what was retired.

The same pull request narrowed to one nested view: two lanes, the new batch path only

Open it big

Every comment links to the interactive canvas: the same diagrams at full size, with pan, zoom and a light or dark theme, so a large change is read at the size it needs rather than the width of a comment.

The canvas: a small architecture render zoomed into, switched to the light theme, and fitted back to the screen

Walk the change

A walkthrough tours the change one step at a time. It dims everything else, lights the cards and routes for that step, and says in a line what happened there. Press play on the canvas, or W.

A walkthrough on the canvas: the first step lights the signup route and the new queue, the second zooms to the queue and the worker that drains it

Your agent, live on the canvas

Ask your local coding agent to connect live to a canvas, then use it to understand any codebase or code change. Ask follow-up questions, drill into any part of the graph, or drag across several nodes and ask your agent about them.

A terminal agent beside a live canvas: a question typed in the terminal comes back on the canvas as a two-step answer, and the canvas follows the agent through each step

From one card to a monorepo

The same visual grammar answers for every size of change: lanes, node cards, delta colours, and routes you can trace with your eye alone.

For what its worth, you should not be opening a PR this large

A six-lane monorepo graph: 37 nodes and 49 routed edges

Light or dark theme

With the Github app, every diagram ships as a pair, and GitHub shows the one that matches the reader's theme. Or you can render any theme locally via your coding agent

One small architecture render cut diagonally: the dark theme on the left, the light theme on the right, with every card and route lining up across the seam


Hall of Fame

The pull requests behind Hooks, Node fetch and Ingress, run back through PR Lens. Same renderer and same contract as the diagrams above.

React Hooks, redrawn: four lanes across the React package, the reconciler, the server renderer and shared config

react/react#13968 · 36 files · +5,868/−130 · 5 lanes. Hooks arrive behind a feature flag.

Node's fetch implementation, redrawn across five lanes

nodejs/node#41749 · 16 files · +8,076/−3 · 5 lanes. fetch, Request, Response and Headers land in core.

The first Kubernetes Ingress type, redrawn across four lanes

kubernetes/kubernetes#14175 · 8 files · +766/−0 · 4 lanes. The first Ingress resource type, for L7 load balancing.

Seven more · Vue, Rust, Tokio, Neovim, Django, webpack, vLLM

Vue's script setup and ref sugar, redrawn across three lanes

vuejs/core#2532 · 11 files · +1,081/−670 · 3 lanes. &lt;script setup&gt; and the original ref sugar.

Rust's question-mark operator, redrawn across four lanes

rust-lang/rust#31954 · 26 files · +369/−16 · 4 lanes. The postfix ? operator, chainable shorthand for try!.

Tokio's work-stealing thread pool rewrite, redrawn across three lanes

tokio-rs/tokio#1657 · 100 files · +7,408/−6,795 · 3 lanes. The work-stealing pool rebuilt to cut scheduler overhead.

Neovim's built-in LSP client, redrawn across three lanes

neovim/neovim#11336 · 15 files · +5,556/−1 · 3 lanes. The LSP client moves into Neovim itself.

Django's ASGI handler, redrawn across five lanes

django/django#11209 · 38 files · +931/−42 · 5 lanes. An ASGI handler and a coroutine-safe request context.

Webpack's ContainerPlugin, redrawn across five lanes

webpack/webpack#10440 · 13 files · +567/−5 · 5 lanes. ContainerPlugin, and module federation with it.

vLLM's PagedAttention V2, redrawn across three lanes

vllm-project/vllm#1348 · 6 files · +764/−139 · 3 lanes. PagedAttention V2 and its sequence-level parallelism.

Open the Hall of Fame → Every diagram there is live.


Configuration

Use .github/pr-lens.yml to customize PR Lens. On GitLab and Bitbucket, where there is no .github/ directory, .gitlab/pr-lens.yml and a plain pr-lens.yml at the root work the same way. See the configuration reference for settings, defaults, and examples.

Make corrections

For CLI rendering, put map corrections in .github/pr-lens.yml rather than editing generated SVGs:

schemaVersion: 0.1.0
map:
  rename:
    - match: services/legacy-mailer.ts
      to: Postmark sender
  exclude:
    - "**/*.test.ts"

It is an overlay, so it keeps holding as the code moves and the model renames things between runs. Renames, exclusions, lane pins and groupings, all in packages/cli.


Other ways to run it

The App is the whole setup for most people. The modes below cover what it does not: your own CI, your own model, or a diagram before the pull request exists.

Via your coding agent

A lot happens before a PR's issued. You can leverage the same underlying renderer to understand any codebase, or agent changes before a PR is issued, or just keep a sharp mental model of the system as it evolves

Claude Code, Codex, Gemini CLI, Cursor, OpenCode and Copilot

npx skills add coldteadotai/pr-lens

Then instruct your coding agent e.g.:

Diagram the change you just made with PR Lens and attach it to the pull request.

The agent reads the diff, writes the document, runs npx @coldtea/pr-lens-cli validate until the contract is satisfied, renders, and puts the diagram in the pull request description with gh pr create --attach, so it lands with the change instead of behind it. If a diagram names things wrongly, the same skill teaches it to fix .github/pr-lens.yml instead of editing generated output. Details in packages/agent-skill.

Prefer to have the agent do the whole setup? Paste this:

Set up PR Lens for me: it draws code changes as animated architecture and data-flow diagrams.

1. Install the agent skill: `npx skills add coldteadotai/pr-lens`.

2. Walk me through installing the GitHub App at https://github.com/apps/coldtea-pr-lens on every repository where I review pull requests. It posts one sticky comment per pull request and updates it on every push, with no model key of mine involved.

3. If I'd rather run it from CI with a model key of mine, offer the Action instead: `.github/workflows/pr-lens.yml` using `coldteadotai/pr-lens/packages/action@v0`, with the key as a repository secret. It takes any endpoint speaking `/chat/completions` e.g., OpenAI, Gemini etc.

4. Then test it: diagram the most recent change in this repository and show me the rendered SVGs or canvas
As a workflow: the GitHub Action · your CI · your key · one static comment

The same comment from your own CI, drawn with your own model key. Add that key as a repository secret — GEMINI_API_KEY below, because provider defaults to Gemini — then commit this as .github/workflows/pr-lens.yml:

name: PR Lens

on:
  pull_request:

permissions:
  contents: write # to publish the rendered SVGs
  pull-requests: write # to post the comment

concurrency: # one run per pull request; a push supersedes the last
  group: pr-lens-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  lens:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # the diff is between two commits, so both must be here
      - uses: coldteadotai/pr-lens/packages/action@v0
        with:
          api-key: ${{ secrets.GEMINI_API_KEY }}

Nothing here is tied to one model. provider takes gemini (the default), openai, or openai-compatible with a base-url and model, so the same workflow runs against OpenRouter, DeepSeek or a server of your own. The key reaches the CLI through the environment, never a command line, and the diff goes to the provider you name and nowhere else. The comment here is deliberately static — an Action cannot hold state between runs, so the checkboxes live in the App. Providers, lenses, branding and the rest of the inputs are in packages/action.

On GitLab: the CI/CD component · your pipeline · your key · one sticky comment

GitLab CI can post the same diagrams on a merge request. Add two masked variables, your model key and a project access token with the api scope (the token posts the comment), then include the component:

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

include:
  - component: gitlab.com/coldteadotai/pr-lens/pr-lens@0.1.0

The workflow: rules block has to live in your own .gitlab-ci.yml. Merge request pipelines only exist when that file asks for them, and rules inside an included component do not count. The access token is required because CI_JOB_TOKEN cannot post notes. On GitLab.com, project access tokens need Premium or Ultimate. On the Free plan, a personal access token with the api scope works too, and the comment posts under your name instead of a bot's.

The rendered SVGs are uploaded to the project as attachments, so they load for every reader of the merge request, including people who cannot fetch a raw file from the repository. On a private project, that has a cost: the attachment URL is the permission. The URL is unguessable, but anyone who has it can see the diagram, member or not. If a project turns on Require authentication to view media files, the images are blocked, and readers can open the diagrams from the canvas link in the comment instead. Details are in packages/gitlab-component.

On Bitbucket: the pipe · your pipeline · your key · one sticky comment

On Bitbucket Pipelines, add the pipe under pull-requests::

clone:
  depth: full # the diff needs history back to the merge base

pipelines:
  pull-requests:
    "**":
      - step:
          name: PR Lens
          script:
            - pipe: docker://ghcr.io/coldteadotai/pr-lens-pipe:0.1.0
              variables:
                GEMINI_API_KEY: $GEMINI_API_KEY
                PR_LENS_TOKEN: $PR_LENS_TOKEN

A pipe sees only the variables its step passes in, so the step passes two secured variables by name: your model key, and a repository access token with the pullrequest:write and repository:write scopes. The second scope lets the pipe publish the diagrams to the repository's Downloads. Repository access tokens come with every Bitbucket plan; workspace-wide tokens need Premium.

Bitbucket renders comments as plain Markdown, so the comment has no collapsible sections or light/dark pair. It shows the headline, the numbers, one diagram per lens, and the drill-down views in order. Bitbucket never runs a pipeline for a pull request opened from a fork, so the pipe cannot draw those pull requests, whatever its configuration. A repository that depends on fork contributions should use the hosted app. Details are in packages/bitbucket-pipe.

From the CLI · every step on your machine, one at a time

Everything the other modes do, one step at a time, on your machine. Only analyze talks to a model, and its key is read from the environment, never from a flag:

export GEMINI_API_KEY=…    # the default provider; OPENAI_API_KEY with --provider openai

# Diff in, graph document out — measured against the merge base, not the branch tip.
npx @coldtea/pr-lens-cli analyze --base origin/main

# The document as light and dark SVGs, plus the manifest a comment is built from.
# Each drawing lands in its own directory under .pr-lens/, named after its title.
npx @coldtea/pr-lens-cli render .pr-lens/graph.json

# The pull request comment as markdown, on stdout. Posting is your business.
npx @coldtea/pr-lens-cli comment --graph .pr-lens/<drawing>/drawn.graph.json --manifest .pr-lens/<drawing>/manifest.json \
  --asset-base-url https://raw.githubusercontent.com/owner/repo/pr-lens/42

# Any PR Lens document, checked against the contract — every problem, not just the first.
npx @coldtea/pr-lens-cli validate .pr-lens/graph.json .github/pr-lens.yml

# After the merge: the pull-request document as a stored map of the system, worth committing.
npx @coldtea/pr-lens-cli export .pr-lens/graph.json -o .github/pr-lens.map.json

Everything lands in .pr-lens/, which the CLI adds to your .gitignore the first time it writes there. Treat it as scratch: the files are rebuilt from the diff on demand, and the only one worth committing is the map export writes. --out puts them somewhere else if you would rather.

comment --target gitlab or --target bitbucket writes markup that forge can render, and analyze reads the forge from your git remote. Ollama, DeepSeek, OpenRouter and anything else speaking /chat/completions are reached with --provider openai-compatible --base-url <url>. The full command reference, the correction file, and the failure codes a script can branch on are in packages/cli.

In your terminal · the diagram before the pull request exists

Nothing about the diagrams needs a pull request. Render locally and look at the change before anyone else does:

npx @coldtea/pr-lens-cli analyze --base origin/main
npx @coldtea/pr-lens-cli render .pr-lens/graph.json
open .pr-lens/*-dark-*.svg    # macOS; the SVGs are self-contained, any browser reads them

This is also the shape of reviewing an agent's work: while you read the diff, the agent that wrote it renders it. With the skill installed, "render this change with PR Lens and open the SVGs" gets you the diagram beside the diff, the same picture its pull request will carry, minutes earlier.


Packages

Package What it is
packages/schema @coldtea/pr-lens-schema: the contract every other component speaks
packages/renderer @coldtea/pr-lens-renderer: deterministic JSON graph → the animated, theme-paired SVGs on this page
packages/cli @coldtea/pr-lens-cli: read a diff with your own model key, render it, compose the comment
packages/action the GitHub Action: analyze, publish, post one static comment
packages/gitlab-component the GitLab CI/CD component: the same run on a merge request
packages/bitbucket-pipe the Bitbucket pipe: the same run on a pull request
packages/agent-skill @coldtea/pr-lens-agent-skill: teaches a coding agent to draw the change it just made

Working in this repo

pnpm install
pnpm verify      # build, typecheck, test

Node 20.11+ and pnpm 10.


Self-hosting the canvas

Please start here docs/canvas-api.md


Community

Ask questions or show us your diagrams on Discord. We post releases and new features on X at @drawwithlens


Contributing

Open an issue first and wait for one of us to approve it before you (or agents) write any code. A pull request with no approved issue behind it will be closed. Once your issue is approved, link it from the pull request.

Commit under your own name only. No Co-Authored-By line for a model, no "Generated with" footer, no session link — use an agent if you like (and we do too), but the commits are yours, full responsibility. Most tools add these unless you turn them off.


Why we built this

Reducing the cognitive load of reviewing PRs


License

MIT © Coldtea

About

Review code 100X faster. Lens draws every PR as animated architecture and data-flow walkthroughs, inside the pull request itself. Use it as a GitHub App, GitHub Action, CLI, or a skill for your coding agent

Topics

Resources

Security policy

Stars

1.7k stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages