IntentNav
Navigate your codebase by intent, not by filename.
Developers think in intents — "where is payment failing?", "show me the retry logic", "where do we validate JWT tokens?" — not in filenames. IntentNav turns that natural-language intent directly into ranked, clickable code locations, right inside VS Code.
Traditional: IntentNav:
Ctrl+P Cmd/Ctrl + Shift + I
→ login.ts → "Where is the login flow handled?"
→ search manually → ranked, explained results
→ inspect code → Enter
→ search callers → exact code, in context
It is not a chatbot. There's no conversation, no generic Q&A panel. You type
an intent, you get a ranked list of real code locations with a one-line reason
for each, and pressing Enter takes you straight there.
Table of contents
Example queries
- "Where is payment failing?"
- "Show API retry logic"
- "Where is authentication handled?"
- "Where do we validate JWT tokens?"
- "Find checkout validation"
- "Where is this error thrown?" (with an error line selected)
- "Where do we call Stripe?"
Works with zero configuration
IntentNav's MVP requires no API key, no cloud service, and no internet
connection. It is built entirely on:
- VS Code's native language services (
executeDocumentSymbolProvider) for accurate symbols
- A dependency-free regex-based fallback analyzer for languages without a rich language server
- Deterministic, dictionary-based intent classification and query expansion
- A local, explainable, multi-signal ranking model
External AI providers (Anthropic, OpenAI, Azure OpenAI, Ollama) are entirely optional and
off by default. Enabling one only ever adds a short natural-language
explanation on top of results that local ranking already found, or an
optional semantic-similarity signal — it never replaces the core pipeline.
Architecture
IntentNav is explicitly not search query → ripgrep → results. It's a
layered retrieval pipeline that always runs multiple signals in parallel and
combines them into one explainable score:
┌─────────────────┐
raw query → │ QueryParser │ deterministic intent classification +
│ (IntentProvider) │ concept-map term expansion
└────────┬─────────┘
│ QueryAnalysis
▼
┌─────────────────────────────┐
│ RetrievalPipeline │
│ (SemanticSearchProvider) │
│ │
│ for every indexed symbol: │
│ • lexical / content match │
│ • symbol-name match │
│ • path / folder match │
│ • error-handling patterns │
│ • import relationships │
│ • call relationships │
│ • recency / context match │
│ • (optional) semantic sim. │
└────────┬─────────────────────┘
│ scored candidates
▼
┌─────────────────┐
│ Ranker │ weighted sum → sorted SearchResult[]
└────────┬─────────┘
│
▼
┌─────────────────┐
│ IntentSearch │ QuickPick UI: preview / open / related /
│ (ui layer) │ copy / explain
└──────────────────┘
How ranking works
Every candidate (a function, method, class, or whole file) gets a score:
score = semanticScore * semanticWeight (default 0.35)
+ symbolScore * symbolWeight (default 0.20)
+ contentScore * contentWeight (default 0.15)
+ pathScore * pathWeight (default 0.10)
+ errorHandlingScore * errorHandlingWeight (0.08)
+ importRelationshipScore * importRelWeight (0.05)
+ callRelationshipScore * callRelWeight (0.05)
+ recencyOrContextScore * recencyWeight (0.02)
- semanticScore — cosine similarity from an optional embedding provider;
0 when none is configured (the default).
- symbolScore / contentScore / pathScore — term-overlap between the (concept-expanded) query and the symbol name / comments+strings / file path, with light stemming so "failing", "failed", "fails" all match "fail".
- errorHandlingScore — only active for error/debugging-flavored queries; rewards
try/catch/throw patterns found via VS Code symbol ranges or the regex fallback.
- importRelationshipScore / callRelationshipScore — reward code that's actually wired into the rest of the codebase over orphaned code.
- recencyOrContextScore — boosts your current selection/file, with a small recency fallback otherwise.
All eight weights are configurable (intentNav.semanticWeight, intentNav.symbolWeight, etc.), and every score is explainable: pick "Explain result" (or the lightbulb button) and IntentNav tells you exactly which signals fired, in plain English — never a black box.
Query understanding without an LLM
QueryParser deterministically:
- Tokenizes and strips stop-words from the query.
- Splits identifier-style words (
camelCase, snake_case).
- Expands each domain term via a concept dictionary — e.g.
payment → charge, transaction, checkout, refund, stripe, ... and failing → error, exception, catch, throw, declined, ...
- Classifies the query into an intent category (debugging, error-origin, error-handling, validation, integration, workflow-feature, symbol-lookup, ...) using hint phrases ("thrown" vs "handled", etc.).
This is what lets "Where is payment failing?" find code that never contains the word "failing" at all — code that throws PaymentError, catches provider exceptions, or returns a declined status.
Indexing is designed to stay out of your way, even in large repositories:
- Batched, async, non-blocking. Files are indexed in small concurrent batches with progress reported via VS Code's window progress indicator — the UI thread is never blocked on a big scan.
- Incremental after the first pass. File watchers (create/change/delete) plus
onDidSaveTextDocument feed a 400ms-debounced queue, so only the handful of files you actually touched get re-indexed — never the whole workspace.
- Warm-started from an on-disk cache.
IndexCache persists the index to VS Code's per-workspace storage folder after every rebuild (debounced, so rapid saves don't each trigger a disk write). On the next window open, that cached snapshot is loaded and made searchable immediately, while a full re-index runs in the background to reconcile it with whatever changed since the last session — so a reopened large repo doesn't start from zero.
- Never destructively cleared mid-scan. A rebuild upserts files as they're (re)discovered and only removes entries for files that turn out to be genuinely gone, so search results stay available throughout a rebuild instead of going blank.
Privacy
Privacy is a first-class feature, not a settings-page footnote:
- By default, your source code never leaves your machine. Indexing, search, ranking, and snippet extraction are 100% local.
- The on-disk index cache (symbol names, extracted comments/strings, file paths — derived data, not full file contents) is written only to VS Code's own per-workspace storage folder on your machine, never synced or uploaded anywhere.
- No telemetry ships source code, ever —
intentNav.enableTelemetry (off by default) would only ever cover anonymous, code-free usage counters, and no such telemetry is currently implemented.
- Enabling an external provider (
intentNav.provider: "anthropic", "openai", or "azure-openai", plus intentNav.enableAI: true) sends only the already-extracted snippet (capped) and your query for a single explanation call — never the full file, never the repository.
- API keys are stored via VS Code's
SecretStorage, never written into workspace files or logged.
Supported languages
Symbols come from VS Code's built-in executeDocumentSymbolProvider wherever a language server is available, so accuracy scales with whatever extensions you have installed. When no provider is available (or returns nothing), IntentNav falls back to its own regex-based analyzer, which has explicit support for:
TypeScript · TSX · JavaScript · JSX · Python · Java · Go · Rust · C# · C/C++
Adding another language means adding one entry to SUPPORTED_EXTENSIONS (src/utils/ignore.ts) and, optionally, a fallback symbol pattern in src/index/CodeAnalysis.ts — no rewrite required.
Commands
Every command has a default keybinding — all are remappable via VS Code's Keyboard Shortcuts editor (Preferences: Open Keyboard Shortcuts) if any combination conflicts with another extension you have installed.
| Command |
Default keybinding |
Description |
IntentNav: Navigate by Intent |
Ctrl/Cmd+Shift+I |
Main entry point — the live QuickPick search. |
IntentNav: Search Workspace by Intent |
Ctrl/Cmd+Shift+Alt+I |
Same search via a plain input box (no live QuickPick). |
IntentNav: Show Related Code |
Ctrl/Cmd+Shift+R |
Callers, dependencies, and tests for the symbol at your cursor. Also available by right-clicking inside the editor. |
IntentNav: Rebuild Index |
Ctrl/Cmd+Shift+Alt+B |
Force a full re-index. |
IntentNav: Open Settings |
Ctrl/Cmd+Shift+Alt+S |
Jump to intentNav.* settings. |
IntentNav: Explain Result |
Ctrl/Cmd+Shift+Alt+E |
Explain the symbol at your cursor. |
IntentNav: Set AI Provider API Key |
Ctrl/Cmd+Shift+Alt+K |
Securely store the API key for the currently configured intentNav.provider (Anthropic/OpenAI/Azure OpenAI), via VS Code SecretStorage. |
Configuration
| Setting |
Default |
Description |
intentNav.maxResults |
5 |
Results shown before "Show more". |
intentNav.enableAI |
false |
Allow external AI providers. |
intentNav.provider |
"local" |
local | anthropic | openai | azure-openai | ollama. |
intentNav.azureOpenAIEndpoint |
"" |
Your Azure OpenAI resource endpoint, e.g. https://my-resource.openai.azure.com. Only used when intentNav.provider is "azure-openai". |
intentNav.azureOpenAIDeploymentName |
"" |
The deployment name of your Azure OpenAI model. Only used when intentNav.provider is "azure-openai". |
intentNav.azureOpenAIApiVersion |
"2024-06-01" |
Azure OpenAI REST API version to call. Only used when intentNav.provider is "azure-openai". |
intentNav.indexIgnoredPaths |
[] |
Extra paths/globs to exclude from indexing. |
intentNav.semanticWeight |
0.35 |
Weight of semantic similarity (needs a provider). |
intentNav.symbolWeight |
0.20 |
Weight of symbol-name relevance. |
intentNav.contentWeight |
0.15 |
Weight of comment/string relevance. |
intentNav.pathWeight |
0.10 |
Weight of file-path relevance. |
intentNav.enableTelemetry |
false |
Anonymous, code-free usage telemetry. |
Using an external AI provider
All external providers are optional and off by default (see Privacy). To enable one:
- Set
intentNav.provider to "anthropic", "openai", or "azure-openai", and intentNav.enableAI to true.
- For Azure OpenAI specifically, also set
intentNav.azureOpenAIEndpoint (your resource endpoint, e.g. https://my-resource.openai.azure.com) and intentNav.azureOpenAIDeploymentName (the deployment name you created in Azure AI Studio / Azure OpenAI Studio) — these aren't secrets, so they're plain settings rather than something the API-key command asks for.
- Run
IntentNav: Set AI Provider API Key (Ctrl/Cmd+Shift+Alt+K) and paste your key. It's stored via VS Code's SecretStorage, never in a settings file.
If a key or required setting is missing, or the request fails for any reason, IntentNav silently falls back to the local, offline explanation — enabling AI can never make results disappear, only add a short summary on top of what local ranking already found.
Installation
git clone https://github.com/AnandShah10/intentnav.git
cd intentnav
npm install
npm run compile
Then press F5 in VS Code to launch an Extension Development Host with IntentNav loaded, open any real repository in that window, and press Ctrl/Cmd+Shift+I.
Development
src/
extension.ts activation: wires everything together
commands/ command registration + status bar
search/
QueryParser.ts deterministic intent classification
Ranker.ts weighted, explainable scoring model
RetrievalPipeline.ts lexical + symbol + structural + semantic retrieval
IntentEngine.ts top-level facade used by the UI
index/
WorkspaceIndexer.ts full + incremental indexing, file watchers
SymbolIndexer.ts per-file symbol extraction (VS Code API + fallback)
FileIndexer.ts file discovery, respecting ignore rules
IndexStore.ts in-memory index + import/caller lookups
IndexCache.ts on-disk cache for instant warm-start on reopen
CodeAnalysis.ts dependency-free regex analyzer (imports/exports/
comments/strings/fallback symbols)
providers/
LocalIntentProvider.ts, LocalSearchProvider.ts default, offline
EmbeddingProvider.ts Noop (default) + Ollama scaffold
AIProvider.ts Local (default) + optional Anthropic explanation
navigation/
Navigator.ts open/reveal in the editor
RelatedCode.ts callers, dependencies, tests, sibling symbols
SnippetExtractor.ts smart logical-region extraction
ui/
IntentSearch.ts the QuickPick experience
ResultRenderer.ts formatting
config/Settings.ts typed `intentNav.*` settings wrapper
utils/ pure, dependency-free helpers (heavily unit tested)
types/ shared interfaces (no vscode dependency)
Running tests
npm test
Unit tests cover QueryParser (intent classification across the example
queries from the spec), Ranker (score ordering, error-handling gating,
explanation generation, and configurable weights), and matchesContext
(precise selection/file/symbol context matching) — all pure, vscode-free
modules that run under plain Vitest with no editor instance required.
npm run test:integration
Runs the integration suite (test/suite/) inside a real, downloaded VS Code
instance via @vscode/test-electron, with the fixture project at
test/fixtures/sample-project/ opened as the workspace. This exercises the
vscode-dependent code paths the unit tests can't reach:
indexer.test.ts — file discovery and ignore rules, symbol extraction and kinds, import-relationship detection, incremental re-indexing after a save
navigation.test.ts — opening the correct file/line, clamping out-of-range lines instead of throwing, clipboard copy format
edgeCases.test.ts — empty/whitespace queries, no-match queries, binary files never indexed, index cleanup after file deletion, a malformed file not corrupting the rest of the index, rebuildIndex completing without throwing
The first run downloads a VS Code test binary (cached afterward), so it
needs normal internet access and takes longer than the unit suite.
Packaging as a VSIX
npm install -g @vscode/vsce
npm run compile
vsce package
This produces intentnav-0.1.0.vsix, installable via Extensions: Install from VSIX... in VS Code.
Known limitations
- The regex-based fallback symbol extractor (used only when no VS Code symbol provider is available) uses brace/indentation counting and can occasionally mis-detect the end of a function whose signature contains an inline object type literal (e.g.
function f(): { x: string } { ... }) — the primary executeDocumentSymbolProvider path does not have this issue.
- "Call relationship" scoring is name-based (does it appear referenced elsewhere), not a true call graph — accurate call graphs generally require full type information per language.
- Semantic search only activates when an embedding provider is explicitly configured; the default local mode is lexical/symbol/structural only.
- The on-disk cache (see below) speeds up the next window open, but the very first index of a brand-new workspace is always a full scan — there's nothing to warm-start from yet.
Roadmap
The architecture was built so these can be added without a rewrite:
- Real embedding-based semantic search (local model or cloud), wired through the existing
EmbeddingProvider interface
- Git history awareness ("what changed here recently and why")
- Stack-trace-driven navigation ("paste this stack trace, jump to the failure")
- "Why is this failing?" / "Where should I fix this?" — reasoning on top of the same retrieval pipeline
- Cross-repository and team-shared indexes
- Personalized ranking based on your own navigation history
Contributing
Contributions are very welcome — see CONTRIBUTING.md for the dev workflow, coding conventions, and how to submit a PR. Please also review our CODE_OF_CONDUCT.md.
Security
Found a security issue? Please don't open a public issue — see SECURITY.md for responsible disclosure instructions.
Changelog
See CHANGELOG.md for release history.
License
MIT © Anand Shah — see LICENSE for details.
Made with ❤️ by Anand Shah for the developer community