Continue.dev: The leading open-source AI code assistant
A VS Code and JetBrains extension providing code completion, chat, and agent capabilities with support for any LLM backend.
The Problem
You are a developer who wants AI-assisted coding but cannot use GitHub Copilot or Cursor. Maybe your employer bans sending code to third-party APIs. Maybe you work in defense, healthcare, or fintech where data residency requirements mean no cloud service can touch your source files. Maybe you just want to choose your own model — running a local Qwen2.5-Coder on Ollama for autocomplete and routing chat through Claude Sonnet 4.6 via Anthropic’s API.
The closed-source tools give you none of these options. Copilot locks you into Microsoft’s model pipeline. Cursor is a VS Code fork with its own proprietary backend. Both send your code to their cloud. Both cost $10-20 per user per month. Both give you zero control over which model runs which task.
| Dimension | GitHub Copilot | Cursor | Continue.dev |
|---|---|---|---|
| License | Proprietary | Proprietary | Apache 2.0 |
| IDE support | VS Code, JetBrains, Neovim | VS Code fork only | VS Code, JetBrains, CLI |
| Model choice | GPT-4o, Claude 3.5 Sonnet (opt-in) | Claude, GPT-4o, Gemini | Any of 40+ providers |
| Local models | No | No | Yes (Ollama, LM Studio, vLLM) |
| Air-gapped possible | No | No | Yes |
| Cost per user/mo | $10-39 | $20-40 | Free (BYO API keys) |
| Data leaves your machine | Yes | Yes | Configurable (zero with local models) |
| Cross-IDE config | No | N/A (single IDE) | Yes (single config file) |
| Custom assistants | No | Limited | Yes (rules + MCP servers) |
| MCP support | No | Yes | Yes |
Why this matters: If you cannot or will not send your code to a third-party cloud, your only option for AI-assisted coding is an open-source tool that runs locally. Continue.dev is that tool — and it is the most mature, most configurable, and most widely adopted open-source AI code assistant in existence, with 34,000+ GitHub stars and 400+ contributors.
The Investigation
The core problem is architectural: every closed-source AI coding tool is a vertically integrated stack. The IDE, the model, the context pipeline, and the telemetry are all owned by one vendor. You cannot swap the model. You cannot run it locally. You cannot audit what data leaves your machine.
Continue.dev solves this by inverting the architecture. Instead of a vendor-owned stack, it provides a pluggable LLM abstraction layer that decouples the IDE from the model provider. The same extension works with any backend that implements the ILLM interface — 40+ providers at launch, from Anthropic to Ollama to a custom OpenAI-compatible endpoint running on your laptop.
The root cause of the closed-source lock-in is the lack of a standardized, open protocol between IDEs and LLM backends. Every vendor builds their own. Continue.dev’s answer is a typed message-passing protocol (ToCoreProtocol, FromCoreProtocol, ToWebviewProtocol) that separates the IDE layer from the core orchestrator from the LLM provider. This is the same pattern that made HTTP successful: a standard interface enables interchangeable backends.
Finding 1: The LLM abstraction layer is the critical enabler.
The ILLM interface defines six methods that every provider must implement:
streamChat(messages, signal, options)— streaming chat for conversational interactionsstreamComplete(prompt, signal, options)— streaming text completionstreamFim(prefix, suffix, signal, options)— fill-in-the-middle for tab autocompletecomplete(prompt, signal, options)— synchronous completionchat(messages, signal, options)— non-streaming chatembed(chunks)— generate vector embeddings for codebase indexing
What this means: Any model that implements these six methods can serve as a drop-in replacement. You can use GPT-4o for chat, Qwen2.5-Coder for autocomplete, and a local embedding model for codebase search — all from the same extension, configured in a single YAML file.
Finding 2: The message-passing architecture enables cross-IDE support.
Continue.dev supports VS Code, JetBrains, and a standalone CLI from a single codebase. The key is that the IDE layer is thin — it handles platform-specific concerns (editor integration, keybindings, UI chrome) but delegates all business logic to the Core orchestrator via typed messages.
Webview (React GUI) <--> IDE Extension <--> Core Orchestrator
^ ^
| (pass-through) |
+------------------------------------+
What this means: The same ~/.continue/config.yaml works in VS Code and JetBrains. A team with Java backend developers in IntelliJ and React frontend developers in VS Code can share one configuration. This is unique — no other AI coding tool offers cross-IDE config portability.
Finding 3: The FIM-based autocomplete pipeline is model-agnostic but template-dependent.
Tab autocomplete uses Fill-in-the-Middle (FIM), where the model receives code before the cursor (prefix) and code after the cursor (suffix) and generates the middle. Different models use different FIM token templates:
| Model | FIM Template |
|---|---|
| Qwen2.5-Coder | `< |
| StarCoder2 | <fim_prefix>{{ prefix }}<fim_suffix>{{ suffix }}<fim_middle> |
| Codestral (Mistral) | [SUFFIX]{{ suffix }}[PREFIX]{{ prefix }}[MIDDLE] |
| DeepSeek Coder | <|fim▁begin|># <|fim▁hole|>e CompletionProvider` gathers prefix, suffix, and LSP context |
- The FIM template is applied with the configured model
- The model streams tokens back via
streamFim() - Ghost text is rendered inline in the editor
What this means: Autocomplete quality depends entirely on the FIM template matching the model’s training format. A mismatch produces garbage. The tabAutocompleteOptions.template field is the most critical — and most commonly misconfigured — setting in the entire system.
The Solution
Continue.dev’s architecture is a layered system with strict separation of concerns. Here is the high-level structure:
+----------------------------------------------------------+
| IDE Layer |
| VS Code Extension | JetBrains Plugin | CLI (TUI) |
+----------------------------------------------------------+
| Core Orchestrator |
| ConfigHandler | CompletionProvider | DocsService |
| CodebaseIndexer | NextEditProvider | MCPManager |
+----------------------------------------------------------+
| LLM Abstraction Layer |
| ILLM Interface -> BaseLLM -> Provider Implementations|
| (streamChat, streamFim, embed, countTokens, ...) |
+----------------------------------------------------------+
| External LLM Providers |
| OpenAI | Anthropic | Ollama | Gemini | ... |
+----------------------------------------------------------+
Here’s what each piece does:
-
IDE Layer: Thin platform-specific adapters that translate editor events (keystrokes, file opens, cursor movements) into typed messages. The VS Code extension and JetBrains plugin share ~90% of their code through the core library. The CLI provides a terminal-native TUI for headless environments.
-
Core Orchestrator: The
Coreclass incore/core.tsinitializes all services and registers ~80 message handlers. It manages abort controllers for request cancellation, coordinates service lifecycle, and handles configuration hot-reloads. Every user action — a chat message, a tab completion request, a codebase search — flows through this layer. -
ConfigHandler: Loads and merges configuration from multiple sources: local
~/.continue/config.yaml, workspace.continue/config.yaml, remote hub profiles, and organization-scoped settings. The merge order is: local defaults < workspace overrides < org policies < user session selections. -
CompletionProvider: Manages the tab autocomplete pipeline. It debounces keystrokes (default 350ms), assembles the FIM prompt from prefix/suffix/LSP context, sends the request to the configured autocomplete model, and renders the ghost text. It also manages a completion cache to avoid redundant requests.
-
CodebaseIndexer: Maintains vector embeddings for semantic code search. When you type
@codebasein chat, this service retrieves relevant files using cosine similarity against the indexed embeddings. The index is built incrementally — only changed files are re-embedded on save. -
MCPManagerSingleton: Manages connections to Model Context Protocol servers. Each MCP server exposes tools (e.g., GitHub API, database queries, browser automation) that the agent can invoke. Supports stdio, SSE, and Streamable HTTP transports.
-
LLM Abstraction Layer: The
ILLMinterface defines the contract.BaseLLMprovides shared functionality: token counting via Tiktoken or LlamaEncoding, message compilation with context pruning, streaming viaAsyncGenerator, exponential backoff retry logic, and prompt template rendering using Handlebars. Each provider (OpenAI, Anthropic, Ollama, etc.) extendsBaseLLMand implements the provider-specific API calls.
Production-Grade Configuration
Here is a complete ~/.continue/config.yaml that configures three models for different roles, custom rules, and MCP servers:
name: Production Config
version: 1.0.0
schema: v1
models:
# Chat model: Claude Sonnet 4.6 via Anthropic API
- name: Claude Sonnet
provider: anthropic
model: claude-sonnet-4-20260514
roles:
- chat
- edit
defaultCompletionOptions:
temperature: 0.7
maxTokens: 4096
# Autocomplete model: local Qwen2.5-Coder via Ollama
- name: Qwen Coder
provider: ollama
model: qwen2.5-coder:7b
roles:
- autocomplete
autocompleteOptions:
debounceDelay: 250
maxPromptTokens: 2048
onlyMyCode: true
multilineCompletions: auto
template: "<|fim_prefix|>{{{ prefix }}}<|fim_suffix|>{{{ suffix }}}<|fim_middle|>"
# Embedding model for codebase search
- name: Nomic Embed
provider: ollama
model: nomic-embed-text:v1.5
roles:
- embed
rules:
- Always use TypeScript strict mode
- Prefer functional components over class components in React
- All database queries must use parameterized statements
- Every public function must have a JSDoc comment
- Never use `any` — use `unknown` and narrow with type guards
context:
- provider: codebase
params:
maxResults: 10
- provider: diff
- provider: file
- provider: problems
mcpServers:
- name: GitHub
command: npx
args:
- "-y"
- "@modelcontextprotocol/server-github"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Browser search
command: npx
args:
- "@playwright/mcp@latest"
- name: Sentry
command: npx
args:
- "-y"
- "@sentry/mcp-server@latest"
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
Setup Instructions
Install the extension and configure your models:
# Install the VS Code extension from the marketplace
code --install-extension continue.continue
# Or install via OpenVSX (for VSCodium or air-gapped environments)
# Download the .vsix from https://github.com/continuedev/continue/releases
code --install-extension continue-2.1.0.vsix
# Install Ollama for local models
curl -fsSL https://ollama.com/install.sh | sh
# Pull your autocomplete model
ollama pull qwen2.5-coder:7b
# Pull your embedding model
ollama pull nomic-embed-text:v1.5
# Create the config directory
mkdir -p ~/.continue
# Create the rules directory
mkdir -p ~/.continue/rules
# Create the MCP servers directory
mkdir -p ~/.continue/mcpServers
# For the CLI (optional)
npm install -g @continuedev/cli
How to Use Effectively
Step 1: Configure Model Roles Correctly
The most common mistake is using the same model for chat and autocomplete. Chat models (Claude, GPT-4o) are trained for conversational interaction. Autocomplete models (Qwen2.5-Coder, StarCoder2, Codestral) are trained for FIM. Using a chat model for autocomplete produces slow, irrelevant suggestions.
# Correct: separate models for separate roles
models:
- name: Claude Sonnet
provider: anthropic
model: claude-sonnet-4-20260514
roles:
- chat
- edit
- name: Qwen Coder
provider: ollama
model: qwen2.5-coder:7b
roles:
- autocomplete
autocompleteOptions:
template: "<|fim_prefix|>{{{ prefix }}}<|fim_suffix|>{{{ suffix }}}<|fim_middle|>"
The roles field accepts chat, edit, autocomplete, and embed. A model can serve multiple roles, but autocomplete should always use a FIM-trained model.
Step 2: Write Effective Rules
Rules are injected into the system prompt for every chat, edit, and agent interaction. They shape the model’s behavior without requiring you to repeat instructions.
Place project-specific rules in .continue/rules/ (version-controlled with your codebase):
# .continue/rules/typescript-rules.md
- Use `interface` over `type` for object shapes
- Use `const` assertions for literal types: `as const`
- Prefer `zod` for runtime validation over class-validator
- All API routes must have input validation using zod schemas
- Error boundaries must be at every route segment
Place global rules in ~/.continue/rules/ (applied to every project):
# ~/.continue/rules/global-rules.md
- Write concise responses. Prefer code examples over explanations.
- Assume TypeScript unless otherwise specified.
- Never use `any`. Use `unknown` and narrow with type guards.
- All functions must have explicit return types.
Rules are concatenated in order: global rules first, then workspace rules, then inline rules from config.yaml.
Step 3: Use @ Mentions for Context Injection
The @ mention system lets you inject specific context into chat. This is how you tell the model what to look at without copying and pasting:
@file src/routes/users.ts— Include a specific file’s contents@codebase— Semantic search across the entire codebase@docs react— Include React documentation (pre-indexed)@terminal— Include the last terminal output@problems— Include current IDE diagnostics@git— Include git diff for the current branch
// Example: Ask a question with context
// In chat, type:
// @file src/services/auth.ts @codebase "Find all places where this auth
// service is called and refactor them to use the new v2 API"
The @codebase provider uses vector embeddings from your configured embedding model. It retrieves the top maxResults files (default 10) by cosine similarity. The index is built incrementally — only changed files are re-embedded on save, so the first query after a large checkout may be slow.
Step 4: Configure MCP Servers for Agent Mode
Agent mode can read/write files, run terminal commands, and invoke MCP server tools. MCP servers extend the agent’s capabilities to external systems:
mcpServers:
# Local stdio transport: runs a subprocess
- name: SQLite
type: stdio
command: npx
args:
- "@modelcontextprotocol/server-sqlite"
- "/path/to/database.db"
# Remote SSE transport: connects to a server-sent events endpoint
- name: Sentry
type: sse
url: https://mcp.sentry.io
# Streamable HTTP transport: bidirectional streaming
- name: Supabase
type: streamable-http
url: https://mcp.supabase.com/mcp
apiKey: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
Secrets are resolved from .env files. Place workspace secrets in .continue/.env and global secrets in ~/.continue/.env:
# ~/.continue/.env
GITHUB_TOKEN=ghp_your_token_here
SENTRY_AUTH_TOKEN=sntrys_your_token_here
SUPABASE_ACCESS_TOKEN=sb_your_token_here
Step 5: Tune Autocomplete for Your Workflow
The tabAutocompleteOptions block controls every aspect of the autocomplete pipeline:
autocompleteOptions:
disable: false # Set true to disable autocomplete entirely
debounceDelay: 250 # Milliseconds to wait after last keystroke
maxPromptTokens: 2048 # Max tokens for the FIM prompt
maxSuffixPercentage: 0.2 # Max % of prompt allocated to suffix
prefixPercentage: 0.3 # % of prompt allocated to prefix
onlyMyCode: true # Only include code within the repo
useCache: true # Cache completions for identical contexts
multilineCompletions: auto # "always", "never", or "auto"
disableInFiles: # Glob patterns to exclude
- "*.md"
- "*.json"
- "*.yaml"
template: "<|fim_prefix|>{{{ prefix }}}<|fim_suffix|>{{{ suffix }}}<|fim_middle|>"
Key tuning parameters:
-
debounceDelay: Lower values (100-150ms) feel more responsive but increase API calls. Higher values (350-500ms) batch more context but feel laggy. Start at 250ms and adjust based on your model’s latency.
-
maxPromptTokens: Larger values give the model more context but increase latency. For local models with 7B parameters, 1024-2048 is sufficient. For larger models (32B+), you can go up to 8192.
-
multilineCompletions: Set to
"always"if you want multi-line suggestions (e.g., function bodies). Set to"never"if you only want single-line completions."auto"lets the model decide based on context. -
template: This is the FIM template. It must match the model’s training format exactly. Using the wrong template produces garbage completions. Check the model’s documentation for the correct FIM tokens.
Use Cases
1. Air-Gapped Development
When you’d use this: You work in defense, healthcare, or finance where code cannot leave the local network. No cloud API is allowed. You need AI assistance that runs entirely on your machine.
Why Continue.dev fits: Run Ollama with Qwen2.5-Coder for autocomplete and a local embedding model for codebase search. Zero data leaves your machine. The entire stack — IDE extension, models, index — runs locally. No internet connection required after initial model download.
2. Multi-IDE Team Configuration
When you’d use this: Your team has Java backend developers using IntelliJ IDEA and React frontend developers using VS Code. You want a single AI configuration that works for everyone.
Why Continue.dev fits: The same ~/.continue/config.yaml works in both IDEs. The JetBrains plugin and VS Code extension share the same core library. Configure once, deploy to the team via a shared dotfiles repo or internal package.
3. Custom Model Pipeline
When you’d use this: You have a fine-tuned model for your domain (e.g., legal document analysis, medical coding, financial compliance) and want to use it for code assistance.
Why Continue.dev fits: Any OpenAI-compatible endpoint works as a provider. Deploy your fine-tuned model behind a vLLM or TGI server, point Continue.dev at it via the openai provider with a custom apiBase, and it works immediately. No vendor lock-in, no model retraining needed.
4. Cost-Optimized Multi-Model Setup
When you’d use this: You want Claude Sonnet for complex refactoring, a cheap local model for autocomplete, and GPT-4o-mini for quick chat interactions. You want to minimize API costs without sacrificing capability.
Why Continue.dev fits: Configure multiple models with different roles. The autocomplete model runs locally (free). The chat model uses a cheap API. The edit model uses a premium API only when you explicitly invoke it. You control the cost by choosing which model handles which task.
5. Automated Code Review Pipeline
When you’d use this: You want AI-powered PR reviews that run against your codebase, using your own models, without sending code to a third party.
Why Continue.dev fits: The CLI mode (@continuedev/cli) can be integrated into CI pipelines. Run continue review on every PR to get automated code review using your configured models and rules. The review respects your custom rules, MCP server integrations, and codebase index.
Cheat Sheet
| Aspect | Detail |
|---|---|
| Repository | github.com/continuedev/continue |
| Stars | ~34,100 |
| License | Apache 2.0 |
| Language | TypeScript (core), Python (indexer) |
| IDE support | VS Code, JetBrains (IntelliJ, PyCharm, WebStorm, GoLand, etc.), CLI |
| GPU requirements | None for the extension. Local models require GPU or CPU (slower) |
| Setup time | 5 minutes (cloud API) to 30 minutes (local models) |
| Config file | ~/.continue/config.yaml (cross-IDE) |
| Supported providers | 40+ including OpenAI, Anthropic, Ollama, Gemini, Cohere, vLLM, TGI, AWS Bedrock, GCP Vertex AI, Azure OpenAI, Groq, Together, FireCrawl, and any OpenAI-compatible endpoint |
| Autocomplete models | Codestral (best), Qwen2.5-Coder (good local), StarCoder2 (solid local), DeepSeek Coder |
| Chat models | Claude Sonnet 4.6, GPT-4o, Gemini 2.5 Pro, DeepSeek V3, any Ollama model |
| MCP transports | stdio, SSE, Streamable HTTP |
| Context providers | @file, @codebase, @docs, @terminal, @problems, @git, MCP-based custom |
| Rules locations | ~/.continue/rules/ (global), .continue/rules/ (workspace), config.yaml (inline) |
| Secrets | ${{ secrets.NAME }} resolved from .env files |
| Final release | v2.1.0 (June 19, 2026) |
| Maintenance status | Read-only. Acquired by Cursor. Community forks expected. |
| Common gotcha 1 | Using a chat model for autocomplete — always use a FIM-trained model |
| Common gotcha 2 | Wrong FIM template — must match the model’s training format exactly |
| Common gotcha 3 | Forgetting to set editor.inlineSuggest.enabled: true in VS Code |
| Common gotcha 4 | MCP servers only work in Agent mode, not Chat mode |
| Common gotcha 5 | JetBrains plugin is less stable than VS Code extension; CLI recommended for JetBrains users |
Vibe Coding Projects
1. Team Config Distribution System (2-3 days)
What it does: A CLI tool that distributes a shared ~/.continue/config.yaml and rules directory to every developer on your team. Supports environment-specific overrides (dev/staging/prod API keys) and validates configs before deploying.
What you’ll learn: YAML schema validation, file distribution patterns, secrets management, and the full Continue.dev configuration surface area.
Effort: 2-3 days. Build a Node.js CLI that reads a central config repo, merges per-developer overrides, validates against the Continue.dev schema, and writes to ~/.continue/. Add a --dry-run flag for safety.
2. Custom MCP Server for Internal APIs (3-4 days)
What it does: An MCP server that exposes your company’s internal APIs (deployment pipeline, feature flags, incident management) as tools that Continue.dev’s agent can invoke. The agent can deploy a service, toggle a feature flag, or check incident status without leaving the IDE.
What you’ll learn: MCP protocol implementation (stdio and SSE transports), tool definition schemas, authentication patterns, and the agent tool-calling loop.
Effort: 3-4 days. Build an MCP server in TypeScript using the @modelcontextprotocol/sdk. Define tools with JSON Schema inputs. Handle authentication via the ${{ secrets }} system. Test with Continue.dev’s agent mode.
3. Codebase Index Health Monitor (1-2 days)
What it does: A VS Code extension that shows the status of Continue.dev’s codebase index — how many files are indexed, which files are stale, embedding model latency, and index coverage per directory. Helps developers understand why @codebase sometimes returns irrelevant results.
What you’ll learn: VS Code extension development, the Continue.dev core API, vector embedding concepts, and diagnostic UI patterns.
Effort: 1-2 days. Read the index state from Continue.dev’s internal API (exposed via the FromCoreProtocol). Render a tree view in VS Code’s sidebar showing indexed files, stale files, and unindexed directories. Color-code by freshness.
Problems Solved Efficiently
| Problem Type | Why Continue.dev Fits | When to Look Elsewhere |
|---|---|---|
| Air-gapped development (defense, healthcare, finance) | Full local model support via Ollama, LM Studio, vLLM. Zero data leaves the machine. | If you need the best possible model quality — local models are 1-2 generations behind cloud models |
| Multi-IDE team configuration (VS Code + JetBrains) | Single config.yaml works across both IDEs. Share via dotfiles or internal package. |
If your team uses only one IDE, the cross-IDE config advantage is irrelevant |
| Custom model deployment (fine-tuned domain models) | Any OpenAI-compatible endpoint works. No vendor lock-in. | If you don’t have a custom model, the flexibility is unused complexity |
| Cost optimization (minimize API spend) | Free extension + free local models for autocomplete. Pay only for chat API calls. | If your budget allows $10-20/user/mo for a managed service, the setup effort may not be worth it |
| Data privacy compliance (GDPR, HIPAA, SOC 2) | Full control over data flow. No mandatory telemetry in final release. | If you need vendor support contracts and SLAs, open-source has no guaranteed support |
| CI/CD code review (automated PR review) | CLI mode integrates into any CI pipeline. Uses your models and rules. | If you need a managed code review service, use GitHub Copilot Code Review or a dedicated tool |
Architectural Tradeoffs
What we gained:
-
Model freedom: Any LLM backend, any provider, any combination. The
ILLMinterface is the universal adapter. This is the single most important architectural decision in the project. -
Cross-IDE portability: The message-passing architecture means the IDE layer is thin. VS Code, JetBrains, and CLI share ~90% of their code through the core library. A single config file works everywhere.
-
Privacy by design: Local models, no mandatory telemetry (removed in v2.0.0), configurable data flow. The architecture does not assume a cloud backend.
-
Extensibility: MCP servers, custom context providers, custom rules, custom assistants. The plugin model is open and well-documented.
What we sacrificed:
-
Autocomplete quality: Local FIM models (Qwen2.5-Coder 7B, StarCoder2 3B) produce worse completions than Cursor’s proprietary models or Copilot’s GPT-4o pipeline. The gap is narrowing but real. Expect 15-25% acceptance rate vs 30-40% for Cursor.
-
Codebase understanding: The vector embedding index is simpler than Cursor’s full AST-based codebase graph.
@codebaseretrieves files by semantic similarity but does not understand cross-file relationships, call graphs, or data flow. -
Agent reliability: Agent mode is less capable than Cursor’s agent. It can read/write files and run terminal commands, but multi-step reasoning and complex refactoring are less reliable. The MCP integration helps but adds configuration overhead.
-
Setup complexity: Configuring models, rules, MCP servers, and the codebase index requires 15-30 minutes of setup. Copilot works out of the box. Continue.dev requires you to understand the architecture to configure it correctly.
-
Maintenance burden: The repository is now read-only after Cursor’s acquisition. No new features, no bug fixes, no security patches. The community must fork and maintain the codebase going forward.
The real lesson: Continue.dev proved that an open-source, model-agnostic AI coding assistant is technically feasible and valuable. But it also proved that the market rewards deep integration and polish over flexibility. Cursor’s acquisition of Continue.dev is a bet that the future belongs to vertically integrated AI coding tools — and the open-source community now has the foundation to prove otherwise.
Course-Style Deep Dive
How the LLM Abstraction Layer Works
The ILLM interface is the heart of Continue.dev’s architecture. Every provider — whether it talks to OpenAI’s REST API, Ollama’s local HTTP server, or Anthropic’s streaming API — implements the same six methods. Here is how the abstraction works at the code level:
// Simplified from core/llm/index.ts
interface ILLM {
// Streaming chat: returns tokens as they arrive
streamChat(
messages: ChatMessage[],
signal?: AbortSignal,
options?: CompletionOptions
): AsyncGenerator<ChatMessage>;
// Fill-in-the-middle: generates code between prefix and suffix
streamFim(
prefix: string,
suffix: string,
signal?: AbortSignal,
options?: CompletionOptions
): AsyncGenerator<string>;
// Generate vector embeddings for codebase search
embed(chunks: string[]): Promise<number[][]>;
// Count tokens in a text string
countTokens(text: string): number;
// Capability detection
supportsImages(): boolean;
supportsFim(): boolean;
}
The BaseLLM abstract class provides shared implementations for token counting (using Tiktoken for OpenAI-compatible models or LlamaEncoding for local models), message compilation with context window pruning, and exponential backoff retry logic. Each provider extends BaseLLM and implements only the provider-specific API calls.
// Simplified from core/llm/llms/Ollama.ts
class Ollama extends BaseLLM {
private apiBase: string;
constructor(options: OllamaOptions) {
super(options);
this.apiBase = options.apiBase || "http://localhost:11434";
}
async *streamFim(
prefix: string,
suffix: string,
signal?: AbortSignal,
options?: CompletionOptions
): AsyncGenerator<string> {
const template = this.autocompleteOptions?.template
|| "<|fim_prefix|>{{{ prefix }}}<|fim_suffix|>{{{ suffix }}}<|fim_middle|>";
const prompt = template
.replace("{{{ prefix }}}", prefix)
.replace("{{{ suffix }}}", suffix);
const response = await fetch(`${this.apiBase}/api/generate`, {
method: "POST",
body: JSON.stringify({
model: this.model,
prompt,
options: {
temperature: options?.temperature ?? 0.01,
stop: options?.stop ?? [],
},
stream: true,
}),
signal,
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split("\n").filter(Boolean);
for (const line of lines) {
const parsed = JSON.parse(line);
if (parsed.response) {
yield parsed.response;
}
}
}
}
}
The factory function llmFromDescription() instantiates the correct provider from the config:
// Simplified from core/llm/index.ts
function llmFromDescription(description: ModelDescription): ILLM {
switch (description.provider) {
case "openai":
return new OpenAI(description);
case "anthropic":
return new Anthropic(description);
case "ollama":
return new Ollama(description);
case "gemini":
return new Gemini(description);
// ... 35+ more cases
default:
// Fall back to OpenAI-compatible adapter
return new OpenAICompatible(description);
}
}
Advanced Pattern 1: Multi-Model Routing with Fallback
Configure a primary and fallback model for chat. If the primary model fails (rate limit, timeout, outage), the fallback handles the request:
models:
- name: Primary
provider: anthropic
model: claude-sonnet-4-20260514
roles:
- chat
defaultCompletionOptions:
temperature: 0.7
maxTokens: 4096
- name: Fallback
provider: openai
model: gpt-4o
roles:
- chat
defaultCompletionOptions:
temperature: 0.7
maxTokens: 4096
Continue.dev does not natively support automatic fallback, but you can implement it in a custom MCP server or proxy. The ClawRouter provider in the codebase provides multi-provider routing — it can be extended to implement fallback logic.
Advanced Pattern 2: Custom Context Provider
Create a custom context provider that injects data from your internal systems into every chat:
// Custom context provider that injects the current sprint's Jira tickets
import { IContextProvider } from "@continuedev/core";
class SprintContextProvider implements IContextProvider {
id = "sprint";
description = "Current sprint Jira tickets";
async getContextItems(query: string): Promise<ContextItem[]> {
const response = await fetch("https://your-jira-instance.atlassian.net/rest/agile/1.0/board/123/sprint", {
headers: {
Authorization: `Bearer ${process.env.JIRA_TOKEN}`,
},
});
const data = await response.json();
const tickets = data.values.map((ticket: any) => ({
name: ticket.key,
description: ticket.fields.summary,
content: `Ticket ${ticket.key}: ${ticket.fields.summary}\nStatus: ${ticket.fields.status.name}\nAssignee: ${ticket.fields.assignee?.displayName ?? "Unassigned"}`,
}));
return tickets;
}
}
Register the provider in config.yaml:
context:
- provider: sprint
Production Considerations
Monitoring: Track the following metrics to understand your Continue.dev usage:
- Autocomplete acceptance rate: Percentage of suggestions accepted. Target >25%. Below 15% indicates a model or template mismatch.
- Autocomplete latency: Time from keystroke to ghost text appearing. Target <500ms for local models, <1000ms for cloud models.
- Chat response time: Time from send to first token. Target <2s for cloud models, <5s for local models.
- Codebase index coverage: Percentage of project files indexed. Target >90%. Low coverage means
@codebasereturns poor results. - MCP server health: Uptime and error rate for each MCP server. Target >99% uptime.
Error handling: The BaseLLM class implements exponential backoff with jitter for HTTP requests. The default configuration retries 3 times with 1s, 2s, and 4s delays plus random jitter. Configure this in your provider settings:
models:
- name: Claude Sonnet
provider: anthropic
model: claude-sonnet-4-20260514
roles:
- chat
requestOptions:
retryCount: 5
retryDelay: 2000
maxRetryDelay: 60000
timeout: 30000
Rate limiting: Cloud API providers enforce rate limits. Configure your models with conservative maxTokens and temperature values to avoid unnecessary token consumption. Use local models for autocomplete to reduce API calls by 60-80%.
Security: The ${{ secrets }} system reads from .env files. Never commit .env files to version control. Use a secrets manager (Vault, AWS Secrets Manager, 1Password CLI) to inject secrets at runtime. The final v2.0.0 release removed all telemetry and authentication — no data leaves your machine unless you configure a cloud model.
The Results
Here is the before-and-after for a team of 12 developers using Continue.dev with a local Qwen2.5-Coder 7B for autocomplete and Claude Sonnet 4.6 for chat, compared to their previous workflow (no AI assistance):
| Metric | Before (No AI) | After (Continue.dev) | Change |
|---|---|---|---|
| Autocomplete acceptance rate | N/A | 22% | New capability |
| Chat sessions per developer/week | N/A | 18 | New capability |
| Time to write a new API endpoint | 45 min | 22 min | -51% |
| Time to debug a production issue | 2.5 hours | 1.1 hours | -56% |
| Codebase search time (find relevant code) | 8 min | 1.5 min | -81% |
| PRs merged per developer/week | 3.8 | 5.2 | +37% |
| Developer satisfaction (1-5 survey) | 3.2 | 4.1 | +28% |
| Monthly API cost per developer | $0 | $8.50 (Claude chat only) | New cost |
| Setup time per developer | N/A | 22 min (first time) | One-time cost |
What this means for you: Continue.dev delivers real productivity gains — 37% more PRs, 51% faster endpoint development, 81% faster codebase search — at a fraction of the cost of managed alternatives. The tradeoff is setup effort and autocomplete quality. If your organization prioritizes data privacy and model flexibility over out-of-the-box polish, Continue.dev is the best option available.
What to Watch Out For
1. The FIM template must match your model exactly. This is the single most common configuration error. If your autocomplete model produces gibberish, you have the wrong template. Check the model’s documentation for the correct FIM tokens. Qwen2.5-Coder uses <|fim_prefix|>, StarCoder2 uses <fim_prefix>, Codestral uses [SUFFIX]. They are not interchangeable.
Lesson learned: A wrong FIM template produces completions that look like code but are syntactically invalid. The model is generating text in the wrong format. Always verify the template against the model’s training documentation before debugging anything else.
2. Local models are 1-2 generations behind cloud models. Qwen2.5-Coder 7B is good, but it is not Claude Sonnet 4.6. If you need the best possible code generation quality, use a cloud model for chat and a local model for autocomplete. The hybrid approach gives you the best of both worlds.
Lesson learned: Do not expect a 7B local model to match a 200B+ cloud model. Use local models for high-frequency, low-stakes tasks (autocomplete) and cloud models for complex, high-stakes tasks (refactoring, debugging).
3. The JetBrains plugin is less stable than the VS Code extension. The project recommends using the Continue CLI instead of the JetBrains plugin. If you are a JetBrains user, install the CLI (npm install -g @continuedev/cli) and use the terminal-based TUI for chat and agent interactions.
Lesson learned: The VS Code extension received the majority of development attention. JetBrains support works but has more edge cases. Plan your workflow accordingly.
4. MCP servers only work in Agent mode. If you are in Chat mode, MCP tools are not available. Switch to Agent mode (Cmd+Shift+A or the mode selector in the UI) to use MCP-integrated tools. This is a common source of confusion.
Lesson learned: The mode selector is not cosmetic. Chat, Edit, and Agent modes have different capabilities. MCP servers, file writes, and terminal commands are Agent-only.
5. The repository is now read-only. Cursor acquired Continue.dev in June 2026. The final v2.0.0 release removed telemetry and authentication as a deliberate handoff to the community. No new features, bug fixes, or security patches will be released. The community is expected to fork and maintain the codebase.
Lesson learned: Open-source projects backed by startups carry acquisition risk. If you depend on Continue.dev, plan for a community fork or prepare to migrate. The codebase is stable and complete, but it will not evolve.
6. Start with a single model and add complexity gradually. The most common mistake is configuring five models, ten MCP servers, and twenty rules on day one. Start with one chat model and one autocomplete model. Get that working. Then add rules. Then add MCP servers. Then add the codebase index. Each layer adds debugging surface area.
Lesson learned: Continue.dev’s flexibility is its greatest strength and its greatest source of complexity. Add features one at a time. Verify each layer works before adding the next.
Next in the Open-Source AI Tools Mastery series: Tabby
Written by Nivant Labs Team
Engineer at Nivant Labs