Skip to main content

Multi-language codebase dependency graph analyzer and dead-code detector.

Project description

Orbits

Orbits analyzes a source tree, resolves project-local dependencies, writes a graph.json, and serves a bundled visualizer for exploring the dependency graph.

The current stack is:

  • Python backend analyzer
  • D3 + canvas visualizer
  • browser-side worker analysis for folder loading in supported browsers
  • local HTTP serving through analyzer.py --serve
  • first-class HTML/CSS/static-asset graph extraction for web projects
  • optional Python and Node.js runtime tracing with merged dynamic-edge overlays

Quick Start

From the repo root:

  1. Create and activate a local venv.
  2. Install frontend dependencies once.
  3. Run the analyzer with --serve.

Windows PowerShell

python -m venv .venv
.\.venv\Scripts\Activate.ps1
npm install
python analyzer.py . --serve

Then open the URL printed by the server, typically:

http://127.0.0.1:8765/visualizer.html

If You Only Want a Graph File

python analyzer.py .

That writes graph.json in the repo root by default.

If You Already Have a Venv

.\.venv\Scripts\python.exe analyzer.py . --serve

Installable CLI

Orbits can be installed as a normal Python CLI:

python -m pip install -e .
orbits . -o graph.json
orbits . --serve
orbits --diff C:/temp/old-graph.json C:/temp/new-graph.json
python -m orbits . --serve

The console script maps to the existing analyzer:main entry point, and the packaged wheel includes the visualizer assets used by --serve. The legacy python analyzer.py ... form still works from a source checkout.

What It Does

  • Crawls a project tree while skipping common noise
  • Extracts imports/includes for multiple languages
  • Extracts HTML links/scripts/forms/media references and CSS @import / url(...) assets
  • Resolves project-local dependencies
  • Computes cycles, islands, orphans, depth, health, and summary stats
  • Detects common project entrypoints so launch files are not reported as dead just because nothing imports them
  • Serves an interactive visualizer for the generated graph

Entrypoint Detection

Orbits marks detected launch files as ENTRY even when they have no outbound imports. Detection currently uses package.json fields/scripts, Python project script metadata, setup.cfg / setup.py console scripts, Dockerfile CMD / ENTRYPOINT hints, common Makefile run targets, and conventional names such as main.py, app.py, server.py, manage.py, index.js / index.ts, and Go main.go.

Detected entries are stored in meta.entrypoints, and each matching node gets entrypoint: true plus entrypoint_reasons for future UI/report use.

Supported Languages

First-class extraction and resolution:

  • Python
  • JavaScript / TypeScript / TSX
  • Go
  • C / C++
  • Java
  • Kotlin
  • HTML
  • CSS / SCSS / Sass / Less
  • referenced static assets such as images, fonts, JSON, PDFs, media, and Wasm

Fallback:

  • Generic regex-based extraction for unsupported or unknown languages

Pipeline

  1. Crawl the project tree.
  2. Extract raw import/include statements.
  3. Resolve local edges against project files and referenced web assets.
  4. Build graph metadata and summary metrics.
  5. Optionally trace a Python or Node.js entrypoint at runtime and write runtime_trace.json.
  6. Merge dynamic runtime edges into the served/exported graph overlay.
  7. Write graph.json.
  8. Optionally serve the visualizer and graph assets.

Current Frontend Architecture

The active visualizer is not Cytoscape.

It currently uses:

  • D3 for zoom, motion, and interaction
  • canvas for graph rendering
  • DOM panels for controls, inspector, waste, cycles, and search
  • visualizer_worker.js for browser-side folder analysis

This preserves the older 3.5f visual feel while keeping the later browser-side workflow and performance work.

Phase 4 Status

Implemented in the current UI:

  • Cluster view
  • Filter panel
  • File sidebar / inspector
  • Waste panel
  • Cycles panel
  • Search with dependency-tree highlighting
  • Minimap
  • Language multi-select
  • Unsupported-language warning banner
  • Folder loading via File System Access API in Chromium-based browsers
  • Browser-side worker analysis
  • Large-graph performance modes and auto-degradation

