Skip to main content

Fast dependency graph analysis and visualization for Python, JavaScript/TypeScript, and Rust projects

Project description

Serpentine

Serpentine

Fast dependency graph analysis and visualization for Python, JavaScript/TypeScript, and Rust projects.

Serpentine UI

Serpentine analyzes your codebase using a Rust-powered parser and displays an interactive dependency graph in your browser. It resolves dependencies down to the variable and call level and watches for file changes in real time.


Features

  • Multi-language: Python, JavaScript, TypeScript, and Rust
  • Deep resolution: Resolves calls and type references, not just import edges
  • Interactive graph: Expandable nodes, search, pan/zoom, collapsible modules
  • Real-time updates: File watcher pushes changes via WebSocket
  • Agent-ready CLI: Structured JSON output with selectors for use in AI agent workflows

Installation

PyPI (recommended)

pip install serpentine-parser

Or with uv:

uv tool install serpentine-parser

From source

Prerequisites: Python 3.12+, Rust toolchain, Node.js 18+

git clone https://github.com/serpentine-parser/serpentine.git
cd serpentine

# 1. Build the frontend
cd frontend && npm install && npm run build && cd ..

# 2. Build the Rust extension and install the CLI
make install

This installs the serpentine CLI globally via uv tool, which you will need to install separately.


Quick Start

Navigate to any Python, JavaScript/TypeScript, or Rust project and run:

serpentine serve

Serpentine will analyze the project, start a local server at http://127.0.0.1:8765, and open the visualization in your browser. File changes are detected automatically and the graph updates in real time.


CLI Reference

serpentine serve

Start the visualization server with live file watching.

serpentine serve [PATH] [OPTIONS]

Arguments:
  PATH    Directory to analyze (default: current directory)

Options:
  -p, --port INT        Port to run the server on (default: 8765)
  -h, --host TEXT       Host to bind to (default: 127.0.0.1)
  --no-browser          Don't open browser automatically
  --no-watch            Disable file watching (static analysis only)

serpentine analyze

Analyze a project and output the full dependency graph as JSON.

serpentine analyze [PATH] [OPTIONS]

Options:
  -o, --output PATH           Write output to file instead of stdout
  --pretty                    Pretty-print JSON
  --select TEXT               Selector expression to filter nodes (see below)
  --exclude TEXT              Exclusion pattern (same syntax as --select)
  --no-cfg                    Strip control-flow graph data from nodes
  --edges-only                Output only the edges array (compact)
  --include-standard          Include stdlib nodes (default: off)
  --include-third-party       Include third-party nodes (default: off)
  --state TEXT                Filter by change state: modified,added,deleted

serpentine catalog

Flat list of all nodes — useful for discovering node IDs before building selectors.

serpentine catalog [PATH] [OPTIONS]

Options:
  --filter TEXT               Glob pattern matched against node id and name
                              (repeatable; multiple patterns = union)
  --no-assignments            Exclude variable/assignment nodes
  -o, --output PATH           Write output to file instead of stdout
  --pretty                    Pretty-print JSON
  --include-standard          Include stdlib nodes
  --include-third-party       Include third-party nodes

serpentine stats

Quick summary of project scale: node/edge counts by type and origin.

serpentine stats [PATH] [OPTIONS]

Options:
  --pretty                    Pretty-print JSON
  --include-standard          Include stdlib nodes
  --include-third-party       Include third-party nodes

Querying the Graph

serpentine analyze and serpentine catalog accept --select and --exclude to slice the graph down to just the nodes you care about. This is useful for large codebases where the full graph is too noisy, or for piping results into other tools.

Step 1 — Find your node IDs

Every node has a dotted ID that reflects its location in the codebase. Use catalog to discover them:

serpentine catalog . --no-assignments --pretty

This outputs a flat list of every module, class, and function with its ID:

{
  "nodes": [
    { "id": "src.auth.models.User", "name": "User", "type": "class", ... },
    { "id": "src.auth.views.login", "name": "login", "type": "function", ... },
    { "id": "src.payments.stripe.charge", "name": "charge", "type": "function", ... }
  ]
}

Narrow it down with --filter (glob matched against both ID and name):

# Find everything related to auth
serpentine catalog . --filter "*auth*" --no-assignments --pretty

# Find by name across modules
serpentine catalog . --filter "*User*" --no-assignments --pretty

Step 2 — Select nodes and their dependencies

Once you have IDs, use --select with serpentine analyze. The selector controls which nodes (and their relationships) appear in the output.

Plain pattern — just the matching nodes:

serpentine analyze --select "src.auth.*" --no-cfg --pretty

+pattern — the matching nodes plus everything they depend on (upstream):

# What does the login view need to work?
serpentine analyze --select "+src.auth.views.login" --no-cfg --pretty

pattern+ — the matching nodes plus everything that depends on them (downstream):

# What breaks if I change the User model?
serpentine analyze --select "src.auth.models.User+" --no-cfg --pretty

