Skip to content

Latest commit

 

History

521 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GhostDeps

CI License: MIT npm npm downloads/week npm downloads/month yearly downloads

GhostDeps reads your manifests, lockfiles and source code and tells you which declared dependencies your code does not actually need, with the evidence and confidence behind every claim. It runs as a check on your pull requests (GitHub App or Action) and as a local CLI, and it never executes your code.

Quickstart

The CLI is on npm as ghost-deps and needs Node 22 or newer:

npx ghost-deps scan .

Scanning this repository's own fixtures/js/basic-unused fixture prints (ghost-deps 0.1.6):

GhostDeps

Languages:
  JavaScript/TypeScript

Package managers:
  none detected

Direct dependencies:
  1

Transitive dependencies:
  unknown

Findings:
  1 unused
  1 info

Verdicts:
  unused:
    left-pad - left-pad is declared but never used (medium confidence, rule: unused)
      - no import, require or dynamic import of left-pad found
      - no script, bin or config reference to left-pad found
      - left-pad is not on the dev-tooling allowlist

Notes:
    (repository-wide) - unused confidence capped pending corpus validation (high confidence, rule: unused-confidence-capped)
      - 1 unused finding(s) capped at medium confidence

Every verdict names the package, the rule, the evidence and a confidence level. Read the evidence before touching a dependency - a finding is not a command to remove a package.

Two things the output never means:

  • No findings is not an all-clear. It means nothing was reported in the analysed scope. An incomplete scan says what it could not verify instead of guessing.
  • A neutral check is not success. On GitHub, neutral can mean there are verdicts worth reviewing or that analysis was incomplete. The App never concludes failure; read the check title and notes.

To install instead of running through npx: npm install --global ghost-deps, then ghostdeps scan ..

Use it as a GitHub Action

name: ghostdeps
on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read # actions/checkout
  checks: write # create the ghostdeps check run
  pull-requests: read # added-line lookup for PR annotations

jobs:
  ghostdeps:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rowkavdev/ghostdeps/packages/action@main
        with:
          path: "."
          # fail-on: high   # opt in to gating; advisory by default

The action runs the analysis inside your job; there is no external service to install. It reports through GitHub: a ghostdeps check run on your own branches, or workflow annotations and the job summary on fork pull requests, where GITHUB_TOKEN is read-only.

Pin the action for production. @main follows the latest main, so an unpinned workflow can change behavior without a commit from you. Pin a full commit SHA instead: rowkavdev/ghostdeps/packages/action@<sha> (the SHA of the GhostDeps commit you reviewed). Full input list and the known trade-offs versus the App: GitHub Action.

Options and configuration

scan is the working command today. The CLI router also lists inspect, graph, languages, packages and explain, but those are planned routes and currently answer "not implemented yet" (exit 3); fix is a later milestone. Everything below applies to scan.

Scan flags

Flag Default Effect
scan [path] . Directory to analyse.
--json Off Print the complete schema-versioned result; never filtered.
--fail-on <severity> Unset (advisory) Exit 1 when any verdict finding reaches the threshold. Evaluates all verdict findings, even ones hidden by the display filter.
--severity <severity> Unset (show all) Filter the human display only; cannot be combined with --json.
--disable-rule <id> None Turn one recommendation rule off for the run; repeatable.
--downgrade <rule>=<confidence> None Cap a rule's confidence at high, medium or low; repeatable, never raises it.
--allowlist <ecosystem>:<name> None Mark expected tooling; a trailing * matches a name prefix; repeatable.
--fixture-roots <json> Unset Per-run fixture-scope override with the .ghostdeps.json grammar; replaces committed roots for the run.

An unknown rule id or ecosystem is a usage error that names the known values, never a silent no-op. Full semantics: CLI.

Gating with --fail-on

GhostDeps is advisory by default: findings never fail a successful scan unless you set --fail-on. One caveat matters for adopters today: unused confidence and severity are capped at medium pending 14 consecutive green nightly corpus runs, so --fail-on high cannot catch a current unused finding, while --fail-on medium can. Verified against ghost-deps 0.1.6: scanning the js/basic-unused fixture exits 0 with --fail-on high and 1 with --fail-on medium. Info notes (scan-completeness, no-recommendations) are always severity info and cannot trip any threshold above info.

Exit codes

Code Meaning
0 Success; with --fail-on, no finding reached the threshold
1 --fail-on threshold met or exceeded
2 Usage error, or the scan itself failed (no usable result)
3 Command not implemented yet

Repository config: .ghostdeps.json

The only repository config file declares fixture-only roots to leave out of dependency analysis. Commit it at the repository root:

{
  "schemaVersion": 1,
  "fixtureRoots": ["fixtures"],
  "commentsOff": true
}

commentsOff is optional (default false): set it when GhostDeps must never maintain a PR comment for this repository. The committed file is the switch - the GitHub App's comment delivery honours it, and the CLI summary always discloses the off state and its source, never silent. A per-run --fixture-roots payload may restate the choice for that run's record, but the App follows only the committed file. Roots are exact, repository-relative directory paths - no globs, negation or ... Exclusion is always disclosed in the output's Scan scope record, never silent, and a run that excluded files caps absence-based verdicts. A malformed config fails the scan with a named error. Beyond scan scope there is no general config file yet: configuration resolves from defaults plus flags, and flags always win. Full grammar and limits: Configuration.

False positives and limits

GhostDeps is a static analyzer. It parses source, manifests and lockfiles but never installs dependencies, runs build scripts, executes your code or runs your tests. That is a deliberate safety posture, and it sets the honest limits:

  • It cannot see runtime behavior. Dynamic imports, plugin systems, convention-based loading (frameworks, test runners, bundler config) and packages consumed by external tools can make a genuinely used package look unreferenced.
  • It does not certify removal. Even a high-confidence finding means the stated evidence and coverage conditions were met - not that deletion preserves behavior. A package with no finding has not been certified necessary either.
  • Incomplete analysis stays visible. Skipped files, unsupported capabilities, timeouts and missing lockfiles are reported as notes, lower confidence or a withheld verdict, not filled in with a guess. Transitive counts without a usable lockfile are unknown, not zero.

Before removing a flagged package, validate manually:

  1. Search the repo for dynamic or runtime references: import(, require( with a computed name, plugin registries, and script, bin and config references in package.json, CI and tooling config.
  2. Remove the package in a scratch branch and update the lockfile with your package manager.
  3. Run your full build and test suite - only your own tests can prove removal is safe, and GhostDeps does not run them.

How verdicts, confidence, notes and package facts work: Interpreting results.

Status

Early development. JavaScript/TypeScript, Python, Rust and Go are wired end to end; findings are advisory, and unused confidence and severity stay capped at medium pending 14 consecutive green nightly corpus runs. GhostDeps says what it could not verify instead of guessing.

What the check looks like

Findings land on a ghostdeps check run with an advisory conclusion: success (quiet) or neutral, never a blocking failure. Annotations appear only on high-confidence findings whose evidence points at a line the PR added. Every finding carries its evidence, a confidence level and stated limitations, and an incomplete scan says what it could not verify rather than calling packages unused. How to read verdict kinds, confidence levels and notes: Interpreting results.

Documentation

Use GhostDeps

How it works

Build and contribute

License

MIT

About

Find dependencies your code doesn't really need, in any language.

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages