Why does repomap.go come before internal_helper.go? Because the ranker scored it higher. Here's what the ranker looks at.
Each file gets a score. Higher scores show up first and keep more detail.
| Signal | Weight |
|---|---|
Entry point (main.go, index.ts, mod.rs, …) |
+50 |
Well-known entry filenames (index.*, server.*) |
+30 |
| Each exported symbol | +1 |
| Each file that directly imports this one | +10 |
| Transitive fan-in bonus (deeply depended-on files) | additional score |
| Parser-backed non-Go call sites | +4 per caller file, capped |
| Path depth (per level) | -1 |
A file imported by five others, with twelve exported symbols, three levels deep scores roughly 5×10 + 12×1 − 3 = 59.
Every score mutation is also tracked as a score component. Use structured JSON or repomap explain <file> to see why a file ranked where it did:
repomap explain ranker.goranker.go
score: 123
detail: 2
components:
imports: +110
symbols: +3
transitive: +10
Transitive fan-in: files that sit deep in the import graph — depended on by many files indirectly — receive an additional score bonus proportional to their reachability. This keeps core library files visible even when only a few direct importers exist.
repomap resolves imports two ways:
- Go: matches import paths against the module root. A file in
pkg/authimported bycmd/apicreditspkg/authfor one reference. - Other languages: matches import strings (or
require,from,use) against basenames. It's approximate — you'll miss aliased imports and package re-exports — but it's fast and language-agnostic.
A file is an entry point if any of these match:
cmd/<name>/main.gomain.go,main.py,main.rs,main.ts,main.jsindex.ts,index.js,index.htmlserver.ts,app.py,Program.cs
Entries get +50, surface first, and render with a [entry] tag.
Within the budget, each file is assigned one of five detail levels:
| Level | What shows |
|---|---|
| -1 | Omitted (counted in the trailing summary) |
| 0 | Path only, with optional (package name) |
| 1 | Summary: 3 types, 7 funcs |
| 2 | Full symbol groups |
| 3 | Full symbols plus struct/interface field expansion |
Top-ranked files push toward level 3. Tails collapse to level 0 and fold into a single (+12 more: a.go, b.go, ...) line when they share no symbols worth showing.
When a file has exported symbols but zero importers, those symbols are marked dead. Dead exports cost half a budget unit instead of a full one — the file stays visible but compresses more aggressively. This keeps genuinely unused public API out of the way without hiding it entirely.
Per-language boundary scoring identifies natural module or package boundaries and factors them into the ranking. This works for: Go, TypeScript/JavaScript, Python, Rust, Java. Files that sit at package/module entry boundaries rank higher relative to files deep inside the same boundary.
In --calls mode, files with many callers receive an additional score bonus. The more places that call into a file, the higher it ranks — useful for surfacing heavily-used utilities that might otherwise score low due to few importers.
For tree-sitter-backed non-Go files, repomap extracts call-site expressions during parsing and boosts files whose exported symbols are called by other scanned files. This is stronger than lexical symbol references because it only counts parser-backed call expressions, but it is still not type-resolved: aliases, dynamic dispatch, and common method names can be missed or ambiguous. The score is capped so it cannot dominate import, intent, or exact Go LSP caller signals.
--symbol-refs adds a cheap approximate cross-language reference signal for non-Go symbols. It builds an exported symbol-name index, tokenizes each scanned file once, and boosts a non-Go file when other files mention its symbols.
This is lexical, not semantic: it ignores same-file mentions and duplicate mentions within one source file, skips very short names, and caps the score so false positives cannot dominate import, intent, or semantic caller signals. Use --calls for exact Go caller data.
repomap uses content-hash stale detection: a file whose mtime changed but whose content is unchanged does not trigger a rebuild. Only actual byte-level content changes invalidate the cache. This avoids spurious rebuilds caused by touch, git checkout, or filesystem metadata updates.
When you pass --intent "natural language query", repomap runs a BM25 pass over the parsed files before budget allocation. The query is tokenized and scored against three fields with different weights:
| Field | Weight | Source |
|---|---|---|
| Symbols | high | exported symbol names |
| Paths | medium | directory and filename components |
| Imports | low | import path components |
High-scoring files receive a score bonus that promotes them to higher detail levels within the same token budget. This is purely additive — it cannot demote files that would otherwise rank high.
repomap --intent "fix token refresh" .No external dependencies. No configuration. Omit the flag and behavior is identical to before.
repomap task "fix token refresh" .task uses its own bounded selector without changing the global ranker. It
matches goal terms against paths, packages, symbols, signatures, documentation,
and imports, then orders positive matches by task relevance, structural score,
and path. Structural centrality only breaks relevance ties.
The report selects at most six primary targets. If nothing has positive task
evidence, it returns structurally important non-test files with
confidence=fallback; it does not imply a goal match. Tests normally remain
relationships beneath their owners and become primary only when the goal
directly names their path or symbol or explicitly targets tests.
You can't tune ranking from the CLI. The weights are constants in ranker.go and budget.go. If you need different weights:
- Fork the module
- Edit the constants
- Vendor the fork
This is on purpose. A tunable ranker that nobody tunes the same way is worse than an opinionated one.
- Test files (
*_test.go,*.test.ts) — parsed but ranked lower - Generated code — scanned if present, ranked normally
- File size — not used as a signal
- Git history — not used
Ranking looks at the symbol graph, not at the history. A two-day-old file with heavy imports beats a five-year-old helper.