ghostdep π»
A fast, cross-language phantom dependency detector.
ghostdep scans your project and finds two things:
- Phantom dependencies β packages you import in code but forgot to add to your manifest
- Unused dependencies β packages declared in your manifest that nothing actually imports
It works across Go, JavaScript/TypeScript, Python, Rust, and Java. Single binary, zero runtime dependencies, built in Rust.
Why does this matter?
Phantom dependencies are a real problem:
- Your code works locally because some transitive dep happens to provide the package
- CI breaks, or worse β production breaks when that transitive dep gets removed
- Supply chain attacks can exploit undeclared dependencies
Unused dependencies are less dangerous but still annoying:
- Bloated install times
- Larger container images
- Confusing dependency lists for new contributors
ghostdep catches both in milliseconds.
How it works
source files βββ AST parser βββ import list βββ
ββββ matching engine βββ findings
manifest file βββ manifest parser βββ dep list β
- Walks your project directory
- Parses source files using tree-sitter (Go, Python, Rust, Java) or OXC (JS/TS) to extract imports
- Parses your manifest file to get declared dependencies
- Cross-references the two lists
- Reports whatβs missing and whatβs unused
Quick example
$ ghostdep -p my-project
[phantom] axios at src/api.js:3 (confidence: high)
[unused] lodash at package.json (confidence: high)
Found 1 phantom and 1 unused dependencies (12 files scanned in 3ms)
Installation
From GitHub Releases (recommended)
Download the latest binary for your platform:
curl -fsSL https://raw.githubusercontent.com/ojuschugh1/ghostdep/main/install.sh | sh
This detects your OS and architecture, downloads the right binary, and installs it to /usr/local/bin.
From source
If you have Rust installed:
cargo install --path .
Or clone and build:
git clone https://github.com/ojuschugh1/ghostdep
cd ghostdep
cargo build --release
# binary is at ./target/release/ghostdep
Supported platforms
| OS | Architecture | Binary |
|---|---|---|
| Linux | x86_64 | ghostdep-linux-amd64 |
| Linux | aarch64 | ghostdep-linux-arm64 |
| macOS | x86_64 | ghostdep-darwin-amd64 |
| macOS | Apple Silicon | ghostdep-darwin-arm64 |
| Windows | x86_64 | ghostdep-windows-amd64.exe |
Verify installation
ghostdep --version
Quick Start
Scan your project
Just run ghostdep in your project directory:
ghostdep
Or point it at a specific path:
ghostdep -p /path/to/project
ghostdep auto-detects the language and manifest file. No configuration needed.
Read the output
[phantom] axios at src/api.js:3 (confidence: high)
[unused] lodash at package.json (confidence: high)
Found 1 phantom and 1 unused dependencies (12 files scanned in 3ms)
[phantom]= imported in code but not in your manifest[unused]= in your manifest but never imported- The file and line number tell you where the import is
- Confidence tells you how sure ghostdep is (see Confidence Scoring)
Exit codes
| Code | Meaning |
|---|---|
| 0 | No findings β your deps are clean |
| 1 | Findings present |
| 2 | Error (no manifest found, parse error, etc.) |
Generate a config file
ghostdep init
Creates a .ghostdep.yaml with sensible defaults. See Configuration for details.
Fix your deps
Preview the fix commands:
ghostdep fix --dry-run
Or let ghostdep run them (with confirmation):
ghostdep fix --apply
See Auto-Fix for details.
Scanning Projects
Basic scan
ghostdep
Scans the current directory. ghostdep walks the file tree, finds manifest files, and scans source files for imports.
Scan a specific directory
ghostdep -p /path/to/project
What gets scanned
ghostdep uses the ignore crate for file walking, which respects .gitignore rules. It also skips:
node_modules/vendor/target/(Rust build output)build/dist/__pycache__/.git/- Binary files
Restrict to a language
ghostdep -l python
Only scans Python files and Python manifests.
Specify a manifest
ghostdep -m path/to/package.json
Useful when your manifest isnβt in the project root.
Exclude paths
ghostdep --exclude-path "generated/**" --exclude-path "scripts/**"
Ignore specific dependencies
ghostdep -i "internal-*" -i "my-shared-lib"
Glob patterns. Matched deps wonβt appear in findings.
Include/exclude dev dependencies
Dev dependencies are included by default. To exclude them:
ghostdep --dev
Parallel scanning
ghostdep scans files in parallel using Rayon. By default it uses all available CPU cores. To limit:
ghostdep --threads 4
Verbose mode
ghostdep -v
Prints progress info to stderr: number of project scopes detected, files scanned, errors skipped.
Quiet mode
ghostdep -q
Suppresses all output. Only the exit code tells you the result. Useful in CI when you just want pass/fail.
Output Formats
ghostdep supports three output formats: text (default), JSON, and SARIF.
Text (default)
ghostdep
[phantom] axios at src/api.js:3 (confidence: high)
[unused] lodash at package.json (confidence: high)
Found 1 phantom and 1 unused dependencies (12 files scanned in 3ms)
Human-readable. Each line shows the finding type, package name, location, and confidence. Summary at the end.
JSON
ghostdep -f json
{
"findings": [
{
"finding_type": "Phantom",
"package": "axios",
"file": "src/api.js",
"line": 3,
"manifest": "package.json",
"language": "JavaScript",
"confidence": "High"
}
],
"metadata": {
"project_root": "/path/to/project",
"scanned_files": 12,
"duration_ms": 3,
"ghostdep_version": "0.1.0",
"languages": ["JavaScript"]
}
}
Every finding includes: finding_type, package, file, line, manifest, language, confidence. The metadata object has scan stats.
SARIF
ghostdep -f sarif
Produces a valid SARIF v2.1.0 document. SARIF is the standard format for static analysis tools and integrates with GitHub Code Scanning.
Rules:
GHOST001β Phantom dependency (level: error)GHOST002β Unused dependency (level: warning)
Each result includes locations, confidence in properties, and the manifest path.
See CI Integration for how to upload SARIF to GitHub.
Configuration
Config file
Create a .ghostdep.yaml in your project root:
ghostdep init
Or write one manually:
ignore_deps:
- "internal-*"
- "my-shared-lib"
ignore_paths:
- "scripts/**"
- "generated/**"
include_dev: true
min_confidence: low
format: text
cache: false
Config options
| Option | Type | Default | Description |
|---|---|---|---|
ignore_deps | list of globs | [] | Dependencies to exclude from findings |
ignore_paths | list of globs | [] | File paths to skip during scanning |
include_dev | bool | true | Whether to analyze dev dependencies |
min_confidence | low/medium/high | low | Minimum confidence threshold |
format | text/json/sarif | text | Output format |
threads | number or null | null (all cores) | Max scanner threads |
cache | bool | false | Enable incremental scan cache |
Precedence
Configuration is resolved in three layers:
- Defaults β hardcoded sensible values
- Config file β
.ghostdep.yamloverrides defaults - CLI flags β always win over config file
ignore_deps and ignore_paths are additive across all layers. Everything else is last-writer-wins.
Example: suppress known false positives
ignore_deps:
- "internal-*" # internal packages in your monorepo
- "my-codegen-lib" # generated code imports this
Example: CI-optimized config
format: sarif
min_confidence: medium
include_dev: false
cache: true
ignore_paths:
- "test/**"
- "scripts/**"
Auto-Fix
ghostdep can generate package manager commands to fix detected findings.
Dry run (default)
Preview what would be run:
ghostdep fix --dry-run
npm install axios
npm uninstall lodash
pip install pandas
pip uninstall numpy
Nothing is executed. Commands are printed to stdout.
Apply fixes
Run the commands directly:
ghostdep fix --apply
ghostdep will ask for confirmation before executing anything:
Execute 4 command(s)? [y/N]
Each commandβs result is reported:
running: npm install axios
ok
running: npm uninstall lodash
ok
Commands by language
| Manifest | Phantom (install) | Unused (remove) |
|---|---|---|
package.json | npm install <pkg> | npm uninstall <pkg> |
package.json + yarn.lock | yarn add <pkg> | yarn remove <pkg> |
requirements.txt | pip install <pkg> | pip uninstall <pkg> |
go.mod | go get <module> | go mod tidy |
Cargo.toml | cargo add <crate> | cargo remove <crate> |
pom.xml | prints XML snippet | prints removal note |
build.gradle | prints dependency line | prints removal note |
ghostdep detects yarn.lock to choose yarn over npm automatically.
For Maven and Gradle, thereβs no clean CLI command to add/remove deps, so ghostdep prints the snippets you need to paste manually.
Confidence Scoring
Not all imports are equally reliable. ghostdep assigns a confidence level to each finding based on how the import was detected.
Levels
| Confidence | What it means | Examples |
|---|---|---|
| high | Static import at top level | import x, use x, require("x"), from x import y |
| medium | Conditional or dynamic with string literal | try: import x, import("lodash") |
| low | Dynamic with variable/expression | require(expr), importlib.import_module(var), import(getModule()) |
Language-specific behavior
- Go β all imports are high confidence (Go has no dynamic imports)
- Rust β all imports are high confidence (no dynamic imports)
- Java β all imports are high confidence (
Class.forName()not detected in v1) - JavaScript/TypeScript β mixed: ESM imports and
require("string")are high,import("string")is medium,require(variable)is low - Python β mixed:
import xandfrom x import yare high, imports insidetry/exceptare medium,__import__()andimportlib.import_module()are low
Filtering
Show only high-confidence findings:
ghostdep --min-confidence high
Show medium and above:
ghostdep --min-confidence medium
Default is low (show everything).
In config
min_confidence: medium
In output
Text format shows confidence inline:
[phantom] axios at src/api.js:3 (confidence: high)
JSON and SARIF include it as a field/property on each finding.
Monorepo Support
ghostdep automatically detects monorepos and handles cross-project imports correctly.
How it works
When ghostdep finds multiple manifest files in nested directories, it treats each one as a separate project scope. It builds an internal package index from the directory names and excludes cross-project imports from phantom findings.
my-monorepo/
βββ packages/
β βββ api/
β β βββ package.json β scope 1
β β βββ src/index.js
β βββ web/
β β βββ package.json β scope 2
β β βββ src/app.js
β βββ shared/
β βββ package.json β scope 3
β βββ src/utils.js
If api/src/index.js imports @myorg/shared, ghostdep recognizes shared as an internal package and wonβt flag it as phantom.
Scoping
Each project scope is analyzed independently:
- Imports in
api/are matched againstapi/package.json - Imports in
web/are matched againstweb/package.json - Cross-project imports are excluded from phantom results
Mixed languages
Monorepos with different languages work fine:
my-monorepo/
βββ services/
β βββ api/
β βββ go.mod
β βββ main.go
βββ frontend/
β βββ package.json
β βββ src/app.tsx
Each sub-project uses its own language plugin.
Limitations
- Internal package detection uses directory names, not the
namefield from manifests (planned improvement) - Deeply nested workspaces (workspace-of-workspaces) arenβt specially handled β each manifest is a flat scope
CI Integration
ghostdep is designed for CI. Itβs a single binary with deterministic output and meaningful exit codes.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Clean β no findings |
| 1 | Findings present |
| 2 | Error |
GitHub Actions
Basic check
- name: Install ghostdep
run: curl -fsSL https://raw.githubusercontent.com/ojuschugh1/ghostdep/main/install.sh | sh
- name: Check dependencies
run: ghostdep
With SARIF upload (GitHub Code Scanning)
- name: Install ghostdep
run: curl -fsSL https://raw.githubusercontent.com/ojuschugh1/ghostdep/main/install.sh | sh
- name: Run ghostdep
run: ghostdep -f sarif > ghostdep.sarif
continue-on-error: true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ghostdep.sarif
This shows findings as annotations directly in pull requests.
JSON output for custom processing
- name: Run ghostdep
run: |
ghostdep -f json > ghostdep.json
cat ghostdep.json | jq '.findings | length'
GitLab CI
ghostdep:
stage: lint
script:
- curl -fsSL https://raw.githubusercontent.com/ojuschugh1/ghostdep/main/install.sh | sh
- ghostdep
Pre-commit hook
#!/bin/sh
ghostdep -q
Save as .git/hooks/pre-commit and chmod +x it. The -q flag suppresses output β itβll just block the commit if findings are present.
Tips
- Use
--cachefor faster repeat scans in CI (cache the.ghostdep-cache/directory between runs) - Use
--min-confidence mediumto reduce noise from dynamic imports - Use
-qwhen you only care about pass/fail
Go
Manifest
go.mod β parses require directives (both single-line and block form). Version suffixes are stripped.
Import scanning
Uses tree-sitter with the Go grammar. Extracts from import_declaration / import_spec nodes. Handles single imports, grouped imports, aliased imports, and blank imports (_ "pkg").
All Go imports are static β confidence is always high.
Normalization
github.com/foo/bar/bazβgithub.com/foo/bar(first 3 segments for domain-style paths)golang.org/x/syncβgolang.org/x/syncfmtβfmt(stdlib, filtered out)
Stdlib detection
~150 known Go stdlib packages compiled into the binary. Includes nested paths like net/http, encoding/json, etc.
Aliases
None β Go packages use their module path directly.
JavaScript / TypeScript
Manifest
package.json β extracts dependency names from dependencies, devDependencies, peerDependencies, and optionalDependencies.
Import scanning
Uses the OXC parser (Rust-native, faster than tree-sitter for JS/TS). Handles:
| Pattern | Confidence |
|---|---|
import x from 'pkg' | high |
const x = require('pkg') | high |
export { x } from 'pkg' | high |
export * from 'pkg' | high |
import('pkg') with string literal | medium |
require(variable) | low |
import(expression) | low |
File extensions: .js, .jsx, .ts, .tsx, .mjs, .cjs, .mts, .cts
Normalization
- Relative imports (
./,../,/) are skipped - Scoped:
@scope/pkg/deep/pathβ@scope/pkg - Unscoped:
lodash/mergeβlodash node:fsβfs(then filtered as stdlib)
Stdlib detection
All Node.js built-in modules including the node: prefix. Covers fs, path, http, crypto, stream, child_process, etc.
Aliases
Minimal β most JS packages use their npm name directly.
Python
Manifests
ghostdep reads dependencies from multiple Python manifest formats:
| Format | Section | Status |
|---|---|---|
requirements.txt | line-by-line package names | stable |
pyproject.toml | [project].dependencies (PEP 621) | stable |
pyproject.toml | [project].optional-dependencies | stable |
pyproject.toml | [tool.poetry.dependencies] | stable |
pyproject.toml | [tool.poetry.dev-dependencies] | stable |
pyproject.toml | [tool.poetry.group.*.dependencies] | stable |
pyproject.toml | [dependency-groups] (PEP 735 / uv) | stable |
requirements.txt
Parses line-by-line. Skips blanks, comments (#), and option lines (-r, -e, --). Strips version specifiers, extras ([extra]), and environment markers (;).
pyproject.toml
Handles PEP 621 ([project]), Poetry ([tool.poetry]), and PEP 735 ([dependency-groups]) in a single pass. Skips python = "^3.x" in Poetry deps and {include-group = "..."} directives in dependency-groups.
Package name normalization
Follows PEP 503: lowercase, replace -, _, . with a single _. So scikit-learn, Scikit_Learn, and scikit.learn all normalize to scikit_learn.
Import scanning
Uses tree-sitter with the Python grammar.
| Pattern | Confidence |
|---|---|
import x | high |
from x.y import z | high |
import x inside try/except | medium |
__import__("x") | low |
importlib.import_module("x") | low |
Normalization
Takes the first segment of the dotted path:
from PIL.Image import openβPILimport os.pathβos
Stdlib detection
~300 Python 3.10+ stdlib modules. Includes common sub-packages like collections.abc, concurrent.futures, urllib.parse, etc.
Aliases
Python has a lot of packages where the import name differs from the pip name:
| Import | Package |
|---|---|
PIL | Pillow |
cv2 | opencv-python |
sklearn | scikit-learn |
yaml | PyYAML |
bs4 | beautifulsoup4 |
attr | attrs |
dateutil | python-dateutil |
dotenv | python-dotenv |
serial | pyserial |
Crypto | pycryptodome |
git | GitPython |
google.protobuf | protobuf |
And more β see the source for the full list.
Rust
Manifest
Cargo.toml β extracts crate names from [dependencies], [dev-dependencies], and [build-dependencies]. Handles both crate = "version" and crate = { version = "...", package = "..." } forms.
When package = "actual-name" is present, the actual name is used for matching.
Import scanning
Uses tree-sitter with the Rust grammar. Extracts from:
use serde::Deserializeβserdeextern crate serdeβserdeserde_json::from_str!(...)βserde_json(macro invocations with::path)
All Rust imports are static β confidence is always high.
Normalization
Takes the first path segment and replaces _ with - for Cargo matching:
use tokio::runtime::Runtimeβtokiouse serde_json::Valueβserde-json
Stdlib detection
std, core, alloc, proc_macro, test, plus the relative paths self, super, crate.
Aliases
Built dynamically from Cargo.toml. If you have:
[dependencies]
my_serde = { version = "1", package = "serde" }
Then use my_serde::Deserialize correctly resolves to the serde crate.
Java (beta)
Java support is in beta. The import-to-artifact mapping is inherently heuristic β thereβs no standard 1:1 mapping between Java import paths and Maven artifact IDs. ghostdep covers ~150 common libraries and uses groupId matching as a fallback, but may miss niche libraries.
Manifests
pom.xml
Parses <dependency> elements. Extracts <groupId>, <artifactId>, and <scope>. Maps scopes to dependency groups:
| Maven scope | ghostdep group |
|---|---|
| compile (default) | main |
| test | dev |
| provided | optional |
| runtime | main |
| system | optional |
build.gradle / build.gradle.kts
Regex-based extraction. Matches Groovy ('group:artifact:version') and Kotlin DSL (("group:artifact:version")) syntax. Supported configurations:
| Gradle config | ghostdep group |
|---|---|
implementation, api, runtimeOnly | main |
testImplementation, testCompileOnly, testRuntimeOnly | dev |
compileOnly | optional |
annotationProcessor, kapt | build |
Import scanning
Uses tree-sitter with the Java grammar. Extracts from import_declaration nodes, including static imports and wildcard imports (java.util.* β java.util).
All Java imports are static β confidence is always high.
Normalization
Two-stage process:
-
Alias table lookup β tries progressively shorter prefixes of the import path against a built-in table of ~150 common libraries. Example:
org.apache.commons.lang3.StringUtilsβ triesorg.apache.commons.lang3β findscommons-lang3. -
groupId matching β if no alias matches, falls back to the first 3 segments as a groupId-style prefix (e.g.,
com.example.mylib). The matcher then checks if any declared dependencyβs groupId starts with this prefix.
Stdlib detection
Any import starting with java., javax., or jdk. is standard library.
Aliases
~150 built-in mappings covering:
- Google: gson, guava, protobuf, guice
- Apache Commons: lang3, io, collections4, codec, text, csv, compress, etc.
- Jackson: core, databind, annotations, dataformat, datatype
- Spring: boot, web, data, security, context, beans, core, test, jdbc, batch, kafka, cloud
- Testing: junit-jupiter, mockito, assertj, hamcrest, testng, testcontainers, cucumber
- Logging: slf4j, logback, log4j
- Database: HikariCP, mybatis, jooq, flyway, liquibase, jedis, lettuce, mongodb, elasticsearch
- AWS: aws-sdk-java, aws-java-sdk-core, s3, sqs, dynamodb
- And many more
PRs to expand the mapping table are very welcome.
WASM Plugins (Experimental)
ghostdep can load community-contributed language plugins compiled as WebAssembly modules.
Status: The WASM host is functional but the plugin API is not yet formally documented. If youβre interested in writing a plugin, the information below should get you started. Expect the API to stabilize in a future release.
Plugin discovery
ghostdep looks for .wasm files in two locations:
plugins/in your project root~/.ghostdep/plugins/
Any .wasm file found is loaded automatically. If loading fails, a warning is printed and the plugin is skipped.
Expected exports
Your WASM module must export these functions:
| Function | Signature | Returns |
|---|---|---|
alloc | (len: i32) -> i32 | Pointer to allocated memory |
plugin_name | () -> i64 | Packed ptr:len string |
file_extensions | () -> i64 | Comma-separated list |
manifest_filenames | () -> i64 | Comma-separated list |
parse_manifest | (ptr: i32, len: i32) -> i64 | JSON response |
scan_imports | (ptr: i32, len: i32) -> i64 | JSON response |
is_stdlib | (ptr: i32, len: i32) -> i32 | 0 or 1 |
normalize_import | (ptr: i32, len: i32) -> i64 | Packed ptr:len string |
aliases | () -> i64 | JSON object |
String passing convention
Strings are passed through WASM linear memory. The host writes input strings using alloc, and reads output strings from packed i64 values where the high 32 bits are the pointer and the low 32 bits are the length.
JSON formats
parse_manifest receives:
{"content": "...", "path": "..."}
And returns an array of:
[{"name": "pkg", "normalized": "pkg", "group": "main"}]
scan_imports receives the same input and returns:
[{"raw": "pkg", "normalized": "pkg", "line": 1, "confidence": "high"}]
aliases returns:
{"import_name": "package_name"}
Error handling
- If a plugin fails to load, itβs skipped with a warning
- If a plugin panics during a function call, the file is skipped
- Plugin errors never crash the main process
Source reference
See src/plugin/wasm_host.rs for the full implementation.
Caching
ghostdep supports incremental scanning β it caches parsed imports so unchanged files donβt need to be re-parsed.
Enable caching
ghostdep --cache
Or in .ghostdep.yaml:
cache: true
How it works
- On scan start, loads the cache from
.ghostdep-cache/scan.json - For each source file, computes a blake3 hash of the content
- If the hash matches the cached entry, reuses the cached imports (skips AST parsing)
- If the hash differs or the file is new, parses it and updates the cache
- After the scan, writes the updated cache to disk
- Entries for deleted files are pruned automatically
Cache location
.ghostdep-cache/
βββ scan.json
In your project root. Add .ghostdep-cache/ to your .gitignore.
Cache invalidation
The cache includes a version number. When ghostdepβs import extraction logic changes between releases, the cache is automatically invalidated and rebuilt.
CI usage
Cache the .ghostdep-cache/ directory between CI runs for faster repeat scans:
# GitHub Actions
- uses: actions/cache@v4
with:
path: .ghostdep-cache
key: ghostdep-${{ hashFiles('**/*.rs', '**/*.py', '**/*.js', '**/*.go', '**/*.java') }}
restore-keys: ghostdep-
- name: Scan
run: ghostdep --cache
Thread safety
The cache uses RwLock internally, so itβs safe to use with parallel scanning (--threads).
Contributing
Weβre actively looking for contributors. All skill levels welcome.
Getting started
git clone https://github.com/ojuschugh1/ghostdep
cd ghostdep
cargo test
If all tests pass, youβre ready to go.
Ideas
- Add support for new languages (Ruby, PHP, C#, Swift, Kotlinβ¦)
- Expand the Java import-to-artifact mapping table
- Improve monorepo package name extraction (read
namefrom manifests) - Write a WASM plugin for a language you use
- Document the WASM plugin API with an example
- Add
setup.cfg/setup.pysupport for Python - Improve the fix command for Maven/Gradle (direct manifest editing)
- Write more property-based tests
- Performance benchmarks on large codebases
Tests
Run the full suite:
cargo test
The test suite includes:
- Unit tests (inline in each module)
- Integration tests (invoke the binary against fixture projects in
tests/fixtures/) - Property-based tests (proptest, in
tests/property/)
Add tests for any new functionality. Property tests are preferred for correctness properties.
Pull requests
- Keep PRs focused β one feature or fix per PR
- Include tests
- Make sure
cargo testpasses - Brief description of what and why
License
By contributing, you agree that your contributions will be licensed under the MIT License.
Roadmap
v1 (current) β shipped
- Go, JS/TS, Python, Rust, Java language support
- AST-based import scanning with confidence scoring
- Text, JSON, SARIF output
- Fix command generation (dry-run and apply)
- Monorepo support
- Incremental caching
- WASM plugin host (experimental)
- Poetry and uv/PEP 735 support for Python
- GitHub Actions release workflow
- ~150 Java import-to-artifact mappings with groupId fallback
v1.x β next up
- WASM plugin API docs + example plugin
- Homebrew tap
- crates.io publish
- Better Java import-to-artifact mapping
- Direct manifest editing for Maven/Gradle fix command
setup.cfg/setup.pysupport for Python- Read
namefield from manifests for monorepo detection
v2 β planned
- Transitive dependency analysis β detect deps that only work because of transitive availability
- AI hallucination detection β flag phantom deps that donβt exist in any public registry (npm, PyPI, crates.io, Maven Central)
- LSP integration β real-time warnings in your editor
- Lockfile reconciliation β cross-reference lockfiles against manifests
Changelog
0.1.0 (unreleased)
Initial release.
- Go, JavaScript/TypeScript, Python, Rust, Java support
- AST-based import scanning (tree-sitter + OXC)
- Confidence scoring (high/medium/low)
- Text, JSON, SARIF output formats
scan,init,fixsubcommands.ghostdep.yamlconfiguration with three-layer merging- Monorepo support with internal package detection
- Incremental caching with blake3 hashing
- WASM plugin host (experimental)
- Poetry and uv/PEP 735 pyproject.toml support
- ~150 Java import-to-artifact mappings
- GitHub Actions release workflow
- Cross-platform binaries (Linux, macOS, Windows)