English · Português (Brasil)
RepoLens reads a codebase and tells you how it fits together: the stack, what has to be running, which environment variables you need, how to start it, and what's misconfigured.
No configuration needed. No cloud upload. No AI API. It never runs your code.
npx @douglas-strey/repolensJoining a project usually starts with 20 minutes of archaeology. Which package manager?
Which Node version, and why does the Dockerfile disagree with .nvmrc? What has to be
running locally? Which env vars does the code actually read, and why isn't that one in
.env.example?
RepoLens does that pass for you, reading only the files in the repository.
npx @douglas-strey/repolens # overview of the current directory
npx @douglas-strey/repolens doctor # setup problems, with suggested fixes
npx @douglas-strey/repolens setup # set up a fresh clone: env file, services, dependencies
npx @douglas-strey/repolens ../other-repo --jsonOr install it to use the shorter command, repolens:
brew install douglas-strey/tap/repolens # macOS and Linux (Homebrew)
npm install -g @douglas-strey/repolens # any OS with Node.js 22+Works on macOS, Linux and Windows. The npm package needs Node.js 22 or newer; Homebrew installs Node for you.
Just cloned it? repolens setup creates the env file from the example,
starts the backing services and installs dependencies, after showing you exactly what it
will run.
Scanning a repository you don't trust? Run RepoLens from outside it, e.g.
cd ~ && npx @douglas-strey/repolens ~/path/to/repo, or use a global install. Whennpxruns inside a repository, npm itself honors that repository's.npmrcandnode_modules, which could substitute a different package before RepoLens even starts.
| Project | Type (monorepo, app, library, CLI), languages, package manager, runtime versions, Go main packages and bin commands, Git branch and remote |
| Frameworks | Per workspace package, with version and the evidence behind each detection |
| Workspace | pnpm / npm / Yarn / Bun workspaces, Turborepo, Nx, Lerna, go.work, with each package's framework |
| Quick start | Suggested first commands: copy the env file, start Compose services, install, run |
| Services | Docker Compose services with images, published ports and profiles, plus Dockerfiles (base images, stages, exposed ports) |
| Databases | PostgreSQL, MySQL, MongoDB, Redis, SQLite and more, from drivers, Compose images, env URLs and Prisma / Drizzle / TypeORM config, plus the ORM in use |
| Environment | Every variable: is it set in .env, documented in .env.example, used in code? Names only; values are never printed |
| Scripts | package.json scripts with the exact command to run them, plus Makefile, justfile, Taskfile and Deno tasks |
| Routes | API routes and pages for Nuxt, Next.js, Express, Fastify, NestJS, Hono, Gin, Echo, chi, Fiber, Gorilla mux and net/http |
| CI | Workflows and what each job does (lint, test, build, deploy, …) |
| Tooling | Test frameworks, linters, formatters, type checkers, Git hooks and build tools |
Use --verbose to see low-confidence findings, the evidence behind each detection, and
every variable, route and script.
doctor runs 35 checks for problems that cost teams time. Each finding has a stable
code, a one-line explanation and a suggested fix:
Some of the things it catches:
- env vars used in code but missing from
.env.example, and documented ones nothing reads .envfiles tracked by Git, or not covered by.gitignore- a real credential committed to
.env.example(AWS keys, GitHub tokens, Stripe live keys, …), reported by variable name only - secrets exposed to the browser (
NEXT_PUBLIC_…SECRET,VITE_…PASSWORD) package-lock.jsonnext topnpm-lock.yaml, or a lockfile that contradictspackageManager- runtime versions with known vulnerabilities: a pinned PHP, Node.js or Go release older than its line's latest security release
- Node versions that disagree across
.nvmrc,.node-version,engines, Dockerfiles and CI, and end-of-life versions - a
go.modthat asks for a newer Go than the Docker image or CI provides - two Compose services publishing the same port, and
DATABASE_URLpointing at 5433 while Postgres is published on 5432 env_fileentries that don't exist, obsolete Composeversion:keysturbo.jsonstill usingpipelineon Turborepo 2, legacy.eslintrcwith ESLint 9+, duplicated workspace config- config files that don't parse
See every check in docs/diagnostics.md.
repolens doctor # exits 1 on errors
repolens doctor --strict # also fails on warnings
repolens doctor --online # also check dependencies against OSV.dev
repolens doctor --json # for scripts and CIA plain repolens scan always exits 0 when it completes, so adding it to a CI log never
breaks a build. Gate on doctor instead:
- run: npx --yes @douglas-strey/repolens doctor --fail-on errorA security release only protects the projects that upgrade. doctor reads every place a
project pins its runtime, from Dockerfiles to CI steps and version files, and tells you
when that version has known vulnerabilities. The PHP image you forgot in a Dockerfile shows
up the day the fix ships:
- PHP, Node.js and Go. An exact pin older than the latest security release of its line is reported with how many fixes it misses, the severity the project gave them, the CVEs and the version to upgrade to.
- A critical fix fails the run. Missing a fix rated critical is an error, so
repolens doctorexits with 1 in CI; other missed fixes are warnings. A repository's own config can't turn the check off or lower it. - Straight from the source, still offline. The security releases come from php.net, the Node.js security posts and the Go release history, in a table that ships with RepoLens and is updated with every release. The scan makes no network requests, so upgrading RepoLens is how you get the latest advisories.
- Only versions that are really pinned.
php:8.3-fpm,^8.3andenginesranges follow the latest patch and aren't reported;php:8.3.10-fpmis. - Your dependencies too, when you ask.
repolens doctor --onlinelooks every exact version in your lockfiles (npm, pnpm, Yarn, Bun, Composer, Go modules) up in OSV.dev and reports the advisories with the version that fixes them. It is the only thing in RepoLens that uses the network, and it sends package names and versions, nothing else.
Details in docs/diagnostics.md.
Just cloned a project? setup does the quick start for you: it creates your local env
file from the example, starts the backing services with Docker Compose and installs
dependencies (npm, pnpm, Yarn, Bun or Go modules). Try it with --dry-run first to see
the plan without running anything:
Installing dependencies runs code from the project and its dependencies, so setup is upfront about it:
- It shows the plan first, including every install lifecycle script the project
defines (
postinstall,prepare, …) and the images Compose will pull or build. - It asks before running anything. Answer
yto go ahead; without a terminal it refuses to run unless you pass--yes. - It only runs commands it builds itself. Project scripts such as
make setuporpnpm db:upare listed under "Skipped" for you to run. - It never touches your values. An existing
.envis left alone, and a new one is never filled in. - It checks your Node.js version against
engines.nodebefore installing, and stops at the first step that fails. Running it again is safe.
repolens setup # show the plan, ask, then run it
repolens setup --dry-run # only show the plan
repolens setup --ignore-scripts # install without lifecycle scripts (untrusted code)
repolens setup --yes # don't ask (CI, scripts)When it finishes, setup tells you whether repolens doctor still finds problems and how to
start the project. Details in docs/cli.md and
docs/security.md.
Let your AI assistant ask RepoLens about the project. repolens mcp is a
Model Context Protocol server: Claude Code, Codex,
Cursor and other MCP clients start it and call its tools whenever they need to know how the project
fits together, instead of reading dozens of files to find out:
- Six tools:
get_project_overview,get_services,get_environment_variables,get_routes,get_scriptsandget_diagnostics. Each answers with the matching part of the Markdown report (or JSON, on request), for the whole project or one package (path: "apps/web"). - Always current. Every call scans the files as they are, so answers follow your edits.
- Same guarantees as the CLI. It only reads files, reports environment variables by name and never their values, and doesn't look outside the project.
- No extra dependencies. The protocol is built into RepoLens and speaks both the current MCP revision and the earlier ones.
Add it to Claude Code:
claude mcp add repolens -- npx -y @douglas-strey/repolens mcpTo Codex:
codex mcp add repolens -- npx -y @douglas-strey/repolens mcpOr to Cursor, in .cursor/mcp.json:
{
"mcpServers": {
"repolens": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@douglas-strey/repolens", "mcp", "${workspaceFolder}"]
}
}
}For repositories you don't trust, install RepoLens once (npm install -g @douglas-strey/repolens) and use repolens mcp as the command: npx run inside a
repository honors its .npmrc. Details in docs/cli.md.
RepoLens needs no configuration. To adjust it, add a repolens.config.json to the project
(shared with everyone who scans it, including CI) or a user config at
~/.config/repolens/config.json (your defaults everywhere):
repolens config shows which files apply and the settings in effect, and --no-config
ignores them all. Configuration is plain JSON that RepoLens never executes, and a
repository's own config can't turn off its security checks. See
docs/configuration.md.
repolens --json | jq '.environment.variables[] | select(.used and (.documented | not)) | .name'
repolens --json | jq '.routes.routes[] | "\(.method) \(.path)"'The JSON output is versioned ("schemaVersion": 1), deterministic (no timestamps, sorted
arrays, relative paths only) and documented in docs/json-schema.md.
TypeScript types are exported from the package:
import { scan } from '@douglas-strey/repolens'
const result = await scan({ cwd: '/path/to/repo' })
console.log(result.frameworks.map((f) => f.name))repolens report writes the whole picture as Markdown, which is handy for onboarding docs,
technical audits, or pasting into an AI chat:
repolens report --output REPOLENS.mdrepolens agent init (experimental) generates short, structured context for coding
agents such as Claude Code, Codex, Cursor or Copilot. It covers the commands to run, the
conventions RepoLens can back with evidence ("use pnpm, not npm", "Node 22", "CI runs
lint, test and build"), the structure, services, env var names and known issues:
repolens agent init # writes .repolens/*.md
repolens agent # prints agent-context.md to stdoutPoint your agent at it from AGENTS.md or CLAUDE.md. Review the files before committing
them; they never contain secret values.
| Area | Support |
|---|---|
| Languages | JavaScript, TypeScript and Go in depth. File counts for Python, Rust, Ruby, PHP, Java, Kotlin, Swift, C/C++, C#, Shell and more |
| Package managers | npm, pnpm (including catalogs), Yarn Classic and Berry, Bun, Go modules; packageManager and devEngines |
| Runtimes | Node.js (.nvmrc, .node-version, .tool-versions, mise, engines, Volta, devEngines, Dockerfiles, CI), Go, PHP (composer.json, .php-version, Dockerfiles, setup-php), Bun, Deno |
| Monorepos | pnpm, npm, Yarn and Bun workspaces, Turborepo, Nx, Lerna, go.work |
| Frameworks | Nuxt, Next.js, Remix, React Router, SvelteKit, Astro, Angular, React, Vue, Svelte, Solid, Preact, Qwik, Express, Fastify, NestJS, Koa, Hono, hapi, Elysia, AdonisJS, tRPC, Electron, React Native, Expo, Gatsby, Docusaurus, VitePress; Go: Gin, Echo, chi, Fiber, Gorilla mux, gRPC, net/http |
| Routes | Nuxt (server routes and pages, srcDir), Next.js (App and Pages Router, basePath, pageExtensions), Express, Fastify, NestJS, Hono, Gin, Echo, chi, Fiber, Gorilla mux, net/http; router prefixes are resolved across files, including Go routers passed to functions |
| Databases & ORMs | PostgreSQL, MySQL, MariaDB, SQLite/libSQL, MongoDB, Redis/Valkey, CockroachDB, SQL Server, ClickHouse and more; Prisma, Drizzle, TypeORM, Sequelize, MikroORM, Kysely, Knex, Mongoose, GORM, Ent, sqlx, sqlc |
| Containers | Docker Compose (all file names, overrides and variants, local include and extends, long and short syntax), Dockerfiles (multi-stage, ARG substitution) |
| CI | GitHub Actions, GitLab CI (with local include and job extends), CircleCI, Forgejo/Gitea Actions; Azure Pipelines, Jenkins, Travis, Bitbucket, Buildkite, Drone and Woodpecker are listed |
| Tooling | Vitest, Jest, Mocha, AVA, node:test, Bun test, Playwright, Cypress, go test, pytest; ESLint, Biome, Oxlint, Prettier, golangci-lint, Ruff; Husky, Lefthook, lint-staged; Vite, webpack, Rollup, esbuild, tsup, tsdown and more |
Other repositories still get languages, Makefile/justfile targets, CI, key config files and the generic doctor checks. Missing something? Request a detector; most are a table entry and a test.
RepoLens is built to be the first thing you run in a repository you don't know yet.
- It never executes anything from the repository. No scripts, no config files (it reads
nuxt.config.tsas text), no Docker. It doesn't even rungit: repository config can makegitrun arbitrary commands, so branch, remotes and tracked files are read straight from.git/. The one exception isrepolens setup, which you run on purpose: it shows each command and the scripts it will trigger, and asks first. - It never prints secret values. The
.envparser keeps variable names and throws the values away. Committed text it echoes back (scripts, dependency URLs, Git remotes) is redacted. The test suite checks that sentinel secrets never show up in any output format. - It only reads inside the directory you point it at. Symlinks are resolved and checked, directory symlinks are never followed, and FIFOs, binaries and huge files are skipped.
- No telemetry, no network by default. RepoLens makes no network requests unless you
run
repolens doctor --online, which sends package names and versions to OSV.dev.
The full model is in docs/security.md. Found a hole? Please report it privately.
RepoLens walks the repository once, respecting .gitignore and skipping node_modules,
vendor and build caches. Independent detectors then read what they need through a
shared, cached, sandboxed context, each producing one section of the result. Doctor
rules check those facts, and renderers turn them into terminal, JSON, Markdown or
agent output.
walk (.gitignore-aware) → file index → detectors (parallel, memoized) → doctor rules → renderers
Every uncertain finding carries a confidence level and its evidence, and low-confidence
findings stay hidden unless you ask for them. Runtime dependencies are yaml, ignore and
semver, none of which has dependencies of its own. More in
docs/architecture.md.
Speed: on an Apple M1 Max with Node.js 26, scanning this repository (about 420 files)
takes around 35 ms, a synthetic 500-package monorepo (3,000 files) around 250 ms, and
3,000 packages (18,000 files) around 1.2 s. Run pnpm bench to measure on your machine.
v0.5 (now): everything above. JavaScript/TypeScript and Go in depth; terminal, JSON,
Markdown and agent output; an MCP server for AI assistants; 36 doctor checks, including
known vulnerabilities in pinned runtimes and, with --online, in locked dependencies;
repolens setup for a fresh clone; user and project configuration files, with
per-finding ignores and a monorepo root's config.
Next
- Python (Django, Flask, FastAPI, uv/Poetry), Rust (Cargo workspaces), PHP (Laravel, Composer)
- More routes: SvelteKit, Remix / React Router, Astro endpoints
- Submitting the formula to
homebrew-core(sobrew install repolensworks without the tap)
Later
- A plugin API for third-party detectors (
@repolens/detector-*) - Architecture and dependency graphs
- Editor extensions
Nothing on this list exists yet. Follow it on the roadmap board, where every item has an issue to comment on if you'd like to help build it, and ROADMAP.md describes each one, with where to start and how big it is. Ideas and votes are welcome in Discussions.
Why is the npm package called @douglas-strey/repolens?
The unscoped repolens names on npm were already taken or too close to existing packages.
The command you run is still repolens.
Does it upload my code or use an AI model?
No. Everything happens locally with static analysis. There is no account or API key, and
no network access unless you run repolens doctor --online, which sends package names and
versions (never code) to OSV.dev.
Is it safe on a repository I don't trust?
That's the main design goal: it never executes project code, config files, hooks or git,
and it never reads outside the directory you scan (except the enclosing .git metadata and
the repository root's config when you scan a subdirectory). repolens setup is the exception you opt into: it installs
dependencies, which runs the project's install scripts, so it lists them and asks first
(--ignore-scripts skips them). See docs/security.md.
It reported something wrong or missed something.
Please open an issue
with the file names involved. Run with --verbose to see the evidence behind each
detection.
How is this different from a linter? Linters check code. RepoLens checks how a repository is put together: the setup that decides whether a new contributor can run it on day one.
Contributions are very welcome, especially new detectors and doctor checks.
pnpm install
pnpm test
pnpm dev -- ../some-projectLooking for a first task? The good first issues are small, self-contained and not taken yet. Then read CONTRIBUTING.md and docs/creating-a-detector.md.
MIT © Douglas Strey
{ "$schema": "https://unpkg.com/@douglas-strey/repolens/schema/config.schema.json", "ignore": ["legacy/", "src/generated/"], // .gitignore syntax "doctor": { "failOn": "warning", // default for --fail-on "rules": { "ENV_UNUSED": "off", "LOCKFILE_MISSING": "error" } }, "environment": { "provided": ["FLY_*"] } // set by the platform, never "missing" }