React
Continue is an open-source AI coding assistant platform that integrates with IDEs (VS Code, JetBrains) and CLI to provide LLM-powered code completion, chat, editing, and agentic workflows. It acts as a middleware layer connecting 60+ LLM providers (OpenAI, Anthropic, AWS Bedrock, Google Gemini, local models, etc.) to developer workflows with features like codebase indexing, context retrieval, tool use, and Model Context Protocol (MCP) support.
Tech Stack
Key Features
- Multi-Provider LLM Abstraction
- Intelligent Code Autocomplete
- Next Edit Prediction
- Codebase Indexing & Retrieval
- 30+ Context Providers
- Model Context Protocol (MCP) Integration
- Diff Streaming & Code Edit Engine
- YAML/JSON Configuration & Profile Management
- Tools & Slash Commands
- Control Plane & Team Features
- CLI & Background Agents
- Custom Prompt Files
- Terminal Security
Entry Points
Start here when working with this codebase
core/core.tsCentral hub that coordinates IDE events, manages LLM chat/completion flows, handles configuration, and delegates to specialized services. All major subsystems are wired together here.
Start here when adding new message handlers, understanding how subsystems connect, or tracing any request from IDE to core.
core/index.d.tsCentral type definitions and contracts for the entire Continue system including IDE interface, context providers, LLM types, tool definitions, and configuration schemas.
Start here when you need to understand or modify any shared type, interface contract, or when adding a new feature that requires new types.
binary/build.jsOrchestrates the complete build: cleanup → asset preparation → esbuild bundling → binary compilation for multiple platforms → verification.
Start here when modifying the build process, adding new build targets, or debugging build failures.
binary/core-dev-server.jsBootstraps the development server with appropriate environment configuration for local development.
Start here when setting up local development or debugging the dev server startup.
scripts/build-packages.jsOrchestrates multi-package builds in correct dependency order with parallel execution and error handling.
Start here when adding a new package to the monorepo or modifying build order dependencies.
core/control-plane/client.tsClient for the Continue platform API: auth, secrets sync, policy, credits, agents, and model proxy endpoints.
Start here when adding new platform API integrations or modifying authentication flows.
packages/config-yaml/src/index.tsYAML config parsing, schema validation, assistant and block definitions for the configuration system.
Start here when modifying config schema, adding new config fields, or changing how YAML configs are parsed.
gui/tailwind.config.cjsConfigures Tailwind CSS with custom design tokens, breakpoints, and color palette for the GUI application.
Start here when modifying the GUI's design system, adding new colors, or changing responsive breakpoints.
binary/test/binary.test.tsIntegration tests that exercise the binary messenger protocol including ping, config, history, and LLM endpoints.
Start here when adding integration tests for new endpoints or debugging protocol-level issues.
extensions/cli/src/index.tsCLI extension entry point that handles agent status reporting and serve mode.
Start here when modifying CLI behavior or adding new CLI commands.
How to Make Changes
Common modification patterns for this codebase
Add a new IPC/messenger API endpoint
core/index.d.tsDefine the request/response types for your new endpoint. Add the endpoint name to the appropriate protocol type map (e.g., add to the request type union).
Pattern: See how 'history/save' and 'config/getSerializedProfileInfo' are typed — each endpoint has a request payload type and response type defined in the central type definitions.
core/protocol/index.tsRegister the new endpoint in the protocol message type definitions. Add the endpoint string literal to the appropriate protocol mapping.
Pattern: See how existing endpoints like 'ping', 'history/list', 'config/addModel' are registered as protocol message types with their corresponding request/response shapes.
core/core.tsAdd the handler for your new endpoint in the core orchestrator. Wire it into the message handling dispatch logic.
Pattern: See how 'history/save' and 'llm/complete' handlers are registered — each handler receives the request payload, performs logic (often delegating to a service), and returns the response type.
binary/test/binary.test.tsAdd an integration test for the new endpoint that sends a request through the binary messenger and validates the response.
Pattern: See how the existing tests for 'ping', 'config/getSerializedProfileInfo', and 'history/list' send requests via the messenger and assert on the response shape.
Conventions to follow:
Add a new LLM provider
packages/llm-info/src/index.tsAdd the new provider's metadata including provider name, supported models, capabilities, and default parameters.
Pattern: See how existing providers are defined with their model lists, context lengths, and capability flags in the llm-info package.
packages/openai-adapters/src/index.tsCreate or register an OpenAI-compatible adapter for the new provider if it uses a compatible API. Map the provider name to the adapter configuration.
Pattern: See how other providers are mapped to adapters with their base URLs, auth header formats, and any request/response transformations needed.
core/llm/index.tsRegister the new provider in the LLM abstraction layer. Add the provider to the provider factory/registry so it can be instantiated from config.
Pattern: See how existing providers are registered with their constructor functions and config-to-instance mapping logic.
core/index.d.tsAdd the provider name to the ModelProvider type union and define any provider-specific configuration options.
Pattern: See how existing provider names are listed in the type union and how provider-specific config fields are typed.
packages/config-yaml/src/index.tsEnsure the YAML config schema validates the new provider name and any provider-specific fields.
Pattern: See how existing provider names are included in the schema validation for model configuration blocks.
Conventions to follow:
Add a new context provider
core/index.d.tsDefine the context provider's type by adding its name to the ContextProviderName type and defining any provider-specific options interface.
Pattern: See how existing context provider names like 'github', 'discord' are typed and how their options interfaces are structured.
core/context/providers/Create a new file for your context provider (e.g., core/context/providers/MyNewContextProvider.ts). Implement the IContextProvider interface with getContextItems() and loadSubmenuItems() methods.
Pattern: See core/context/providers/GitHubIssuesContextProvider.ts for a provider that fetches from an external API, or core/context/providers/DiscordContextProvider.ts for one that handles multiple channels/submenu items.
core/context/index.tsRegister the new context provider in the context provider registry/factory. Import your provider and add it to the mapping from provider name string to provider class.
Pattern: See how existing providers like GitHubIssuesContextProvider and DiscordContextProvider are imported and registered in the provider map.
core/index.d.tsIf your provider needs configuration, add the config fields to the ContextProviderWithParams type or the relevant config interface.
Pattern: See how existing providers define their params (API keys, repo names, etc.) in the type definitions.
Conventions to follow:
Add a new tool
core/index.d.tsDefine the tool's type interface including its name, description, parameters schema, and return type.
Pattern: See how existing tool types are defined with their parameter schemas and descriptions in the central type definitions.
core/tools/index.tsCreate the tool definition and register it in the tools system. Implement the tool's execute function that receives parameters and returns results.
Pattern: See how existing tools are defined with their JSON schema parameters, execution logic, and how they're registered in the tool registry.
core/core.tsIf the tool needs special handling or IDE integration, wire it into the core orchestrator's tool execution flow.
Pattern: See how existing tool calls are dispatched from the chat flow in core.ts — tools are invoked during LLM streaming when the model emits tool_call messages.
Conventions to follow:
Add a new slash command
core/index.d.tsAdd the slash command name to the SlashCommandName type union and define its description and parameter types.
Pattern: See how existing slash command names are typed in the central type definitions.
core/commands/index.tsCreate the slash command handler. Implement the command's run function that receives the user input and context, then produces output.
Pattern: See how existing slash commands are defined with their name, description, and async run() function that yields content items.
core/core.tsVerify the command is picked up by the slash command dispatch logic in the core orchestrator. Most commands are auto-registered but some may need explicit wiring.
Pattern: See how the chat message handler in core.ts detects slash command prefixes and routes to the commands system.
Conventions to follow:
Add a new configuration option
core/index.d.tsAdd the new config field to the appropriate configuration interface (e.g., SerializedContinueConfig, ModelDescription, etc.).
Pattern: See how existing config fields like 'model', 'provider', 'apiKey' are typed in the config interfaces.
packages/config-types/src/index.tsAdd the config field type to the shared config types package so it's available across all packages.
Pattern: See how existing shared config types are exported and used by both core and extension packages.
packages/config-yaml/src/index.tsUpdate the YAML schema validation to accept and validate the new config field. Add default values if applicable.
Pattern: See how existing config fields are validated with their types, constraints, and defaults in the YAML schema.
core/config/index.tsHandle the new config field in the config loading/resolution pipeline. Apply defaults, validate, and pass it to the appropriate subsystem.
Pattern: See how existing config fields are loaded from YAML/JSON, merged with defaults, and resolved into the runtime config object.
Conventions to follow:
Add a new MCP (Model Context Protocol) integration
core/context/mcp/index.tsAdd or modify the MCP server connection logic. Register the new MCP server type or modify how MCP connections are established.
Pattern: See how existing MCP server connections are managed — each server has a connection lifecycle (connect, list tools/resources, disconnect) and exposes tools and context through the MCP protocol.
core/tools/index.tsIf the MCP server provides tools, ensure they're properly surfaced through the tools system. MCP tools are dynamically discovered and registered.
Pattern: See how MCP tools are integrated alongside built-in tools — they follow the same tool interface but are discovered at runtime from MCP server connections.
packages/config-yaml/src/index.tsAdd the MCP server configuration to the YAML schema so users can configure the new MCP server in their config files.
Pattern: See how existing MCP server configs are defined in the YAML schema with their connection parameters (command, args, env, url).
Conventions to follow:
Add a new indexing pipeline or codebase indexer
core/indexing/index.tsCreate or register the new indexing pipeline. Define how documents are chunked, embedded, and stored.
Pattern: See how existing indexing pipelines handle the flow: file discovery → chunking → embedding generation → storage in the index.
core/index.d.tsDefine any new types needed for the indexing pipeline (e.g., new chunk types, index entry types, query result types).
Pattern: See how existing indexing types like CodebaseIndex, IndexTag, and chunk types are defined.
core/core.tsWire the new indexing pipeline into the core orchestrator so it's triggered during indexing operations (on workspace open, file changes, etc.).
Pattern: See how existing indexing is triggered from core.ts — indexing runs on workspace initialization and file change events.
Conventions to follow:
Add a new package to the monorepo
scripts/build-packages.jsAdd the new package to the build order array. Place it after its dependencies and before packages that depend on it.
Pattern: See how existing packages like 'packages/config-yaml', 'packages/openai-adapters', 'packages/llm-info' are ordered in the build script based on their dependency relationships.
packages/shared-release.config.jsIf the package needs publishing, use the shared release config factory to create a release configuration for the new package.
Pattern: See how packages/shared-release.config.js exports a factory function that other packages use to generate their semantic-release config.
core/index.d.tsIf the new package exports types used by core, import and re-export them from the central type definitions.
Pattern: See how types from packages/config-types are referenced in core/index.d.ts.
Conventions to follow:
Add or modify GUI styling and design tokens
gui/tailwind.config.cjsAdd new design tokens (colors, spacing, breakpoints) to the Tailwind configuration. Extend the theme object with your new values.
Pattern: See how existing custom colors, breakpoints, and design tokens are defined in the theme.extend section of gui/tailwind.config.cjs.
gui/postcss.config.cjsIf you need new PostCSS plugins for CSS processing, add them to the PostCSS configuration.
Pattern: See how existing PostCSS plugins (tailwindcss, autoprefixer) are configured in gui/postcss.config.cjs.
Conventions to follow:
Coding Conventions
Standards and patterns used in this codebase
Use esbuild for bundling TypeScript into single-file outputs with explicit external dependencies, platform/architecture targeting, and CommonJS format
esbuild.build({ entryPoints: ['src/index.ts'], bundle: true, outfile: 'out/index.cjs', platform: 'node', format: 'cjs', external: ['esbuild', '@esbuild/*', '@lancedb/*', ...] })Use Jest with ts-jest for TypeScript testing, with explicit transform patterns for ts/tsx/js/jsx files and moduleNameMapper for path aliases or module stubs
module.exports = { transform: { '^.+\\.tsx?$': 'ts-jest', '^.+\\.jsx?$': ['ts-jest', { tsconfig: { allowJs: true } }] }, moduleNameMapper: { 'azure/identity': '<rootDir>/test_util/mock_modules/azure_identity.js' } }Define comprehensive interface hierarchies in .d.ts declaration files with extensive use of union types, optional properties, and discriminated unions for protocol messages and configuration
interface LLMOptions { model: string; title?: string; completionOptions?: CompletionOptions; requestOptions?: RequestOptions; ... } type ChatMessage = { role: MessageRole; content: MessageContent; }Use a central orchestrator class that wires subsystems together via constructor injection of an IDE abstraction interface, with lazy initialization of services and messenger-based IPC communication
export class Core { constructor(private messenger: IMessenger<...>, private ide: IDE) { this.configHandler = new ConfigHandler(...); this.codebaseIndexer = new CodebaseIndexer(...); this.completionProvider = new CompletionProvider(...); } }Define typed message handlers using string literal message types mapped to handler functions, with request/response patterns communicated over IPC between core, IDE, and webview layers
on('llm/streamChat', (msg) => { ... }); on('config/reload', (msg) => { ... }); on('context/getContextItems', (msg) => { ... });Development entry points set environment variables before importing application code, using process.env flags to toggle development mode and configure paths
process.env.CONTINUE_DEVELOPMENT = 'true'; process.env.CONTINUE_GLOBAL_DIR = path.join(__dirname, '..', '.continue'); require('./out/index.cjs');Support multiple configuration formats (YAML, JSON, markdown prompt files) with profile management, merging strategies, and schema validation, resolved through a unified ConfigHandler
configHandler.loadConfig() resolves YAML config, JSON config, and prompt files into a unified ContinueConfig object with profile-based overridesUse async generator functions (async function*) for streaming LLM responses and incremental results, yielding chunks that consumers can iterate with for-await-of
async *streamChat(messages: ChatMessage[], options: LLMFullCompletionOptions): AsyncGenerator<ChatMessage> { for await (const chunk of this.llm.streamChat(messages)) { yield chunk; } }Support both Jest and Vitest test runners across packages, with Vitest preferred for newer code using defineConfig with explicit include/exclude patterns
export default defineConfig({ test: { include: ['**/*.test.ts'], exclude: ['node_modules', 'dist'] } });Explicitly externalize native modules and platform-specific packages in build configurations to avoid bundling issues with binary addons
external: ['esbuild', '@esbuild/*', '@lancedb/*', 'onnxruntime-node', 'sqlite3', '@vscode/*', 'fsevents']Use fluent/method-chaining patterns in utility classes, with methods returning 'this' to enable composable API calls
class Calculator { add(a) { this.result += a; return this; } subtract(a) { this.result -= a; return this; } }