Better Go To File is one TypeScript search product with two editor adapters:
- a VS Code/Cursor extension that runs the search runtime in the extension host;
- a dependency-free Neovim UI backed by the bundled Node.js/Bun sidecar.
This guide covers local setup, focused validation, performance work, packaging, and releases. The cross-editor architecture describes the runtime boundaries, while search ranking covers the algorithm.
- Bun matching the
packageManagerversion inpackage.json; - Git;
- VS Code or Cursor
1.97+to run the extension host; - Neovim
0.10+to work on the Lua adapter; - Node.js or Bun to execute a packaged Neovim engine.
Install the locked dependency graph:
bun install --frozen-lockfile| Path | Responsibility |
|---|---|
src/core/ |
Editor-neutral file/query models, search session, protocol, publication guard. |
src/search/ |
Ranking, lexical features, caches, learning, quality and benchmark helpers. |
src/workspace/ |
File/Git indexes, persistence, contributor history, package/root ownership. |
src/app/ |
VS Code/Cursor activation, commands, Quick Picks, views, and status UI. |
src/icons/ |
VS Code/Cursor icon-theme discovery and rendering. |
src/sidecar/ |
Persistent NDJSON engine and local filesystem/Git adapter. |
editors/neovim/ |
Native Lua client, picker, commands, help, and adapter tests. |
scripts/ |
Scoring CLI, synthetic benchmark, and Neovim release packaging. |
test/ |
Bun unit, integration, persistence, protocol, and ranking tests. |
.rulesync/ + rulesync.jsonc |
Sources for generated AI instructions and project skills. |
AGENTS.md, CLAUDE.md, .codex/skills/**, and .claude/skills/** are generated. Change their
source under .rulesync/, then run:
bun run rulesync:generate
bun run rulesync:checkCompile once:
bun run compileIn VS Code, press F5 and choose Run Better Go To File. The checked-in launch configuration
compiles first and opens an Extension Development Host for the current checkout.
For continuous TypeScript compilation:
bun run watchUseful runtime surfaces:
- Output → Better Go To File for index, icon, benchmark, and contributor diagnostics;
- Better Go To File: Open (debug) for per-row score summaries;
- Better Go To File: Index Health for cache and refresh state;
- Better Go To File: Inspect Icons for the active icon theme;
- Better Go To File: Benchmark This Workspace for the captured editor pipeline.
Run the complete headless Lua suite:
bun run test:neovimThe suite includes a real sidecar integration test. To try the source adapter manually, add
editors/neovim to runtimepath, ensure Bun is on PATH, and call
require("better_go_to_file").setup(...); see the
Neovim guide.
Use the smallest command that answers the question while iterating:
| Command | Coverage |
|---|---|
bun run compile |
TypeScript project references. |
bun test |
TypeScript unit and integration tests. |
bun run test:neovim |
Headless Lua and real-sidecar adapter tests. |
bun run lint |
Oxc lint rules. |
bun run fmt:check |
Oxfmt formatting without writes. |
bun run fmt |
Apply repository formatting. |
bun run knip |
Unused TypeScript files, exports, and dependencies. |
bun run bench:search --assert |
Synthetic scorer latency against padded CI budgets. |
bun run bundle |
Production VS Code and sidecar bundles with linked sourcemaps. |
bun run bundle:check |
Node syntax-check of both generated bundles. |
bun run rulesync:check |
Generated AI instructions match their source. |
Before handing off a substantive change, run the same aggregate command as CI:
bun run checkIt runs Rulesync verification, formatting, lint, TypeScript and Neovim tests, the performance budget, compilation, production bundles, bundle syntax checks, and Knip.
Run the production scorer against a local repository:
bun run score:search -- --repo /path/to/repository --limit 10 queryCompiler
bun run score:search -- --repo /path/to/repository --debug queryCompiler
bun run score:explain -- --repo /path/to/repository queryCompiler src/queryCompiler.tsThe CLI accepts presets, custom preset JSON, active/open paths, ignored-file visibility, local
frecency, and contributor identity overrides. Run bun run score -- --help for its route list and
bun run score:search -- --help for search flags.
When changing ranking:
- add focused lexical or context regressions under
test/; - add a labeled
evaluateSearchQualityscenario when the intended result can be expressed as a relevance expectation; - compare full-prefix quality and volatility, not just the final query;
- run
bun run bench:search --assert; - test a real large repository with the scoring CLI and the in-editor benchmark.
The synthetic benchmark warms the scorer and measures the algorithm, not extension activation, filesystem discovery, icon loading, or UI rendering. Use the in-editor command for those broader costs.
Search computation and result publication are deliberately separate.
- One initial picker revision exists when the UI opens.
- Every actual input change advances to a new monotonically increasing revision.
- A revision may publish at most once.
- A newer revision invalidates older queued work.
- Background refreshes do not publish rows on their own.
- The Neovim protocol keeps only the latest pending search request.
Any change to the picker, sidecar, icons, index events, or learning should retain that contract. Add regression coverage for stale timers/responses, duplicate responses, acceptance while a new revision is pending, and disposal during asynchronous work.
src/core/editor-protocol.ts is the compatibility boundary between a host adapter and the engine.
Messages are newline-delimited JSON over stdin/stdout. Keep stdout protocol-only; diagnostics
belong on stderr or in the host log.
Protocol changes should:
- update the typed parser and serializer contract;
- reject malformed, oversized, stale, or version-incompatible input explicitly;
- add parser and server tests;
- update the Neovim client and integration tests in the same change;
- bump the exported protocol or engine version when compatibility changes.
An adapter should send canonical absolute roots, active/open paths, an explicit search revision, and bounded configuration. It should treat results as a complete immutable replacement for that revision, not as patches.
Build both production JavaScript entries:
bun run bundle
bun run bundle:checkThis produces:
out/extension.jsand its linked sourcemap for VS Code/Cursor;out/bgf-engine.jsand its linked sourcemap for editor sidecars.
Create the Neovim release bundle:
bun run package:neovimThe output is:
dist/release/better-go-to-file-neovim.tar.gz
The archive contains one top-level better-go-to-file.nvim/ directory with lua/, plugin/,
doc/, the bundled engine, its sourcemap, engine.json, README, and license.
To inspect the VS Code package locally:
bunx vsce package --no-dependenciesThe .vscodeignore allowlist is intentionally strict: the VSIX includes the extension manifest,
README/license/assets, the production extension bundle and sourcemap, plus the runtime
opentype.js files needed for font icon themes. The Neovim source tree and development sources do
not ship in the VSIX.
Releases are automated from master:
- install the locked Bun dependencies;
- install the pinned Neovim used by CI;
- run
bun run check; - build
better-go-to-file-neovim.tar.gz; - let semantic-release determine the next version from Conventional Commits;
- package and publish the VSIX to the Visual Studio Marketplace;
- create the GitHub release with the VSIX and Neovim archive.
The default semantic-release commit analysis recognizes fix: as a patch, feat: as a minor, and
a Conventional Commits breaking-change marker as a major release. Documentation and maintenance
commits do not publish unless semantic-release determines that they require a version.
Publishing needs the repository's GitHub token and Visual Studio Marketplace token. Do not put tokens in local configuration, documentation examples, logs, or generated bundles.