Not implemented as originally claimed in older docs:

  • Cytoscape.js / react-force-graph as the active renderer

Visual Baselines

Stage 2 visual regression is opt-in and does not run in the default test:e2e path.

Generate or refresh baselines:

npm run test:e2e:visual:update

Compare against existing baselines:

npm run test:e2e:visual

Baseline screenshots live in e2e/visual.spec.ts-snapshots/ and are committed once approved. The dedicated Playwright screenshot script at scripts/visual-baselines.mjs keeps visual regression opt-in and out of the default test:e2e run.

Phase 5 Status

Implemented now:

  • Python runtime tracing in a separate subprocess
  • Node.js runtime tracing in a separate subprocess
  • scoped C / C++ runtime tracing for local native binaries and libraries
  • separate runtime_trace.json artifact
  • merged runtime edge overlay in graph.json via dynamic_edges
  • multi-session runtime metadata via meta.runtime.sessions[]
  • runtime-aware view menu edge modes: static, runtime, combined
  • runtime edge styling in the D3 + canvas visualizer
  • stale runtime overlay preservation across static reanalysis paths

Current scope and honest boundary:

  • runtime tracing is shipped for Python, Node.js, and scoped C / C++ overlays today
  • multiple runtime artifacts can be merged into one graph as separate runtime sessions
  • static graph metrics like cycles, depth, waste, and health remain static-analysis-based
  • runtime edges are an overlay, not a replacement for the static graph
  • reanalysis preserves prior runtime overlay, but marks it stale after source-changing actions until you retrace
  • Node traces are best for .js / .cjs / .mjs entrypoints; direct TypeScript runtime execution is still not claimed
  • runtime-to-static remapping for transpiled dist/*.js to src/*.ts now uses source maps, inline maps, custom sourceMappingURL files, indexed source-map sections, source roots, and common bundler path forms when available, but is still not compiler-perfect
  • C / C++ tracing is still intentionally scoped: Linux captures loader edges plus local symbol bindings, macOS stays loader-oriented, and Windows captures local PE import-table DLL dependencies
  • static C / C++ analysis also recognizes literal dlopen(...), LoadLibrary(...), and LoadLibraryEx(...) calls when the referenced local library exists

Performance Reality

The visualizer is now much safer on large graphs than the old SVG version, but this is the honest boundary:

  • 500+ files is a reasonable target
  • large graphs degrade by reducing labels, limiting visible nodes/edges, sampling the minimap, and disabling live motion when needed
  • very dense multi-thousand-node graphs can still be heavy depending on browser and machine
  • this is engineered to degrade gracefully, not a promise that every arbitrarily huge graph can never slow down

Performance Modes

The visualizer has three modes in the view menu:

  • perf auto: chooses safer defaults automatically for large graphs
  • perf full: favors richer motion and higher draw limits
  • perf safe: favors stability and stricter draw limits

On very large graphs, Orbits may automatically start with:

  • cluster layout
  • labels off
  • full graph off

Browser Features

The visualizer supports two data sources:

  1. Backend-generated graph.json
  2. Browser-side folder analysis from the folder button

Browser folder analysis:

  • uses a Web Worker so analysis does not block the UI thread
  • requires a Chromium-based browser for showDirectoryPicker()
  • produces the same top-level graph shape as the backend visualizer expects
  • is heuristic and not guaranteed to match backend analysis exactly on every repo

Runtime Requirements

Python

Recommended: use the workspace venv.

The repo expects local Python dependencies in .venv/. Optional parser support depends on installed tree-sitter language packages. If grammars are missing, Orbits reports unsupported languages in CLI output and graph metadata instead of silently pretending those files had no imports.

Node runtime tracing uses the same overlay contract as Python:

python analyzer.py /path/to/project --trace-node app.js
python analyzer.py /path/to/project --trace-node-module myapp.cli --trace-arg=--mode --trace-arg=dev

It writes a separate runtime artifact and merges into dynamic_edges exactly like Python.

You can also merge existing runtime artifacts back into a fresh static analysis:

python analyzer.py /path/to/project --runtime-input C:/tmp/python_runtime.json --runtime-input C:/tmp/node_runtime.json

Scoped native tracing is also available for local C / C++ binaries on supported platforms:

python analyzer.py /path/to/project --trace-cpp build/my_binary

On Linux this captures local loader edges and symbol bindings via loader diagnostics. On macOS it captures local loader edges. On Windows it executes the entry binary normally and adds scoped local DLL dependency edges from the executable's PE import table. Windows tracing is loader/dependency oriented; it does not claim deep native tracing, syscall tracing, or universal LoadLibrary discovery. Static C / C++ extraction also adds graph edges for literal local dlopen(...), LoadLibrary(...), and LoadLibraryEx(...) library paths when the target file exists in the repo.

Frontend

Install frontend dependencies once:

npm install

The active frontend uses D3. Cytoscape is not the active renderer path.

Usage

Analyze a project:

python analyzer.py /path/to/project

Analyze and serve the visualizer:

python analyzer.py /path/to/project --serve

Trace a Python script at runtime and merge dynamic edges:

python analyzer.py /path/to/project --trace-python app.py --serve

Trace a Python module with arguments:

python analyzer.py /path/to/project --trace-module myapp.cli --trace-arg=--mode --trace-arg=dev

Trace a Node.js script or module with the same argument style:

python analyzer.py /path/to/project --trace-node app.js
python analyzer.py /path/to/project --trace-node-module myapp.cli --trace-arg=--mode --trace-arg=dev

Write the runtime artifact somewhere else:

python analyzer.py /path/to/project --trace-python app.py --runtime-output C:/temp/runtime_trace.json

Merge one or more existing runtime artifacts into a fresh graph:

python analyzer.py /path/to/project --runtime-input C:/temp/python_runtime.json --runtime-input C:/temp/node_runtime.json

Write output somewhere else and still serve correctly:

python analyzer.py /path/to/project -o C:/temp/my-graph.json --serve

Compare two graph snapshots:

python analyzer.py --diff C:/temp/old-graph.json C:/temp/new-graph.json
python analyzer.py --diff C:/temp/old-graph.json C:/temp/new-graph.json --diff-json

The diff reports added and removed nodes, added and removed dependency edges, and waste count changes. Edge comparison uses source -> target, so line-number-only import churn does not count as a dependency change.

Demo Evidence

The examples/ directory contains lightweight, deterministic material for a judge or reviewer:

  • examples/orbits-demo.png: screenshot of the visualizer
  • examples/orbits.config.example.json: sample .orbits.json config shape
  • examples/README.md: install, analyze, check, reports, diff, and runtime-boundary command transcript
  • examples/fixtures/*.json: tiny graph snapshots for orbits --diff

Orbits demo screenshot

Config, Reports, and Check Mode

Orbits reads optional project-root config from codegraph.config.json and .orbits.json. If both exist, codegraph.config.json is loaded first and .orbits.json can extend or override it.

{
  "ignore": {
    "dirs": ["legacy/**", "generated/**"],
    "files": ["*.snapshot.py", "fixtures/*.js"]
  },
  "intentional_files": ["scripts/manual_migration.py"],
  "check": {
    "max_orphans": 0,
    "max_islands": 0,
    "min_health": 85
  },
  "resolvers": {
    "python": {
      "src_dirs": ["src"],
      "third_party": ["requests"]
    },
    "javascript": {
      "base_url": "src",
      "aliases": {
        "@/*": "src/*"
      }
    },
    "c_family": {
      "include_dirs": ["include"]
    },
    "jvm": {
      "src_roots": ["src/main/java", "src/main/kotlin"]
    }
  }
}

Write explicit dead-file reports:

python analyzer.py /path/to/project --dead-report-md dead-files.md --dead-report-csv dead-files.csv

When the analyzed root is inside a Git worktree, each dead-file item is also annotated with cheap history context: last touch age/timestamp, commit count, line churn, top authors, and a deterministic confidence score with reasons. Runtime overlays are folded into that score when present, so a fresh runtime touch lowers confidence while an untouched fresh trace raises it.

Run a deterministic CI-style check:

python analyzer.py /path/to/project --check
python analyzer.py /path/to/project --check --max-orphans 0 --max-islands 1 --min-health 90

GitHub Actions and Thresholds

The included Orbits workflow runs the same CLI check on pushes and pull requests:

python analyzer.py . -o orbits-artifacts/graph.json --dead-report-md orbits-artifacts/dead-files.md --dead-report-csv orbits-artifacts/dead-files.csv --check

It uploads graph.json, dead-files.md, dead-files.csv, check logs, and the generated PR comment body as the orbits-report artifact. On pull requests, it also tries to build a base-branch graph diff and update a sticky PR comment. Comment posting is best-effort; the check artifacts are still produced when the token cannot write comments.

Configure thresholds in codegraph.config.json or .orbits.json:

{
  "check": {
    "max_orphans": 0,
    "max_islands": 1,
    "min_health": 90
  }
}

For repository-specific CI overrides without changing config files, set a GitHub Actions repository variable named ORBITS_CHECK_ARGS, for example:

--max-orphans 0 --max-islands 1 --min-health 90

--check exits with code 2 when a configured or flag-provided threshold is exceeded. Orphan and island thresholds use actionable dead files after intentional_files suppressions. Health uses the graph summary score.

PyPI Release Readiness

The repository includes .github/workflows/publish.yml for Python packaging:

  • workflow_dispatch builds the source distribution and wheel, then runs twine check
  • GitHub Release publishing uploads to PyPI through Trusted Publishing
  • the PyPI project target is orbits-codebase, which installs the orbits CLI command

Before the first real release, configure a PyPI Trusted Publisher for this GitHub repository and the pypi environment. No API token is required when Trusted Publishing is configured correctly.

Load a graph directly in the browser UI:

  • open the served visualizer
  • use OPEN GRAPH FILE
  • or drag and drop a graph.json

If runtime data is present, the view menu lets you switch between:

  • static edges only
  • runtime edges only
  • combined static + runtime edges

Behavior Guarantees

  • Analysis does not edit the target repository's .gitignore
  • Cache writes stay in Orbits-owned files
  • --serve does not depend on changing the process working directory
  • Missing parser support is surfaced in metadata and UI/CLI messaging
  • Runtime tracing writes to a separate artifact instead of mutating static cache files
  • Source-changing reanalysis preserves runtime overlay but marks it stale until you rerun tracing
  • Multiple runtime traces are preserved as individual sessions and also aggregated into one runtime overlay

Visualizer Features

Inspector shows:

  • file path
  • classification
  • inbound/outbound references
  • runtime edge markers (dyn or rt) when present
  • depth
  • island
  • cycle membership
  • modified time
  • git blame summary when available

Waste panel supports:

  • open
  • keep to mark a file as intentional waste
  • del

Intentional suppressions are stored in:

  • .orbits_intentional.json
  • intentional_files in codegraph.config.json or .orbits.json

Key Files

  • analyzer.py: CLI entry point, HTTP serving, file actions, metadata APIs
  • runtime_trace.py: Python + Node runtime trace orchestration, artifact writer, runtime merge helpers
  • node_runtime_trace.cjs: Node.js runtime tracer child process and artifact writer
  • entrypoints.py: manifest and conventional entrypoint detection
  • lang_dispatch.py: crawl orchestration, worker dispatch, language support metadata
  • worker.py: per-language extraction and resolution execution
  • extractors/: tree-sitter, web, and fallback extractors
  • resolvers/: language-specific resolution logic
  • orbits_assets/: packaged visualizer assets for installed CLI serving
  • graph_engine.py: enrichment, waste detection, summary metrics
  • graph_diff.py: graph snapshot comparison helper and standalone diff CLI
  • visualizer.html: bundled shell/UI
  • visualizer_app.js: active D3 + canvas visualizer logic
  • visualizer_worker.js: browser-side worker analysis and layout
  • benchmark_graph.py: deterministic large-graph benchmark fixture generator

Benchmarking

Generate a large synthetic graph fixture:

python benchmark_graph.py --nodes 1200 --seed 7 --output large_graph.json

This is useful for testing:

  • render stability
  • minimap behavior
  • perf mode changes
  • large-graph regressions

Verification

The repo currently includes regression coverage for:

  • non-mutating analysis behavior
  • serving behavior
  • unsupported parser metadata
  • Python import-from resolution
  • Python runtime trace capture and merge behavior
  • Node runtime trace capture and merge behavior
  • multi-session runtime merge behavior and Node source-map remapping
  • TypeScript alias resolution
  • Go module resolution
  • C / C++ include resolution
  • Java and Kotlin package resolution
  • HTML/CSS/static asset graph extraction
  • end-to-end graph shape
  • graph dependency diff summaries
  • installable CLI metadata and packaged visualizer assets
  • synthetic benchmark graph generation
  • Playwright Stage 1 browser coverage for the visualizer, including runtime edge mode switching

Run backend tests:

python -m unittest discover -s tests -v

Run browser tests:

npm run test:e2e -- --reporter=line

Limitations

  • Dynamic imports, reflection, generated code, and macro-heavy systems remain hard limits for static analysis alone
  • Python runtime tracing only sees code paths that actually execute
  • Python runtime tracing is time-bounded; timed-out sessions produce partial traces
  • Node runtime tracing is shipped as a backend tracer for JavaScript entrypoints
  • HTML/CSS analysis resolves static references, but it does not execute browser JavaScript or infer DOM nodes created at runtime
  • Browser-side worker analysis is not guaranteed to match backend analysis exactly
  • Large graphs are handled more safely now, but browser and machine limits still matter
  • Git blame and file actions depend on local environment support and repository state
  • Runtime edges are an overlay today; they do not currently rewrite static health, cycle, depth, or waste calculations

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

orbits_codebase-0.1.0.tar.gz (134.4 kB view details)

Uploaded Source

Built Distribution

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

orbits_codebase-0.1.0-py3-none-any.whl (126.2 kB view details)

Uploaded Python 3

File details

Details for the file orbits_codebase-0.1.0.tar.gz.

File metadata

  • Download URL: orbits_codebase-0.1.0.tar.gz
  • Upload date:
  • Size: 134.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for orbits_codebase-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7fc11090794f48a2d38bccb88928e3f4fc28de48da9f9916786c71f938c7b9c1
MD5 ee20ad9a3b641b5f2dad3dd04d500fef
BLAKE2b-256 8b64406cf81e5997a4bc68a0381a3d3ee2bdf5b4a7b300e74a847b98b8252db5

See more details on using hashes here.

Provenance

The following attestation bundles were made for orbits_codebase-0.1.0.tar.gz:

Publisher: publish.yml on Yumekaz/Orbits

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

File details

Details for the file orbits_codebase-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: orbits_codebase-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 126.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for orbits_codebase-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 12e647cc09dd96bb0f79d5311ae7cfc8668abfcf1a00f138800d540ffc4f508b
MD5 8307bfa7bbb29a3f695b661ee8491ee6
BLAKE2b-256 c3973dfb05bc92cba059c0d42e5b77e3a2296e2d82279995c27de4bfc7bbfb60

See more details on using hashes here.

Provenance

The following attestation bundles were made for orbits_codebase-0.1.0-py3-none-any.whl:

Publisher: publish.yml on Yumekaz/Orbits

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