npm package: @mishasinitcyn/betterrank
Description: Structural code index with PageRank-ranked repo maps, symbol search, call-graph queries, and dependency analysis. Built on tree-sitter and graphology.
Latest version: 0.2.5

# BetterRank

Structural code index with PageRank-ranked repo maps, symbol search, call-graph queries, and dependency analysis. Built on [tree-sitter](https://tree-sitter.github.io/) and [graphology](https://graphology.github.io/).

BetterRank parses your source files, builds a dependency graph, and ranks every symbol by structural importance using PageRank. Instead of grepping through thousands of files, ask questions like "what are the most important functions in this project?" or "who calls this function?" and get ranked, structured answers in seconds.

## Install

```bash
npm install -g @mishasinitcyn/betterrank
```

## Quick Start

```bash
# Get a ranked overview of any project
betterrank map --root /path/to/project

# View a file's skeleton without reading the whole thing
betterrank outline src/auth.py

# Expand a specific function to see its full source
betterrank outline src/auth.py authenticate_user

# Search for symbols by name or parameter
betterrank search auth --root /path/to/project

# Who calls this function? (with call site context)
betterrank callers authenticateUser --root /path/to/project --context

# Everything about a function: source, types, deps, callers
betterrank context calculate_bid --root /path/to/project

# Trace the full call chain from entry point to function (upward)
betterrank trace calculate_bid --root /path/to/project

# What does a function call, recursively? (downward)
betterrank callees calculate_bid --root /path/to/project

# What symbols changed and what might break?
betterrank diff --root /path/to/project

# What depends on this file?
betterrank dependents src/auth/handlers.ts --root /path/to/project

# What does this file import?
betterrank deps src/auth/handlers.ts --root /path/to/project

# Local subgraph around a file
betterrank neighborhood src/auth/handlers.ts --root /path/to/project --limit 10
```

## How It Works

1. **Parse**: Native tree-sitter extracts definitions (functions, classes, types) and references (calls, imports) from source files
2. **Graph**: graphology MultiDirectedGraph with typed edges (DEFINES, REFERENCES, IMPORTS)
3. **Rank**: PageRank scores every symbol by structural importance — heavily-imported utilities rank higher than leaf files
4. **Cache**: mtime-based incremental updates. Only re-parses changed files. Cache lives at the platform cache directory (`~/Library/Caches/code-index/` on macOS, `~/.cache/code-index/` on Linux)

## Supported Languages

JavaScript, TypeScript, Python, Rust, Go, Java, Ruby, C, C++, C#, PHP

## Commands

### `outline` — File skeleton with collapsed bodies

View a file's structure without reading the entire thing. Function and class bodies are collapsed to `... (N lines)`. Expand specific symbols by name. **No `--root` required** — works on any file standalone.

```bash
# Skeleton view: imports, signatures, constants — bodies collapsed
betterrank outline src/auth.py

# Expand a specific function to see its full source
betterrank outline src/auth.py authenticate_user

# Expand multiple symbols
betterrank outline src/auth.py validate,process

# Show caller counts next to each function (requires --root)
betterrank outline src/auth.py --annotate --root ./backend

# Resolve path relative to a root
betterrank outline src/auth.py --root ./backend
```

**Example output (with `--annotate`):**
```
   1│ from fastapi import APIRouter, Depends
   2│ from core.auth import verify_auth
   3│
   4│ router = APIRouter(prefix="/api")
   5│
   6│ @router.get("/users")
   7│ async def list_users(db = Depends(get_db)):
    │   ... (25 lines)                                          ← 2 callers
  33│
  34│ @router.post("/users")
  35│ async def create_user(data: UserCreate, db = Depends(get_db)):
    │   ... (40 lines)                                          ← 5 callers
```

Typical compression: **3-5x** (a 2000-line file becomes ~400 lines of outline). Annotations show how many *external* files reference each function — instantly see what's critical vs dead code.

### `map` — Repo map

Summary of the most structurally important definitions, ranked by PageRank. Default limit is 50 symbols.

```bash
betterrank map --root /path/to/project
betterrank map --root /path/to/project --limit 100
betterrank map --root /path/to/project --focus src/api/auth.ts,src/api/login.ts
betterrank map --root /path/to/project --count
```

### `search` — Find symbols by name or signature

Substring search across symbol names **and** full function signatures (including parameter names and types). Results ranked by PageRank.

```bash
betterrank search encrypt --root /path/to/project
betterrank search max_age --root /path/to/project          # matches param names too
betterrank search imp --root /path/to/project --kind function
```

### `symbols` — List definitions

```bash
betterrank symbols --root /path/to/project
betterrank symbols --root /path/to/project --file src/auth.ts
betterrank symbols --root /path/to/project --kind function
```

### `callers` — Who calls this symbol

```bash
# File names only
betterrank callers authenticateUser --root /path/to/project

# With call site context lines (default 2 lines around each site)
betterrank callers authenticateUser --root /path/to/project --context

# Custom context window
betterrank callers authenticateUser --root /path/to/project --context 3
```

**Example output (with `--context`):**
```
src/engine/pipeline.py:
      16│ from app.engine.bidding import run_auction
      17│

     153│     return await run_auction(
     154│         searched=campaigns,
     155│         publisher=config.publisher,

src/api/serve.py:
     145│     bid = await run_auction(searched, publisher=pub_id)
```

### `context` — Full function context in one shot

Everything you need to understand a function: its source, the types in its signature (expanded inline), the functions it calls (with signatures), and a callers summary. Eliminates the multi-command chase.

```bash
betterrank context calculate_bid --root /path/to/project

# Skip the source, just show deps/types/callers
betterrank context calculate_bid --root /path/to/project --no-source
```

**Example output (`--no-source`):**
```
── calculate_bid (src/engine/bidding.py:489-718) ──

Types:
  AuctionData2 (src/engine/bidding.py:74)
    publisher_id: str
    campaign_metrics: Dict[str, CampaignMetrics]
    time_remaining_pct: float

References:
  [function] def from_microdollars(microdollars) -> Decimal  (src/core/currency.py:108)
  [function] def get_config() -> ValuePredictorConfig        (src/engine/predictor/config.py:316)
  [function] def get_value_predictor() -> ValuePredictor      (src/engine/predictor/persistence.py:123)
  [class]    class ValuePredictor                             (src/engine/predictor/predictor.py:43)

Callers (1 file):
  src/engine/bidding.py
```

### `history` — Git history of a specific function

Shows only commits that touched a function's lines. Uses tree-sitter line ranges for accuracy (better than git's heuristic `:funcname:` detection). Add `--patch` to include function-scoped diffs.

```bash
# Commit list (compact)
betterrank history calculate_bid --root /path/to/project

# With function-scoped diffs
betterrank history calculate_bid --root /path/to/project --patch --limit 3

# Paginate through older commits
betterrank history calculate_bid --root /path/to/project --offset 5 --limit 5
```

**Example output:**
```
calculate_bid (src/engine/bidding.py:489-718)

  082b9d5  2026-02-24  fix: restore GSP auction pricing
  c75f5ff  2026-02-14  fix: resolve lint errors from main merge
  623429c  2026-02-13  hot fix
  5d236d3  2026-02-06  feat: wire ad_position to ValuePredictor
```

### `trace` — Recursive caller chain (upward)

Walk UP the call graph from a symbol to see the full path from entry points to your function. At each hop, resolves which function in the caller file contains the call site.

```bash
betterrank trace calculate_bid --root /path/to/project
betterrank trace calculate_bid --root /path/to/project --depth 5
```

**Example output:**
```
calculate_bid (src/engine/bidding.py:489)
  ← run_auction (src/engine/bidding.py:833)
    ← handle_request (src/engine/pipeline.py:153)
      ← app (src/main.py:45)
```

### `callees` — Recursive callee chain (downward)

Walk DOWN the call graph from a symbol to see everything it calls, transitively. The mirror of `trace`. Use before refactoring to understand downstream dependencies.

```bash
betterrank callees calculate_bid --root /path/to/project
betterrank callees calculate_bid --root /path/to/project --depth 5
```

**Example output:**
```
calculate_bid (src/engine/bidding.py:489)
  → from_microdollars (src/core/currency.py:108)
  → get_config (src/engine/predictor/config.py:316)
    → load_yaml (src/core/config.py:22)
  → get_value_predictor (src/engine/predictor/persistence.py:123)
```

Use both together for a full "sandwich view" of a function — who calls it (upstream) and what it touches (downstream).

### `diff` — Git-aware blast radius

Shows which symbols changed in the working tree and how many external files call each changed symbol. Compares current disk state against a git ref.

```bash
# Uncommitted changes vs HEAD
betterrank diff --root /path/to/project

# Changes since a specific commit or branch
betterrank diff --ref main --root /path/to/project
betterrank diff --ref HEAD~5 --root /path/to/project
```

**Example output:**
```
src/engine/bidding.py:
  ~ [function] calculate_bid  (3 callers)
  + [function] compute_value_multi
  - [function] old_quality_score  (1 caller)

⚠ 4 external callers of modified/removed symbols
```

### `deps` — What does this file import

```bash
betterrank deps src/auth.ts --root /path/to/project
```

### `dependents` — What imports this file

```bash
betterrank dependents src/auth.ts --root /path/to/project
```

### `neighborhood` — Local subgraph around a file

```bash
betterrank neighborhood src/auth.ts --root /path/to/project --count
betterrank neighborhood src/auth.ts --root /path/to/project --hops 2 --limit 10
```

### `orphans` — Find disconnected files/symbols

```bash
betterrank orphans --root /path/to/project                          # orphan files
betterrank orphans --level symbol --root /path/to/project           # orphan symbols
betterrank orphans --level symbol --kind function --root /path/to/project
```

### `structure` — File tree with symbol counts

```bash
betterrank structure --root /path/to/project --depth 3
```

### `reindex` — Force full rebuild

```bash
betterrank reindex --root /path/to/project
```

### `stats` — Index statistics

```bash
betterrank stats --root /path/to/project
```

## Global Flags

| Flag | Description |
|---|---|
| `--root <path>` | Project root. **Always pass this explicitly.** |
| `--count` | Return counts only (no content) |
| `--offset N` | Skip first N results |
| `--limit N` | Max results to return (default: 50) |

## Programmatic API

```javascript
import { CodeIndex } from '@mishasinitcyn/betterrank';

const idx = new CodeIndex('/path/to/project');

const map = await idx.map({ limit: 100, focusFiles: ['src/main.ts'] });
const results = await idx.search({ query: 'auth', kind: 'function', limit: 10 });
const callers = await idx.callers({ symbol: 'authenticate', context: 2 });
const tree = await idx.callees({ symbol: 'authenticate', depth: 3 });
const counts = await idx.getCallerCounts('src/auth.ts');
const deps = await idx.dependencies({ file: 'src/auth.ts' });
const dependents = await idx.dependents({ file: 'src/auth.ts' });
const hood = await idx.neighborhood({ file: 'src/auth.ts', hops: 2, maxFiles: 10 });

const stats = await idx.stats();
await idx.reindex();
```

## Ignore Configuration

Built-in defaults ignore `node_modules`, `dist`, `build`, `.venv`, etc. To add project-specific ignores, create `<project-root>/.code-index/config.json`:

```json
{
  "ignore": ["experiments/**", "legacy/**"]
}
```

## Cache

Cache lives at the platform cache directory:
- **macOS**: `~/Library/Caches/code-index/`
- **Linux**: `~/.cache/code-index/`
- **Windows**: `%LOCALAPPDATA%/code-index/Cache/`

Override with `CODE_INDEX_CACHE_DIR` env var. Cache files are disposable — delete anytime, they rebuild automatically.

## License

MIT