+pattern+ — both directions (full blast radius):

serpentine analyze --select "+src.payments.*+" --no-cfg --pretty

@pattern — the full connected component (everything reachable in any direction):

serpentine analyze --select "@src.auth.*" --no-cfg --pretty

Bounded hops — limit how many levels to traverse:

# 2 levels of dependencies upstream, 1 level downstream
serpentine analyze --select "2+src.auth.views.login+1" --no-cfg --pretty

Multiple selectors — comma-separated, combined as a union:

serpentine analyze --select "+src.auth.*,+src.payments.*" --no-cfg --pretty

Step 3 — Exclude noise

--exclude removes nodes from the result (including their descendants):

# Show auth and its deps, but skip test files
serpentine analyze --select "+src.auth.*" --exclude "*test*" --no-cfg --pretty

Glob pattern rules

* in a selector matches any characters including dots, so it crosses module boundaries:

You want Use
All nodes whose ID contains "auth" *auth*
All children of a specific module src.auth.*
A class anywhere in the project *.User
All test files *test*

Tip: ** is equivalent to * — both match across dots.

Compact output for large graphs

Use --edges-only to get just the edge list (much smaller than the full node tree):

serpentine analyze --select "+src.auth.*+" --edges-only --pretty

Each edge has a from and to field with node IDs, and a type field (calls, is-a, or has-a).


Configuration

Serpentine looks for .serpentine.yml or serpentine.yml in the project root. All settings are optional.

analysis:
  # File extensions to analyze (default: all supported)
  extensions: [".py", ".js", ".jsx", ".ts", ".tsx", ".rs"]

  # Directories to skip
  exclude_dirs:
    - node_modules
    - .venv
    - dist
    - build

  # Glob patterns for files to skip
  exclude_patterns:
    - "**/*.test.ts"
    - "**/generated/**"

Using Serpentine with AI Agents

Serpentine's CLI is designed to be consumed by AI coding agents (Claude, Cursor, Copilot, etc.). The structured JSON output, selector syntax, and --edges-only / --no-assignments flags exist specifically to give agents precise, low-noise subgraphs without requiring file reads.

The problem it solves

When an agent starts a non-trivial task, it faces a cold-start problem: it doesn't know which files are relevant, how components relate, or what the blast radius of a change might be. The usual approach — grep, glob, read files one at a time — is expensive in tokens and often incomplete.

Serpentine collapses that exploration into 2-3 CLI calls. The agent gets a structural picture of the relevant code before it starts reading files or writing a plan. In practice this means:

  • Plans are scoped to the right files and boundaries from the start
  • Less back-and-forth between reading files to trace call chains
  • Fewer surprises mid-implementation when a dependency was missed

Recommended workflow for agents

# 1. Get the lay of the land — module names, rough scale
serpentine stats .

# 2. Find relevant node IDs by name
serpentine catalog . --filter "*auth*" --no-assignments --pretty

# 3. Get the subgraph for the relevant area
serpentine analyze . --select "+src.auth.*+" --no-cfg --edges-only --pretty

Read the edges to understand what connects to what, then read only the files that are actually relevant.

Scenarios where this pays off

Before refactoring a module Use --select "module+" to map everything that depends on the module before writing a plan. This ensures the plan accounts for every callsite and doesn't discover new dependents halfway through.

Before adding a feature that spans modules Use --select "+moduleA.*,+moduleB.*" to get the combined subgraph of two areas and understand exactly where they intersect. The boundaries of the work become clear before any code is written.

Orienting to an unfamiliar codebase serpentine stats gives you the top-level module list and scale. serpentine catalog . --no-assignments gives a flat index of every class and function. Together these give structural orientation without reading a single file.

Before deleting code Use --select "*.TargetFunction+" to get all downstream dependents. An empty result confirms the code is truly unused.

Tracing a call chain Use --select "+*.entrypoint+3" to get 3 hops downstream from an entry point and understand the execution path before adding instrumentation or fixing a bug.

Claude Code skill

This repository includes a Claude Code skill at .claude/commands/serpentine.md. If you're using Claude Code in a project analyzed by Serpentine, you can copy this file into your project's .claude/commands/ directory. It instructs Claude to use Serpentine as its first step before reading files or grepping — running stats, then catalog, then analyze in sequence before answering structural questions or planning changes.


Development

Project Structure

serpentine/
├── rust/                       # Rust analyzer (tree-sitter + PyO3)
│   └── src/
│       ├── lib.rs              # PyO3 module entry point
│       ├── python/             # Python parser
│       ├── javascript/         # JS/TS parser
│       ├── rust_lang/          # Rust parser
│       ├── subscribers/        # Event processors (imports, calls, defs)
│       └── graph/              # Graph builder and resolvers
├── src/serpentine/             # Python package
│   ├── cli.py                  # CLI commands
│   ├── state.py                # Graph state management
│   ├── watcher.py              # File watcher
│   ├── selector.py             # Graph selector engine
│   ├── config.py               # Config loading
│   └── server/                 # Starlette web server + WebSocket
├── frontend/                   # React frontend (Vite + TypeScript)
│   └── src/
├── Makefile                    # Build targets
└── pyproject.toml

