How Continue Indexes Your Codebase — and Whether Your Code Leaves Your Machine thumbnail
TeardownJul 2, 2026Developer tools

How Continue Indexes Your Codebase — and Whether Your Code Leaves Your Machine

Continue is the embedding-first counterpart to Aider. Read at v2.1.0, the answer to the question everyone actually asks — does my proprietary code get shipped to an embeddings API? — is: not by default. It embeds locally with a bundled all-MiniLM-L6-v2, stores vectors in a local LanceDB, and only sends code out if you configure a remote embeddings provider.

By ArcYou Editorial. Source read against the pinned repository states listed in Methodology.

Decision brief

What this source read clarifies

What it really is
A coding tool that finds relevant code through local indexing and embedding retrieval
Best for
Workflows that attach semantically nearby code chunks for each question
Avoid if
You do not want to operate an embedding index and local vector store
Core mechanism
local MiniLM embeddings, LanceDB storage, retrieval before chat

Read at: continue-v2.1.0-vscode @ v2.1.0-vscode

If Aider’s repo map is the static-analysis camp — parse the code, rank it by graph centrality, no model in the loop — Continue is the other camp: it chunks your repo, embeds the chunks, and retrieves by similarity. Which raises the question every decision-maker actually asks before pointing it at a private monorepo: does my proprietary code get shipped to an embeddings API? I read Continue at v2.1.0, commit b238c1f, and the honest answer is: not by default. Every claim below points at a line.

The short version

By default, on VS Code, Continue embeds your codebase locally — a bundled all-MiniLM-L6-v2 running in-process through transformers.js — and stores the vectors in a local LanceDB on your disk. Nothing about your code goes over the network. That changes the moment you configure a remote embeddings provider (OpenAI, Voyage, and friends): then the chunk text — and your search queries — get POSTed to that provider. The privacy surface isn’t “Continue”; it’s which embeddings provider you pick.

Does my code leave my machine?

This is the load-bearing question, so let me answer it from the code before anything else.

When you don’t set an embeddings provider, Continue’s config loader falls back to the built-in one. On VS Code that’s explicit: if the embeddings config is empty, it returns new TransformersJsEmbeddingsProvider() (load.ts:424; the type doc says as much at index.d.ts:1798). That provider is all-MiniLM-L6-v2 (TransformersJsEmbeddingsProvider.ts:44), and it goes out of its way to stay offline:

env.allowLocalModels = true;
env.allowRemoteModels = false;   // vendored default is TRUE (huggingface.co)
env.localModelPath = .../models;

That’s TransformersJsEmbeddingsProvider.ts:20. The vendored transformers.js library defaults allowRemoteModels to true pointed at huggingface.co (env.js:98); Continue deliberately flips it off. The ONNX weights are packaged into the extension itself (prepackage.js:470), so this isn’t even “download the model once” — the weights ride along in the VSIX, and inference is a local ONNX call (TransformersJsEmbeddingsProvider.ts:58).

Now flip the switch. The indexer embeds by calling embeddingsProvider.embed(chunks.map((c) => c.content)) — raw chunk text — at LanceDbIndex.ts:203. For a remote provider, .embed() is an HTTP POST: OpenAI’s _embed sends { input: chunks } to <apiBase>/embeddings (OpenAI.ts:717), and Voyage extends OpenAI with apiBase: "https://api.voyageai.com/v1/" (Voyage.ts:19). At retrieval time your query is embedded the same way (LanceDbIndex.ts:451) — so a remote provider sees your questions too, not just your code.1

So the precise mental model: local by default, and a per-provider decision to send anything out. No embeddings config → nothing leaves. A remote embeddings provider → your chunks and queries leave, to that endpoint.

Continue's default local embedding path compared with a configured remote embeddings provider

Continue’s default VS Code path embeds and stores chunks locally; the privacy boundary moves only when you configure a remote embeddings provider.

The pipeline: one walk, several indexes

Continue doesn’t keep just a vector index. Over a single walk of each directory (CodebaseIndexer.ts:380) it builds up to three content indexes, then runs them sequentially — with the blunt comment not parallelizing to avoid race conditions in sqlite (CodebaseIndexer.ts:198). Which indexes actually build is gated on which context providers you have enabled (CodebaseIndexer.ts:169):

Index What it holds Where
Chunks → embeddings tree-sitter code chunks, vectorized, in LanceDB LanceDbIndex.ts:265
Full-text SQLite FTS5, trigram tokenizer, BM25-ranked FullTextSearchCodebaseIndex.ts:31
Code snippets tree-sitter signatures (a symbol index) CodeSnippetsIndex.ts:186

The chunker is language-aware: for a supported code file it walks the tree-sitter AST and emits “smart collapsed” chunks — whole functions and classes when they fit the budget, collapsed to signatures when they don’t — falling back to a plain line accumulator otherwise (chunk.ts:37, code.ts:246). Chunk size is pinned to the embedding model’s maxEmbeddingChunkSize (CodebaseIndexer.ts:184). Note the interesting bit: the vector index and the full-text index are the same tree-sitter chunks, one embedded and one indexed for keyword search.

