# pyscn: a Go analyzer for Python that grades the codebase and explains the grade

> Written in Go with tree-sitter, shipping as a zero-dependency Python package with an MCP server and agent skills attached. The recent releases are a useful lesson in what it takes to make an aggregate score trustworthy.

**ludo-technologies/pyscn** — Code quality analysis for Python in the age of AI coding.

- Repository: https://github.com/ludo-technologies/pyscn
- Website: https://docs.codescan.dev/
- Stars: 1,101 · Forks: 79
- Language: Go
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/ludo-technologies-pyscn

## A structural auditor wearing a linter's clothes

The README's positioning line is aimed at a specific reader: code quality analysis for Python in the age of AI coding, aimed at people building with Cursor, Claude or ChatGPT who want AI-generated code to stay maintainable. That framing is marketing, but it points at a real gap. Linters answer narrow questions about correctness and style. Very few of them will tell you that two hundred lines of your codebase are structurally duplicated, that a directory has become a hotspot, or that your layering rules are being violated in eleven places.

pyscn says it looks at code from five angles and then lists six, which is a small blemish but tells you the list grew recently. The angles are dead code, duplicate code, complexity, module and directory hotspots, architecture, and class design. Each is a thing you would otherwise assemble from separate tools and then correlate by hand.

The output is a single score from 0 to 100 with a letter grade, plus an HTML report ordered by what to fix first. That aggregate is the product's central gamble. A single number is easy to track over time and easy to put on a dashboard, and it is also lossy, because two codebases with the same score can have completely different problems. Where the score earns its keep is the HTML report behind it, so treat the number as a trend line and the report as the actual finding.

The performance claim is a hundred thousand lines per second, built with Go and tree-sitter. A compiled analyzer with an incremental parser is the right architecture for that number.

## The six analyses and what each one is good for

Dead code detection finds unreachable code you can delete. The v1.32.0 release fixed a specific and instructive bug here: a bare yield was being reported even though it is what keeps a function a generator. That is the whole difficulty of static dead code analysis in one line, since the construct that looks dead is often the construct that makes Python do what it is supposed to do.

Duplicate detection covers Type 1 through Type 4 clones, which is the standard four-level scheme: exact copies, renamed identifiers, restructured logic and semantically equivalent but syntactically different code. Type 4 is the one that requires semantic comparison rather than token comparison, and getting it right in a static tool is genuinely difficult.

Complexity is measured two ways, cyclomatic and cognitive. Cyclomatic complexity counts decision points, which is a proxy for test burden. Cognitive complexity attempts to measure how hard code is for a person to read, penalising nesting and structural breaks that cyclomatic complexity scores generously. Both are on functions and on executable class suites.

Hotspots give per-file quality scores and per-directory complexity rollups, which is the view that turns a metric into a work queue. Architecture analysis covers circular imports, layer rule violations with clean, layered, hexagonal and MVC presets, and auto-detected module communities that infer how your code is actually grouped rather than how you say it is grouped. Class design rounds it out with CBO coupling and LCOM4 cohesion, the two metrics from the classic object-oriented metrics set that identify classes which depend on too much or whose parts do not belong together.

One flag is opt-in: DI anti-pattern detection is behind `--select di`, which is sensible since dependency injection smells are a style opinion rather than a defect.

## Installing it and the Intel Mac trap

The fastest way in is running it without installing anything at all.

```bash
uvx pyscn@latest analyze .
```

For persistent use, pipx is the recommended route, with uv as an alternative.

```bash
pipx install pyscn
```

Then there is a platform caveat that will cost you an afternoon if you hit it without warning. PyPI wheels are built for Apple Silicon only. The Intel wheel was dropped in v1.5.1, which means uvx, pipx, uv and pip all fail on an Intel Mac, and the fix is Homebrew or a Go install.

```bash
go install github.com/ludo-technologies/pyscn/cmd/pyscn@latest
```

Building from source is a make target in a Go repository, which is a reminder of what this project actually is.

```bash
git clone https://github.com/ludo-technologies/pyscn.git
cd pyscn
make build
```

