find-refs
v0.1.1
Published
Exhaustive, position-based cross-file find-references via the language server (TypeScript/JavaScript + Python). Waits for project load before answering, so a cold run never silently undercounts.
Maintainers
Readme
find-refs
Exhaustive, exact cross-file find-references for a symbol, driven by the real language server — not a textual heuristic. You give it a file and a 1-based caret position; it returns every reference to the symbol under that caret, across the whole project.
Supported languages:
- TypeScript / JavaScript (
.ts .tsx .js .jsx .mts .cts) viatypescript-language-server - Python (
.py .pyi) viabasedpyright
The language servers and the TypeScript compiler are bundled as dependencies
of this package. npm install reproduces a working toolchain resolved from this
package's own node_modules — independent of the global engine install and the
node version in use, so it survives a node version bump and is transferable to
another machine. (It still needs a node on PATH: the bundled servers run on
Node — their shebang is #!/usr/bin/env node.)
Why this exists
The off-the-shelf options for a scriptable "find all references" all have a gap:
lsp-cliis archived, and it returns cold results — it queries the server before project indexing finishes, so it can confidently undercount.- MCP-only language tools can't be called from a plain shell / script.
- SCIP indexes are great for storage but ship no references-query CLI — you'd build the query layer yourself.
- ctags / grep-ast / ripgrep are textual: they match by name, so they miss aliased imports and renamed re-exports, and they over-match unrelated symbols that share a name.
This tool is position-based (it asks the server about the symbol at an exact caret, not a name string) and it waits for the project to finish loading before it trusts the answer — closing the cold-undercount hole that the naive approaches share.
Install
npm install -g find-refs # puts `find-refs` on your PATHOr run it without installing:
npx find-refs <file> <line> <col>Or from source:
git clone https://github.com/rahimi/find-refs.git
cd find-refs && npm install && npm install -g .Usage
find-refs <file> <line> <col>lineandcolare 1-based.colis the caret on the symbol (point it at the identifier you care about). Most editors showline:colin the status bar; pointing at the symbol's declaration works well. You can query from any reference too — the result is the same set.
Output:
- stdout — one reference per line, as
path:line:col(paths relative to the detected project root, sorted). - stderr — a one-line summary:
N references in M files via SERVER (ready=…, Xs)and, if relevant, an environment warning.
Example:
$ find-refs src/concierge/state.ts 180 14
src/concierge/state.ts:180:14
...
# stderr: 87 references in 10 files via typescript-language-server (ready=progress, 4.2s)The project root is auto-detected by walking up for tsconfig.json /
package.json (TS/JS) or pyrightconfig.json / pyproject.toml / setup.py /
setup.cfg (Python), falling back to the nearest .git then the file's
directory.
Behavior notes
The readiness gate prevents cold undercount. Before reading references, the
tool waits for the server's project-load signal — a $/progress begin→end
cycle. For tsserver this means advertising
capabilities.window.workDoneProgress and answering the server's
window/workDoneProgress/create request, so the "Initializing" progress fires;
for Python it settles and falls back if no progress is reported. It then polls
until the reference count is stable across two consecutive queries. The summary's
ready= field tells you which path was taken (progress, no-progress, or
timeout).
The env-guard keeps a partial result from lying. References silently
undercount when the project can't resolve its own imports (an uninstalled repo).
When the environment looks absent — no node_modules for a JS/TS project, no
.venv/venv/.tox/$VIRTUAL_ENV for Python — the tool prints a ⚠ warning to
stderr. It never blocks; a normal installed project produces no warning, so
the warning's presence is meaningful.
TypeScript lib resolution. For tsserver, the tool prefers the target
repo's node_modules/typescript/lib/tsserver.js (so it analyzes with the
project's own TypeScript version), and falls back to this package's bundled
typescript otherwise.
Adding a language
The dispatch lives in one place in find-refs.mjs (the ext === … block).
To add a language:
- Add the language server as a dependency in
package.json(so it lands innode_modules/.binand is bundled). - Add an extension branch that sets
ROOT(root-marker detection),SERVER(resolveServer("<bin-name>")),SERVER_NAME, and the LSPLANGID. - If the server needs an env to resolve imports, extend the env-health guard so a partial result still warns.
Everything else — the LSP handshake, the readiness gate, the references query, and the output format — is language-agnostic and unchanged.
Using it with an AI coding agent (Claude Code)
find-refs is built to be an agent tool: it gives the agent exact references
where grep is a fuzzy proxy that can silently miss a call site. Three small bits
of wiring make an agent actually reach for it.
1. Allowlist it so it never prompts — in ~/.claude/settings.json:
{ "permissions": { "allow": ["Bash(find-refs *)"] } }2. Make the agent aware it exists — a line in your CLAUDE.md:
find-refs <file> <line> <col>returns a symbol's exact cross-file references (TS/JS + Python) — exhaustive where grep is fuzzy, which misses call sites.
3. Nudge at the moment of reach (optional, the strongest anchor) — a
PreToolUse hook on the Grep tool. When the agent greps a bare identifier
inside a code repo, the hook injects a one-line "prefer find-refs for exact
references" note via hookSpecificOutput.additionalContext (exit 0 — it never
blocks the grep). Keep it smart so it isn't noisy: only fire on identifier-shaped
patterns, gate to repos with a tsconfig.json / package.json /
pyproject.toml, and debounce per-symbol per session. Register it:
{ "hooks": { "PreToolUse": [
{ "matcher": "Grep", "hooks": [
{ "type": "command", "command": "~/.claude/hooks/grep-to-nav.sh" } ] } ] } }License
MIT — see LICENSE.