Why re-indexing is cheap: content hashing and branch reuse

Continue is content-addressed. Each file’s cache key is the SHA-256 of its contents (refreshIndex.ts:371). On refresh, every file that looks new is checked against a global cache keyed on (cacheKey, artifactId); if that exact content was already embedded under any tag, it’s an addTag — reuse, no recompute — otherwise it’s a compute (refreshIndex.ts:412).

The part worth knowing: an index tag is { directory, branch, artifactId } (index.d.ts:802), but the global-cache lookup keys on (cacheKey, artifactId) and not on branch (refreshIndex.ts:359). So switching git branches doesn’t re-embed unchanged files — identical content is embedded once and shared across branches by tag. You pay the embedding cost for changed content only, which is exactly why a big first index is slow and every refresh after is fast.2

Retrieval: vector, but not only vector

A query doesn’t just hit the vector store. Both retrieval pipelines fan out to four sources and merge them: embeddings similarity over LanceDB, full-text search over the FTS5 index, recently-edited files, and a repo-map file request (RerankerRetrievalPipeline.ts:22). The results are deduped by file-and-line-range (util.ts:4). If you have a reranker configured, it reranks the candidates and slices to the top N (RerankerRetrievalPipeline.ts:115); if not, it blends the pools by fixed weights — embeddings ½, full-text ¼, recently-edited ¼ (NoRerankerRetrievalPipeline.ts:17). The target size is small: DEFAULT_N_FINAL = 25, shrunk further for small context windows (retrieval.ts:9).

Here’s a detail that ties the two camps together. That fourth source — the “repo-map file request” — asks an LLM to pick 5–10 relevant files out of a generated repo map (repoMapRequest.ts:34). Continue has a repo map too — but where Aider’s repo map is the mechanism (a deterministic PageRank), Continue’s is one signal among four, and it’s the model, not a graph algorithm, doing the picking.

Where does this fit?

Back to the axis: how does the agent decide what code the model sees?

Aider (static-analysis-first) Continue (embedding-first)
Relevance signal graph centrality + your mentions vector similarity + FTS + rerank
Determinism deterministic, recomputable by hand depends on an embedding model
Infra none — just parsing an index (LanceDB + SQLite) that must be built and refreshed
“Find by meaning” no — you name things yes — that’s the point
Privacy surface nothing leaves nothing by default; your code leaves if you pick a remote provider

Neither is strictly better; they fail differently. Embedding-first can find “the function that parses dates” when you can’t remember its name — the thing a reference graph structurally cannot do. It pays for that with an index to maintain, an embedding model to run, and a privacy decision to make. Static-analysis-first has none of those costs and none of that recall.

When would you reach for it?

Continue’s indexing is a good fit if you want semantic retrieval over your codebase and you’re comfortable running a local model — which, on the default VS Code setup, keeps everything on your machine, making it a reasonable choice even for proprietary code. Go in clear-eyed about two things: you’re maintaining a background index (first build is the expensive one; refreshes are cheap by design), and the moment you switch to a remote embeddings provider for better recall, your chunks and queries start leaving the machine — so that choice is the one to make deliberately, not by copy-pasting a config.

Methodology & scope

I read Continue v2.1.0 at commit b238c1f, focused on core/indexing/, core/context/retrieval/, the config loaders, and the embeddings providers. Three headline claims — local-by-default embeddings (code leaves only via a configured remote provider), the SHA-256 content-hash tag reuse across branches, and the one-walk / multi-index / vector-plus-FTS-plus-rerank retrieval — were each run through an adversarial claim-verifier at this commit and confirmed. Line numbers are exact there.

Scope caveats, stated plainly:

  • I confirmed the model is bundled and loaded locally from the packaging script and the runtime flags, not by opening the binary weights in the git tree (they’re produced at package time, not committed).
  • Which indexes build depends on which context providers you enable; the embeddings index only builds if something requests it (CodebaseIndexer.ts:169). I described the full set.
  • I audited the indexing and embeddings paths for where code goes, not Continue’s entire telemetry layer end-to-end.

Footnotes

  1. One honest nuance so I don’t overstate “nothing leaves”: the ONNX wasm runtime binary can be fetched from a jsdelivr CDN when it isn’t found locally (env.js:57). That’s a runtime asset — the execution engine — not your code. Model weights are local and remote model fetch is disabled.

  2. The mirror case, for completeness: when a file’s content stops being referenced by its last remaining tag, it’s a del — the embedding is dropped, not kept forever. Content shared by more than one tag is only untagged (removeTag), not deleted.

Methodology

This post is read against the pinned repository states below. Directory names are convenience keys; the real pin is the commit SHA.

Repo keyVersionCommitUpstream
continue-v2.1.0-vscodev2.1.0-vscodeb238c1f11f26github.com/continuedev/continue

Related analysis

The decision axis is context ownership. Pre-compressing code, retrieving from a local index, or letting the model read files changes token cost and predictability.