Building for Development

Tilt is recommended for development — it rebuilds the Rust extension and frontend in parallel when files change:

tilt up

Or manually:

# Build Rust extension (rerun after changes to rust/src/)
uv run maturin develop

# Build frontend (rerun after changes to frontend/src/)
cd frontend && npm run build

# Run server
uv run serpentine serve --no-browser

Adding a Language

  1. Create a parser module in rust/src/<language>/
  2. Emit ImportStatement, UseName, and CallExpression events via the message bus
  3. Register the parser in rust/src/lib.rs

See rust/src/python/ for a complete reference implementation and CONTRIBUTING.md for full details.


License

Apache 2.0

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

serpentine_parser-0.1.3.tar.gz (1.6 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

serpentine_parser-0.1.3-cp312-abi3-win_amd64.whl (2.9 MB view details)

Uploaded CPython 3.12+Windows x86-64

serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.1 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ x86-64

serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.1 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ ARM64

serpentine_parser-0.1.3-cp312-abi3-macosx_11_0_arm64.whl (3.0 MB view details)

Uploaded CPython 3.12+macOS 11.0+ ARM64

serpentine_parser-0.1.3-cp312-abi3-macosx_10_12_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.12+macOS 10.12+ x86-64

File details

Details for the file serpentine_parser-0.1.3.tar.gz.

File metadata

  • Download URL: serpentine_parser-0.1.3.tar.gz
  • Upload date:
  • Size: 1.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for serpentine_parser-0.1.3.tar.gz
Algorithm Hash digest
SHA256 5914d44ea6323794a7710f66b7e45ba31ba6f6fd2c3e38d5c2af0e7424781de9
MD5 22da305860f2077d70e0f9cff82a8166
BLAKE2b-256 b673b8c9d641e2cea9df345b5d5f857402b6e1d48716ac44a620cbca079912eb

See more details on using hashes here.

Provenance

The following attestation bundles were made for serpentine_parser-0.1.3.tar.gz:

Publisher: release.yml on serpentine-parser/serpentine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file serpentine_parser-0.1.3-cp312-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for serpentine_parser-0.1.3-cp312-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 6c24ed06e6622f35575816dce649a7c3fea2793febe27e20ce84070ba682db40
MD5 96a72ba9aa81594e4ed12384c92faef8
BLAKE2b-256 555d33547690aa21e14350da9ddb79f9d67eda1126e1e0ff65be6d934c3f1fb9

See more details on using hashes here.

Provenance

The following attestation bundles were made for serpentine_parser-0.1.3-cp312-abi3-win_amd64.whl:

Publisher: release.yml on serpentine-parser/serpentine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0fe1ef974bb4c344fecbe9a9e246aade716074e59252504400d77d6ccf23b659
MD5 071ce1a3618f9bce4f08a4338eb80c37
BLAKE2b-256 396df2ffc3f1c6231b9aa5782adae5182c08d417b9388b6fab75f4b1de7ef9af

See more details on using hashes here.

Provenance

The following attestation bundles were made for serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on serpentine-parser/serpentine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 6369fe0d5795c6e31b1e487918e292054c0a03f6274d19125bb5feed04be3643
MD5 c29544a3a27dfbabc2fe3af81460a68c
BLAKE2b-256 bc48c0b8dcaa02a40d2d2bce804af9ba7333c1583b12383695e71ea770049297

See more details on using hashes here.

Provenance

The following attestation bundles were made for serpentine_parser-0.1.3-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on serpentine-parser/serpentine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file serpentine_parser-0.1.3-cp312-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for serpentine_parser-0.1.3-cp312-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 acf41cfa77dcc586a5f9536d10895b2a6549fa03e20f07fe8ac30d650ccd82a4
MD5 31447f939d11bbaff834fda03af7d0dd
BLAKE2b-256 3d268fa458a577902076c7210c54c0d6bdd47d190093b6fcb6f57db9aa3405de

See more details on using hashes here.

Provenance

The following attestation bundles were made for serpentine_parser-0.1.3-cp312-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on serpentine-parser/serpentine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file serpentine_parser-0.1.3-cp312-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for serpentine_parser-0.1.3-cp312-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 74c10fa4cbfae8cd3e9eeda3250ef4588fcfbb8f14e96c9152e19cc71fa66400
MD5 e4e5e6e26d1285531ffa1d3d06508809
BLAKE2b-256 a16e776eec932c616821b4afae0789aa54ba9d9ef71e5387319b69517097ef99

See more details on using hashes here.

Provenance

The following attestation bundles were made for serpentine_parser-0.1.3-cp312-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on serpentine-parser/serpentine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page