The Python packaging is where the dual nature shows most clearly. `pyproject.toml` declares the package with a dynamic version supplied by setuptools_scm, Python 3.8 through 3.13 classifiers, a Production/Stable development status, and the console scripts `pyscn` and `pyscn-mcp`. The runtime dependency list is empty. A Python package with no Python dependencies that analyses Python is a front end over a Go binary, and that is a legitimate and increasingly common shape, though it does mean you are installing a compiled artefact through a Python index.

Configuration lives in `.pyscn.toml` or in a `[tool.pyscn]` table in your pyproject, and `pyscn init` writes a full file with every option.

## Two commands, and which one belongs in CI

The CLI divides into a report command and a gate command, and the distinction is the one to get right.

`pyscn analyze` is the thorough one. It runs the analyses and writes an HTML report, and it takes selectors when you want a subset.

```bash
pyscn analyze .
pyscn analyze --select complexity .
pyscn analyze --skip-communities .
```

JSON output is available for piping, and you can have JSON and HTML from one run, which matters for dashboards.

```bash
pyscn analyze --json --output - . | jq
pyscn analyze --json --html --no-open .
```

`pyscn check` is the CI command, described as a fast pass and fail gate, and it is where the thresholds live.

```bash
pyscn check .
pyscn check --max-complexity 15 .
pyscn check --max-cycles 0 .
```

Setting `--max-cycles 0` is a strict stance, since it fails on any circular dependency at all, which is the right default for a new service and an unreasonable one for a ten-year-old codebase. `--allow-circular-deps` downgrades those to warnings, which is a reasonable migration position.

The full set of selectors is worth knowing, because running everything on every commit is a good way to get a check disabled. `--select deps` limits it to circular dependencies, `--select complexity,deps,deadcode` runs three, and `--select di` adds the opt-in DI checks.

One configuration note that bit the maintainer himself: v1.32.1 fixed a bug where an ancestor `.pyscn.toml` could relocate your project root. If you use pyscn in a monorepo, upgrade to at least 1.32.1 before trusting its output.

## The v1.32.1 release is the interesting one

The most recent release, published the same day as the last push in September 2026, is worth reading in full because nearly every entry is a scoring fix rather than a feature.

Four of them are the same bug in different clothes. The CBO and LCOM4 class design scores were computed against the class population shown in the output rather than the full set of classes. The clone duplication score had the identical problem, measured against the displayed subset of clones rather than all of them. And the community score was reworked so it measures structure instead of sparsity, which is a change of meaning rather than a change of arithmetic. A metric computed over a filtered view is not measuring the codebase, it is measuring the report, and any of these would systematically misreport a project with many classes or many clone families. That they were found and fixed is a good sign about the maintainer, and it is also a warning about what an aggregate score can mean in a tool this young.

The rest of the release is smaller but instructive. Clone grouping now defaults to complete linkage and the config key for it is validated, which means a typo in your TOML fails instead of being ignored. The analyzer now weights Type 4 semantic operation overlap, which is the part of clone detection most likely to produce nonsense without careful tuning. And an ancestor `.pyscn.toml` no longer relocates the project root, which is the kind of bug that produces baffling results rather than errors.

The previous release also fixed a PEP 758 parser issue, unparenthesized except lists with three or more types. That one is a good sign about tree-sitter grammar maintenance, since a Python parser needs to keep up with the language rather than with its own age.

## Agent skills, MCP, and the wider analyser family

The newest surface area is agent integration, and it is the part that matches the README's framing about AI coding. pyscn ships Agent Skills that teach an agent when and how to run each analysis, covering health checks, refactoring, architecture review and CI-friendly reports, installed with a single command.

```bash
uvx add-skills ludo-technologies/pyscn
```

The stated targets are Claude Code, Cursor, Codex, Gemini CLI and others, with flags to pick an agent or install globally. That is the same mechanism used across a family of these projects, and it is a reasonable way to encode analysis procedure as instructions rather than as code an agent has to guess at.

For tighter coupling there is a bundled MCP server, `pyscn-mcp`, exposing the same analyses as tools.

```bash
claude mcp add pyscn-mcp uvx -- pyscn-mcp
```

