The Problem
TypeScript agents and developers need fast, deterministic answers about code structure—what names exist, where they're referenced, where duplicate logic lives, and where low-value "slop" code accumulates. Existing tools either require network services, index databases, or produce non-deterministic output that breaks scripted workflows.
What This Does
scanr is a single static-analysis CLI binary written in Rust (52 files under src/). It extracts structural inventory (functions, types, components, hooks), traces declaration-to-usage references, detects near-duplicate functions and types via APTED tree edit distance, and flags evidence-backed "slop" patterns. All analysis is offline, deterministic, and gitignore-aware.
The codebase splits into three command groups: src/commands/ handles the CLI surface (search, scan, refs, dupes, slop, tree), src/scan/ implements structural extraction and rule checking, and src/similarity/ contains the tree-diffing engine. A .claude-plugin/ directory packages it for Claude Code agents.
How It Is Wired
Execution starts at src/main.rs, which parses arguments via src/cli.rs and dispatches to command handlers in src/commands/. Each command follows the same pattern: parse flags → walk the filesystem (respecting .gitignore) → process files → emit sorted, stable output.
The scan command routes through src/scan/typescript/extract.rs and parse.rs to build structural inventories. The dupes command uses src/similarity/apted.rs for exact tree matching and fast_similarity.rs for approximate comparison. The slop command draws on detectors in src/slop/exact_detectors.rs, contextual_detectors.rs, and low_value_detectors.rs.
The widest blast radius sits in src/scan/typescript/extract.rs—nearly every command depends on its inventory output, and changes there ripple through refs, dupes, and slop. The src/similarity/ module is the most complex, with 18 files handling tree adaptation, fingerprinting, and type comparison; modifying it requires understanding the AST adapter chain (typescript_structure_adapter.rs → tree.rs → apted.rs).
External effects are minimal: the binary reads files, writes to stdout, and exits. No database, network calls, or API keys. The Makefile and Dockerfile provide build paths, and GitHub Actions in .github/workflows/ runs CI and release builds.
How To Use It
# Build from source (Rust 1.95+)
git clone https://github.com/moses-y/scanr
cd scanr
cargo build --release
# Or via Homebrew
brew install nikuscs/tap/scanr
No environment variables or configuration files required. Run commands directly:
scanr search "useState" --root . --json
scanr scan --root . --mode inventory
scanr refs Card --root .
scanr dupes --root . --types
scanr slop --root .
scanr tree --root .
Real-World Use
For a CI pipeline reviewing TypeScript PRs, scanr can gate merges on structural quality:
scanr scan --root . --mode inventory --json > inventory.json
scanr dupes --root . --types --json > dupes.json
scanr slop --root . --json > slop.json
# Parse JSON outputs in a script; fail the build on high-severity findings
The deterministic, JSON-sorted output makes these results stable across runs—critical for diff-based review tooling.
Code Health & Issues
Deep static analysis has not run on this repository. Structural observations from the file layout:
- Low - No
CONTRIBUTING.mdor explicit contribution guide; the 5 doc files cover usage but not development workflow. - Low - Forked from
nikuscs/scanrwith 0 stars on this fork; upstream maintenance status is unclear. - Positive - 9 test files exist alongside source modules, CI is configured, and a
Cargo.lockis committed for reproducible builds.
The Bottom Line
A focused, well-architected tool for TypeScript codebase analysis that prioritizes determinism and offline operation. The Rust implementation is solid, with clear module boundaries and test coverage. Teams needing fast, scriptable structural analysis without external dependencies will find it valuable; those wanting IDE-grade intelligence should look elsewhere.