A fast, cross-language phantom dependency detector. Finds packages you import but forgot to declare, and packages you declared but never use.
Built in Rust. Zero runtime dependencies. Single binary.
Ever had a build break in CI because someone imported a package that was only available transitively? Or shipped a bloated package.json full of deps nobody uses anymore?
ghostdep catches both:
- Phantom dependencies — imported in code but missing from your manifest
- Unused dependencies — declared in your manifest but never imported
| Language | Manifest | Parser | Status |
|---|---|---|---|
| Go | go.mod |
tree-sitter | stable |
| JavaScript/TypeScript | package.json |
OXC | stable |
| Python | requirements.txt, pyproject.toml (PEP 621 + Poetry + uv) |
tree-sitter | stable |
| Rust | Cargo.toml |
tree-sitter | stable |
| Java | pom.xml, build.gradle |
tree-sitter + regex | beta |
Note on Java: Java import-to-artifact mapping is inherently heuristic — there's no standard 1:1 mapping between
importpaths and Maven artifact IDs. ghostdep ships with a table of ~150 common libraries and falls back to groupId matching against your pom.xml/build.gradle. It works well for popular libraries but may miss niche ones. PRs to expand the mapping table are very welcome.
# install from source (crates.io publish coming soon)
cargo install --path .
# or grab a binary from releases
curl -fsSL https://raw.githubusercontent.com/ojuschugh1/ghostdep/main/install.sh | sh
# scan your project
ghostdep
# scan a specific directory
ghostdep -p /path/to/project
# json output for CI
ghostdep -f json
# sarif output for github code scanning
ghostdep -f sarifghostdep [OPTIONS] [COMMAND]
Commands:
scan Scan for phantom and unused dependencies (default)
init Generate a default .ghostdep.yaml config
fix Generate fix commands for findings
Options:
-p, --path <PATH> Project root [default: .]
-f, --format <FORMAT> Output format: text, json, sarif
--min-confidence <LEVEL> Filter by confidence: low, medium, high
-i, --ignore <PATTERN> Ignore deps matching glob pattern
-l, --language <LANG> Restrict to a specific language
--dev Include dev dependencies
--threads <N> Number of scanner threads
--cache Enable incremental scan cache
-v, --verbose Print progress info
-q, --quiet Suppress output (exit code only)
-h, --help Print help
-V, --version Print version
$ ghostdep -p my-project
[phantom] axios at src/api.js:3 (confidence: high)
[phantom] pandas at app.py:7 (confidence: high)
[unused] lodash at package.json (confidence: high)
[unused] numpy at requirements.txt (confidence: high)
Found 2 phantom and 2 unused dependencies (42 files scanned in 15ms)
ghostdep generates the exact package manager commands to fix your deps:
# preview what would be run
ghostdep fix --dry-runOutput:
npm install axios
npm uninstall lodash
pip install pandas
pip uninstall numpy
You can also let ghostdep run the commands directly (it'll ask for confirmation first):
ghostdep fix --applyThis works for npm/yarn, pip, cargo, and go. For Maven and Gradle, it prints the snippets you need to add/remove manually since those don't have clean CLI commands for it.
Create a .ghostdep.yaml in your project root (or run ghostdep init):
ignore_deps:
- "internal-*"
- "my-shared-lib"
ignore_paths:
- "scripts/**"
- "generated/**"
include_dev: true
min_confidence: low
format: text
cache: falseCLI flags always override the config file. Ignore lists are additive across both.
ghostdep automatically detects monorepos with multiple manifests. Cross-project imports between sibling packages won't be flagged as phantoms.
Not all imports are created equal:
| Confidence | What it means |
|---|---|
| high | Static import at top level (import x, use x, require("x")) |
| medium | Conditional import (try/except, dynamic import("x") with string) |
| low | Dynamic import with variable (require(expr), importlib.import_module(var)) |
Filter by confidence with --min-confidence:
# only show high-confidence findings
ghostdep --min-confidence highghostdep exits with code 1 when findings are present, making it easy to use in CI:
# GitHub Actions
- name: Check dependencies
run: ghostdep -f sarif > results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifghostdep has a WASM plugin host that can load .wasm files from plugins/ in your project root or ~/.ghostdep/plugins/. This lets you add support for languages ghostdep doesn't cover yet.
The plugin API is functional but not yet documented — if you're interested in writing a plugin, look at src/plugin/wasm_host.rs for the expected exports. Proper docs and an example plugin are on the roadmap.
git clone https://github.com/ojuschugh1/ghostdep
cd ghostdep
cargo build --releaseRun the tests:
cargo testHere's what's shipped and what's coming:
v1 (current) — shipped:
- Scanning for Go, JS/TS, Python, Rust, Java
- AST-based import extraction with confidence scoring
- Text, JSON, SARIF output
- Fix command generation (dry-run and apply)
- Monorepo support
- Incremental caching
- WASM plugin host (experimental, undocumented API)
- GitHub Actions release workflow
v1.x — next up:
- WASM plugin API documentation + example plugin
- Homebrew tap
- crates.io publish
- Better Java import-to-artifact mapping
--fixfor Maven/Gradle (direct manifest editing)
v2 — planned:
- Transitive dependency analysis
- AI hallucination detection (flag phantom deps that don't exist in any registry)
- LSP integration (real-time warnings in your editor)
- Lockfile reconciliation
We're actively looking for contributors! Whether it's adding support for a new language, improving detection accuracy, fixing bugs, or writing docs — all contributions are welcome.
Some ideas to get started:
- Add support for new languages (Ruby, PHP, C#, Swift...)
- Improve the Java import-to-artifact mapping
- Write a WASM plugin for your favorite language
- Write more property-based tests
- Improve monorepo package name extraction
- Document the WASM plugin API
Open an issue or submit a PR. No contribution is too small.
MIT — see LICENSE for details.
Made with ❤️ for the open source community.