A Claude Code plugin path installs the server and the skills together, and the manual JSON configuration carries a `PYSCN_CONFIG` environment variable pointing at a `.pyscn.toml`, which is the hook you would use to point several projects at different rule sets.

The commercial layer sits on top of this. Polyscan is the hosted weekly monitoring product, which posts complexity, duplication and dead code changes as a GitHub issue and is free for public repositories, and the documentation moved to docs.codescan.dev. The project also belongs to a broader family: polyscan covers JavaScript and TypeScript with the same model, and the repository topics list architecture analysis, clone detection, complexity, dead code detection and dependency analysis. For a Go project in this space the tree is conventional and well organised, with `cmd/`, `internal/`, `domain/`, `service/`, `app/`, `mcp/`, `integration/`, `e2e/`, `testdata/` and a `website/`, along with a Makefile, golangci configuration and a changelog. The MIT licence, 1,069 stars, 69 forks and eight open issues describe a project with an active single-maintainer rhythm and real adoption.

## Conclusion

pyscn occupies a space most Python linters do not: it does not lint, it audits structure. Dead code, clone families, cyclomatic and cognitive complexity, module hotspots, architectural rules and class coupling are all things a linter usually leaves alone, and having them in one pass with an HTML report is a real service. The Go and tree-sitter implementation explains the speed claim and also the limitation, since a parser that is fast and tolerant is not necessarily as precise as one built on the Python AST, and precision is what dead code analysis needs most. The v1.32.1 release is the most useful thing to read here, because it is a maintainer fixing his own scoring rather than his parsing: CBO, LCOM4 and clone scores were being computed against the subset of classes and clones shown in the output rather than the whole population, and the community score was measuring sparsity instead of structure. A score that is computed on a filtered view is not a score. The advice that follows from that is simple. Run it, read the HTML, and if a number looks wrong, ask which population it was computed over before you refactor a thousand lines to satisfy it. Then pin the version, because the checks move fast and there is no configuration compatibility promise here.

## FAQ

### How do I install pyscn on my machine?

Use pipx for a persistent install, or run it without installing with `uvx pyscn@latest analyze .`. On macOS with Intel, none of the Python installers work: PyPI wheels are Apple Silicon only and the Intel wheel was dropped in v1.5.1, so use `brew install pyscn` or `go install github.com/ludo-technologies/pyscn/cmd/pyscn@latest` instead.

### What does pyscn check that a Python linter does not?

pyscn covers structural concerns rather than correctness and style: unreachable code, Type 1 through Type 4 clone detection, cyclomatic and cognitive complexity, per-file and per-directory hotspots, circular imports and layer rule violations with clean, layered, hexagonal and MVC presets, auto-detected module communities, and class design through CBO coupling and LCOM4 cohesion. It returns a 0 to 100 score with a letter grade and an HTML report.

### Should I run pyscn analyze or pyscn check in CI?

Use `pyscn check` as the CI gate, since it is the fast pass or fail command and takes thresholds like --max-complexity 15 or --max-cycles 0. Use `pyscn analyze` when you want the full HTML or JSON report. Both accept --select to run a subset, for example `--select deps` for circular dependencies only, and `--select di` opts into dependency injection anti-pattern checks.

### Can an AI coding agent run pyscn?

Yes, in two ways. Agent Skills installed with `uvx add-skills ludo-technologies/pyscn` teach Claude Code, Cursor, Codex, Gemini CLI and others when and how to run each analysis. For tighter integration, the bundled `pyscn-mcp` server exposes the same analyses as MCP tools, addable with `claude mcp add pyscn-mcp uvx -- pyscn-mcp`, with a PYSCN_CONFIG environment variable pointing at a .pyscn.toml for per-project rules.

## Sources

- [License: MIT](https://github.com/ludo-technologies/pyscn/blob/main/LICENSE)
- [ludo-technologies/pyscn on GitHub](https://github.com/ludo-technologies/pyscn)
- [Project website](https://docs.codescan.dev/)
- [README](https://github.com/ludo-technologies/pyscn/blob/main/README.md)
- [Releases](https://github.com/ludo-technologies/pyscn/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ludo-technologies-pyscn
