Deterministic codebase intelligence for Python — dead code, duplication, circular dependencies, complexity, architecture, dependency hygiene, type health, and security as evidence, not guesses.
Project description
Deterministic codebase intelligence for Python.
Dead code · duplication · circular dependencies · complexity & hotspots · architecture · dependency hygiene · type health · security — as evidence, not guesses.
Usage · Cookbook · Configuration · Architecture · CI integration · Agent integrations
Mollify is a Rust-native engine that gives humans and AI agents a structured, inspectable map of a Python codebase. It's fallow's model — one fast binary that unifies the whole "what's unused / risky / duplicated / tangled" question — ported to Python and extended with Python-specific signals (type health, notebooks, framework awareness) that fallow doesn't have.
Its one rule: no AI invents findings. Every result is a piece of deterministic evidence with a stable fingerprint, a confidence tier, and a human-readable reason. Mollify produces candidates; you (or your agent) decide what to do with them.
Project status: early but real. The core analysis phases are implemented, tested (100+ tests), and dogfooded; CI is green. See
docs/adr/for design decisions and Engineering notes below for how it works.
Why Mollify
- One tool, eight signals. Most Python shops bolt together vulture + ruff + deptry + tach + radon + jscpd + bandit. Mollify runs the equivalent set in a single deterministic pass with one config and one output contract.
- Built for coding agents. A first-class MCP server plus shipped integrations
for Devin/Cascade, Claude Code, Codex, Cursor, and Gemini CLI — so the
agent reads repo truth instead of reconstructing it from
grep. - Honest about uncertainty. Python dead-code detection is undecidable in
general, so every verdict is tiered
certain / likely / uncertainand onlycertainfindings are ever auto-fixed. Framework decorators (routes, tasks, fixtures, CLI commands, validators) are understood, killing the #1 false positive. - Deterministic & CI-ready. Identical input → byte-identical output. SARIF,
JSON, exit codes, and a PR-scoped
--gate new-only.
What it detects
| Area | Command | Rules |
|---|---|---|
| Dead code | mollify dead-code |
unused-file, unused-export, unused-import, unused-variable, unused-parameter, unused-method, unused-attribute, unused-enum-member, unreachable-code, duplicate-export, commented-code |
| Dependency hygiene | mollify deps |
unused-dependency, missing-dependency, transitive-dependency, misplaced-dev-dependency, unresolved-import (pyproject + requirements/uv/pdm; venv-aware) |
| Architecture | mollify arch |
circular-dependency, layer-violation, forbidden-import, independence-violation, private-import, custom policies |
| Complexity & cohesion | mollify complexity |
high-complexity, hotspot (churn × complexity), low-cohesion (LCOM*) |
| Duplication | mollify dupes |
duplication (clone families) |
| Type health | mollify types |
untyped-function, private-type-leak |
| Security | mollify security |
eval/exec, shell, sql-injection, weak hash/cipher, insecure-random, unsafe deserialization, TLS, secrets, missing-timeout, Flask debug, Jinja2 autoescape, broad except: pass — each with a CWE id |
| Cold paths | mollify coverage --coverage-file |
cold-code (reachable but never executed) |
| Supply chain | mollify supply-chain |
vulnerable-dependency (live OSV; offline DB fallback) |
| Metrics | mollify metrics |
Maintainability Index, Halstead, raw LOC, per-file complexity |
| Everything + score | mollify audit |
all of the above + a 0–100 quality score |
Plus mollify graph [--mermaid] (import-graph export), mollify lsp (editor
diagnostics), and --format github|junit for CI.
Also: Jupyter notebooks (.ipynb) are discovered and analyzed cell-by-cell;
framework awareness (Flask/FastAPI/Django/Celery/pytest/click/Pydantic/…);
architecture presets (layered/hexagonal/feature-sliced/bulletproof) and
declarative rule packs (ban imports/calls per path); mollify fix to safely
remove certain unused symbols; mollify explain <rule> for rule semantics; and
mollify trace <module> for a module's import neighborhood; mollify inspect <file> for a per-file evidence bundle; mollify list for project topology; and
regression baselines (--save-baseline / --baseline --fail-on-regression)
to gate CI on new issues without git.
Install
Python users (recommended) — via uv:
uvx mollify audit # one-off, isolated (no install)
uv tool install mollify # persistent, puts `mollify` on your PATH
uvx mollify@latest audit # pin/refresh to a specific version
Or pip / cargo:
pip install mollify
cargo install mollify-cli # builds from crates.io (binary: mollify)
From source (Rust):
git clone https://github.com/FavioVazquez/mollify
cd mollify
cargo build --release # binary at ./target/release/mollify
Every channel ships the same self-contained binary with the agent
integrations embedded: the PyPI wheel bundles the compiled binary (built with
maturin); the crates.io build embeds the artifacts
from the in-crate assets/. Interactive
human runs print a one-line upgrade hint when a newer version is published;
machine formats, pipes, CI, and non-TTY agent paths never do. Set
MOLLIFY_UPDATE_CHECK=off (or DO_NOT_TRACK=1) to disable it.
Install agent integrations
Scaffold the version-matched skills, rules, hooks, slash-commands, and workflows for your agent straight into a repo (works however mollify was installed):
mollify init --agent claude # or: cursor / gemini / codex / cascade
mollify init --all # every supported agent
mollify init --all --force # overwrite existing files
Quick start
mollify audit --path /your/python/project
Mollify audit — /your/project
Quality score: 84/100
12 finding(s) across 47 file(s) — 0 error, 12 warn
src/app.py:6 [warn/certain] unused-export — function `_legacy` has no reachable references (unused-export:931a82e6)
src/api.py:88 [warn/likely] high-complexity — function `handle` is complex (cyclomatic 14, cognitive 19) (high-complexity:1aa9…)
src/db.py:1 [warn/certain] circular-dependency — import cycle: db → models → db (circular-dependency:7c…)
pyproject.toml:1 [warn/likely] unused-dependency — declared dependency `rich` is never imported (unused-dependency:93…)
Machine-readable + CI:
mollify audit --format json # kind-discriminated contract
mollify audit --format sarif > mollify.sarif # GitHub/GitLab code scanning
mollify audit --gate new-only --base origin/main # only fail on regressions
mollify fix # preview safe removals (--apply to write)
Supply-chain (live OSV by default, offline fallback):
mollify supply-chain # query OSV.dev live for pinned versions
mollify supply-chain --refresh # …and cache results to .mollify/advisories.json
mollify supply-chain --offline # deterministic: local advisory DB only
# `mollify audit` stays offline — it folds in supply-chain from .mollify/advisories.json when present
python3 scripts/fetch-advisories.py .mollify/advisories.json # seed/refresh the DB out-of-band
Confidence tiers
| Tier | Meaning | Auto-fixable |
|---|---|---|
certain |
Provable (e.g. a private unused symbol, no dynamic dispatch in scope) | ✅ |
likely |
Strong static signal, small residual dynamic risk | — |
uncertain |
Public surface, or near getattr/eval/importlib |
— |
The JSON contract
Every command emits a kind-discriminated envelope (schema_version pinned by
agent skills). Clients switch on kind and iterate findings[]:
{
"kind": "audit", "schema_version": "0.1", "quality_score": 84,
"summary": { "total": 12, "errors": 0, "warnings": 12, "files_analyzed": 47 },
"findings": [{
"fingerprint": "unused-export:931a82e6", "rule": "unused-export",
"category": "dead-code", "severity": "warn", "confidence": "certain",
"reason": "function `_legacy` has no reachable references in the project",
"location": { "path": "src/app.py", "line": 6, "end_line": 7 },
"actions": [{ "type": "remove-symbol", "auto_fixable": true,
"suppression_comment": "# mollify: ignore[unused-export]" }]
}]
}
Configuration — .mollifyrc.json
{
"severity": { "dead-code": "error", "duplication": "warn", "unused-dependency": "off" },
"ignore": ["tests/", "migrations/"],
"max_cyclomatic": 10,
"max_cognitive": 15,
"architecture": { "layers": ["api", "service", "domain", "infra"] },
"policies": [
{ "id": "no-requests-in-domain", "forbid_import": "requests", "in_paths": ["domain/"], "severity": "error" }
]
}
Raise rules/categories to error to make CI (and agent hooks) block. Full
reference: docs/configuration.md.
Agent integrations
One MCP server (mollify mcp), many front-ends. Shipped, ready-to-commit artifacts:
| Agent | Artifacts |
|---|---|
| Devin Desktop / Cascade | .devin/skills/mollify/, .devin/rules/mollify.md, .devin/hooks.v1.json + .windsurf/hooks.json, .windsurf/workflows/mollify-*.md |
| Claude Code | .mcp.json, .claude/skills/mollify/, .claude/commands/, .claude/settings.json hooks |
| Codex | AGENTS.md, .codex/config.toml, .agents/skills/mollify/ (portable) |
| Cursor | .cursor/rules/mollify.mdc, .cursor/mcp.json, .cursor/commands/ |
| Gemini CLI | GEMINI.md, .gemini/settings.json, .gemini/commands/mollify/ |
Scaffold any of these into a repo with mollify init --agent <name> (or --all) — see Install agent integrations above.
Architecture
A Cargo workspace; data flows parse → graph → engines → report:
mollify-types (JSON contract) · mollify-parse (Python parsing, ruff AST) ·
mollify-graph (module/symbol graph + reachability + cycles) · mollify-core
(the engines) · mollify-cli (mollify) · mollify-mcp (MCP server) ·
mollify-lsp (Language Server).
See docs/architecture.md.
How it compares
| vulture | ruff | deptry | tach | radon | jscpd | bandit | Mollify | |
|---|---|---|---|---|---|---|---|---|
| Whole-project dead code | ✅ | – | – | – | – | – | – | ✅ (reachability + tiers) |
| Unused class members / enum members | ✅ | – | – | – | – | – | – | ✅ |
| Unreachable code | ✅ | ~ | – | – | – | – | – | ✅ |
| Dependency hygiene (unused/missing/transitive) | – | – | ✅ | – | – | – | – | ✅ |
| Misplaced dev dependency | – | – | ✅ | – | – | – | – | ✅ |
| Unresolved / broken imports | – | ~ | – | – | – | – | – | ✅ |
| Circular deps | – | – | – | ✅ | – | – | – | ✅ |
| Boundaries / interface (private-import) | – | – | – | ✅ | – | – | – | ✅ |
| Complexity | – | ~ | – | – | ✅ | – | – | ✅ |
| Churn × complexity | – | – | – | – | – | – | – | ✅ |
| Duplication | – | – | – | – | – | ✅ | – | ✅ |
| Type health + private-type leaks | – | ~ | – | – | – | – | – | ✅ |
| Security candidates (+CWE) | – | ~ | – | – | – | – | ✅ | ✅ |
| One deterministic pass + agent/MCP contract | – | – | – | – | – | – | – | ✅ |
~ = partial. Mollify's wedge is the unified deterministic pass with one
contract — individual tools each already do a piece well; Mollify unifies them
into a single evidence stream.
Engineering notes
Mollify is built to be precise and dependency-light:
- Full-fidelity parsing. Built on Astral's
ruff_python_parser/ruff_python_ast— the same parser behindruff— pinned to a crates.io release, so every distribution channel builds the identical binary (ADR-0001). - Real scope/binding resolution. Reachability resolves each name load to its binding (LEGB), so shadowing function-locals and attribute accesses never mask a dead top-level symbol.
- Exact duplication. A linear-time SA-IS suffix array + LCP finds exact maximal token clones — no hash-collision guessing, scales to large repos.
- Supply-chain. Matches pinned/locked versions precisely; for declared
ranges it resolves the concrete installed version when a virtualenv is
present, otherwise flags (at
uncertainconfidence) when the range permits a vulnerable version.supply-chainqueries OSV live by default (offline DB fallback);mollify auditstays fully offline and deterministic. - Candidate-producer model. Security findings are syntactic candidates (never claimed as proven vulnerabilities), surfaced with a confidence tier — by design, not a gap.
There are no known correctness limitations; remaining roadmap items are performance optimizations (e.g. Salsa keystroke-incremental reparse for the LSP).
Contributing
See CONTRIBUTING.md. The bar: every change compiles, is tested,
and is documented; the tree stays fmt + clippy -D warnings clean.
License
MIT © 2026 Favio Vázquez
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mollify-0.1.0.tar.gz.
File metadata
- Download URL: mollify-0.1.0.tar.gz
- Upload date:
- Size: 164.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31209e28e093a6e4534abec9090f24cbb589c0a9dac3e52cd911bbd8ec8d89cc
|
|
| MD5 |
65d635e9cd8ae68458e2463d62f14068
|
|
| BLAKE2b-256 |
4bd475065cafb6d8ca1933108f6e43b2a4fdc81f5b9ec3134d48b06047e4bec8
|
File details
Details for the file mollify-0.1.0-py3-none-win_amd64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-win_amd64.whl
- Upload date:
- Size: 3.4 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ffed9a0040276552f81743f6a4b1a6265661aff75c553c747aa11c1df0dd44f
|
|
| MD5 |
d874e92519fafde5ab90d8fa9e5cfff1
|
|
| BLAKE2b-256 |
2a4c2f4b25540eb100e1bb57dd3704a1b9f26c2a74eac86ba89403f2449c049c
|
File details
Details for the file mollify-0.1.0-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 3.6 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d66ea391c719806ced90fbb83f65882692925e5def3195b6584a1c214836c24
|
|
| MD5 |
b86b5eacb3bdab584bebbd3e89aee7cc
|
|
| BLAKE2b-256 |
7c1fceb1b59984fcb0969f2723bf7ad5eaf9b82b596582f9ecb5dcc2b74e4a5d
|
File details
Details for the file mollify-0.1.0-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 3.4 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6eaaf66376ca89f85bafd383b7c78a532578428b128539914d4b66e656e1323e
|
|
| MD5 |
c2994777abf24de85e6d39d19b2049ce
|
|
| BLAKE2b-256 |
18e73a81d5f948344b68228a7cb8c8ea472176decc852702b4c82d2d84e38a8d
|
File details
Details for the file mollify-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.5 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a828ebf9d57bdfc73afdf6a4851bbdc4a1c28f7aa531ea895c2cd3c56a58ad5
|
|
| MD5 |
8ed475e344907d33f44175086a6983ed
|
|
| BLAKE2b-256 |
5b286a3ae4e3cde9e82f8a4cb929a05a26cfad0bb6d0d759d4bdbd0591216b74
|
File details
Details for the file mollify-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.3 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b9710ba4e4ff575ed4a77acb463914af68be33ad538a4444b32d7aaea19d1b2
|
|
| MD5 |
34891e433979941fdc6a65d3db88b71f
|
|
| BLAKE2b-256 |
d63fc83535098bfcd415c8d8e406dbc848b9426ad30aa31ba3b30956d8f271b2
|
File details
Details for the file mollify-0.1.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb5ba6c08e15d73028c052f3f3afa6b8c6b3a2103ed9ec71638dc8912fb8923f
|
|
| MD5 |
c90558ac3fa9888c58bf6d6ed234fede
|
|
| BLAKE2b-256 |
45728a2fa393b93f2cf1d9b8ca3684e16c079d972470dedc3cbc8c92ab474de2
|
File details
Details for the file mollify-0.1.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: mollify-0.1.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.4 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25d09275923ed9a1a42f9800cb4d807fa6f7452a87a67630a9e558d7947912fa
|
|
| MD5 |
86c02b6e7c35d8aa8fd149af90292cd0
|
|
| BLAKE2b-256 |
aa2a158194b19a027a3b1e0b17eec23aa259bb271552c14e63d934b556d946b0
|