.clinerules/cline-overview.md
Cline is a VSCode extension that provides AI assistance through a combination of a core extension backend and a React-based webview frontend. The extension is built with TypeScript and follows a modular architecture pattern.
graph TB
subgraph VSCodeExtensionHost[VSCode Extension Host]
subgraph CoreExtension[Core Extension]
ExtensionEntry[Extension Entry
src/extension.ts]
WebviewProvider[WebviewProvider
src/core/webview/index.ts]
Controller[Controller
src/core/controller/index.ts]
Task[Task
src/core/task/index.ts]
GlobalState[VSCode Global State]
SecretsStorage[VSCode Secrets Storage]
McpHub[McpHub
src/services/mcp/McpHub.ts]
end
subgraph WebviewUI[Webview UI]
WebviewApp[React App
webview-ui/src/App.tsx]
ExtStateContext[ExtensionStateContext
webview-ui/src/context/ExtensionStateContext.tsx]
ReactComponents[React Components]
end
subgraph Storage
TaskStorage[Task Storage
Per-Task Files & History]
CheckpointSystem[Git-based Checkpoints]
end
subgraph apiProviders[API Providers]
AnthropicAPI[Anthropic]
OpenRouterAPI[OpenRouter]
BedrockAPI[AWS Bedrock]
OtherAPIs[Other Providers]
end
subgraph MCPServers[MCP Servers]
ExternalMcpServers[External MCP Servers]
end
end
%% Core Extension Data Flow
ExtensionEntry --> WebviewProvider
WebviewProvider --> Controller
Controller --> Task
Controller --> McpHub
Task --> GlobalState
Task --> SecretsStorage
Task --> TaskStorage
Task --> CheckpointSystem
Task --> |API Requests| apiProviders
McpHub --> |Connects to| ExternalMcpServers
Task --> |Uses| McpHub
%% Webview Data Flow
WebviewApp --> ExtStateContext
ExtStateContext --> ReactComponents
%% Bidirectional Communication
WebviewProvider <-->|postMessage| ExtStateContext
style GlobalState fill:#f9f,stroke:#333,stroke-width:2px
style SecretsStorage fill:#f9f,stroke:#333,stroke-width:2px
style ExtStateContext fill:#bbf,stroke:#333,stroke-width:2px
style WebviewProvider fill:#bfb,stroke:#333,stroke-width:2px
style McpHub fill:#bfb,stroke:#333,stroke-width:2px
style apiProviders fill:#fdb,stroke:#333,stroke-width:2px
The core extension follows a clear hierarchical structure:
This architecture provides clear separation of concerns:
The WebviewProvider class in src/core/webview/index.ts is responsible for:
activeInstances)The WebviewProvider maintains a reference to the Controller and delegates message handling to it. It also handles the creation of both sidebar and tab panel webviews, allowing Cline to be used in different contexts within VSCode.
The Controller class manages multiple types of persistent storage:
The Controller handles the distribution of state to both the core extension and webview components. It also coordinates state across multiple extension instances, ensuring consistency.
State synchronization between instances is handled through:
The Controller implements methods for:
The ExtensionStateContext in webview-ui/src/context/ExtensionStateContext.tsx provides React components with access to the extension's state. It uses a context provider pattern and maintains local state for UI components. The context includes:
It synchronizes with the core extension through VSCode's message passing system and provides type-safe access to the state via a custom hook (useExtensionState).
The ExtensionStateContext handles:
Cline supports multiple AI providers through a modular API provider system. Each provider is implemented as a separate module in the src/api/providers/ directory and follows a common interface.
The API system consists of:
src/api/providers/src/api/transform/Key providers include:
API configurations are stored securely:
The system supports:
Cline supports separate model configurations for Plan and Act modes:
The Task class is responsible for executing AI requests and tool operations. Each task runs in its own instance of the Task class, ensuring isolation and proper state management.
The core task execution loop follows this pattern:
class Task {
async initiateTaskLoop(userContent: UserContent, isNewTask: boolean) {
while (!this.abort) {
// 1. Make API request and stream response
const stream = this.attemptApiRequest()
// 2. Parse and present content blocks
for await (const chunk of stream) {
switch (chunk.type) {
case "text":
// Parse into content blocks
this.assistantMessageContent = parseAssistantMessageV2(chunk.text)
// Present blocks to user
await this.presentAssistantMessage()
break
}
}
// 3. Wait for tool execution to complete
await pWaitFor(() => this.userMessageContentReady)
// 4. Continue loop with tool result
const recDidEndLoop = await this.recursivelyMakeClineRequests(
this.userMessageContent
)
}
}
}
The streaming system handles real-time updates and partial content:
class Task {
async presentAssistantMessage() {
// Handle streaming locks to prevent race conditions
if (this.presentAssistantMessageLocked) {
this.presentAssistantMessageHasPendingUpdates = true
return
}
this.presentAssistantMessageLocked = true
// Present current content block
const block = this.assistantMessageContent[this.currentStreamingContentIndex]
// Handle different types of content
switch (block.type) {
case "text":
await this.say("text", content, undefined, block.partial)
break
case "tool_use":
// Handle tool execution
break
}
// Move to next block if complete
if (!block.partial) {
this.currentStreamingContentIndex++
}
}
}
Tools follow a strict execution pattern:
class Task {
async executeToolWithApproval(block: ToolBlock) {
// 1. Check auto-approval settings
if (this.shouldAutoApproveTool(block.name)) {
await this.say("tool", message)
this.consecutiveAutoApprovedRequestsCount++
} else {
// 2. Request user approval
const didApprove = await askApproval("tool", message)
if (!didApprove) {
this.didRejectTool = true
return
}
}
// 3. Execute tool
const result = await this.executeTool(block)
// 4. Save checkpoint
await this.saveCheckpoint()
// 5. Return result to API
return result
}
}
The system includes robust error handling:
class Task {
async handleError(action: string, error: Error) {
// 1. Check if task was abandoned
if (this.abandoned) return
// 2. Format error message
const errorString = `Error ${action}: ${error.message}`
// 3. Present error to user
await this.say("error", errorString)
// 4. Add error to tool results
pushToolResult(formatResponse.toolError(errorString))
// 5. Cleanup resources
await this.diffViewProvider.revertChanges()
await this.browserSession.closeBrowser()
}
}
The Task class handles API requests with built-in retry, streaming, and token management:
class Task {
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
// 1. Wait for MCP servers to connect
await pWaitFor(() => this.controllerRef.deref()?.mcpHub?.isConnecting !== true)
// 2. Manage context window
const previousRequest = this.clineMessages[previousApiReqIndex]
if (previousRequest?.text) {
const { tokensIn, tokensOut } = JSON.parse(previousRequest.text || "{}")
const totalTokens = (tokensIn || 0) + (tokensOut || 0)
// Truncate conversation if approaching context limit
if (totalTokens >= maxAllowedSize) {
this.conversationHistoryDeletedRange = this.contextManager.getNextTruncationRange(
this.apiConversationHistory,
this.conversationHistoryDeletedRange,
totalTokens / 2 > maxAllowedSize ? "quarter" : "half"
)
}
}
// 3. Handle streaming with automatic retry
try {
this.isWaitingForFirstChunk = true
const firstChunk = await iterator.next()
yield firstChunk.value
this.isWaitingForFirstChunk = false
// Stream remaining chunks
yield* iterator
} catch (error) {
// 4. Error handling with retry
if (isOpenRouter && !this.didAutomaticallyRetryFailedApiRequest) {
await setTimeoutPromise(1000)
this.didAutomaticallyRetryFailedApiRequest = true
yield* this.attemptApiRequest(previousApiReqIndex)
return
}
// 5. Ask user to retry if automatic retry failed
const { response } = await this.ask(
"api_req_failed",
this.formatErrorWithStatusCode(error)
)
if (response === "yesButtonClicked") {
await this.say("api_req_retried")
yield* this.attemptApiRequest(previousApiReqIndex)
return
}
}
}
}
Key features:
Context Window Management
Streaming Architecture
Error Handling
Token Tracking
The Context Management System handles conversation history truncation to prevent context window overflow errors. Implemented in the ContextManager class, it ensures long-running conversations remain within model context limits while preserving critical context.
Key features:
Model-Aware Sizing: Dynamically adjusts based on different model context windows (64K for DeepSeek, 128K for most models, 200K for Claude).
Proactive Truncation: Monitors token usage and preemptively truncates conversations when approaching limits, maintaining buffers of 27K-40K tokens depending on the model.
Intelligent Preservation: Always preserves the original task message and maintains the user-assistant conversation structure when truncating.
Adaptive Strategies: Uses different truncation strategies based on context pressure - removing half of the conversation for moderate pressure or three-quarters for severe pressure.
Error Recovery: Includes specialized detection for context window errors from different providers with automatic retry and more aggressive truncation when needed.
The Task class provides robust task state management and resumption capabilities:
class Task {
async resumeTaskFromHistory() {
// 1. Load saved state
this.clineMessages = await getSavedClineMessages(this.getContext(), this.taskId)
this.apiConversationHistory = await getSavedApiConversationHistory(this.getContext(), this.taskId)
// 2. Handle interrupted tool executions
const lastMessage = this.apiConversationHistory[this.apiConversationHistory.length - 1]
if (lastMessage.role === "assistant") {
const toolUseBlocks = content.filter(block => block.type === "tool_use")
if (toolUseBlocks.length > 0) {
// Add interrupted tool responses
const toolResponses = toolUseBlocks.map(block => ({
type: "tool_result",
tool_use_id: block.id,
content: "Task was interrupted before this tool call could be completed."
}))
modifiedOldUserContent = [...toolResponses]
}
}
// 3. Notify about interruption
const agoText = this.getTimeAgoText(lastMessage?.ts)
newUserContent.push({
type: "text",
text: `[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context.`
})
// 4. Resume task execution
await this.initiateTaskLoop(newUserContent, false)
}
private async saveTaskState() {
// Save conversation history
await saveApiConversationHistory(this.getContext(), this.taskId, this.apiConversationHistory)
await saveClineMessages(this.getContext(), this.taskId, this.clineMessages)
// Create checkpoint
const commitHash = await this.checkpointTracker?.commit()
// Update task history
await this.controllerRef.deref()?.updateTaskHistory({
id: this.taskId,
ts: lastMessage.ts,
task: taskMessage.text,
// ... other metadata
})
}
}
Key aspects of task state management:
Task Persistence
State Recovery
Workspace Synchronization
Error Recovery
Cline implements a dual-mode system that separates planning from execution:
The Plan/Act mode system consists of:
chatSettings.mode in the Controller's statetogglePlanActModeWithChatSettings in the ControllerWhen switching between modes:
Plan mode is designed for:
In Plan mode, the AI uses the plan_mode_respond tool to engage in conversational planning without executing actions.
Act mode is designed for:
In Act mode, the AI has access to all tools except plan_mode_respond and focuses on implementation rather than discussion.
The Controller acts as the single source of truth for all persistent state. It:
The Task class manages terminal instances and command execution:
class Task {
async executeCommandTool(command: string): Promise<[boolean, ToolResponse]> {
// 1. Get or create terminal
const terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)
terminalInfo.terminal.show()
// 2. Execute command with output streaming
const process = this.terminalManager.runCommand(terminalInfo, command)
// 3. Handle real-time output
let result = ""
process.on("line", (line) => {
result += line + "\n"
if (!didContinue) {
sendCommandOutput(line)
} else {
this.say("command_output", line)
}
})
// 4. Wait for completion or user feedback
let completed = false
process.once("completed", () => {
completed = true
})
await process
// 5. Return result
if (completed) {
return [false, `Command executed.\n${result}`]
} else {
return [
false,
`Command is still running in the user's terminal.\n${result}\n\nYou will be updated on the terminal status and new output in the future.`
]
}
}
}
Key features:
Terminal Instance Management
Command Execution
The Task class handles browser automation through Puppeteer:
class Task {
async executeBrowserAction(action: BrowserAction): Promise<BrowserActionResult> {
switch (action) {
case "launch":
// 1. Launch browser with fixed resolution
await this.browserSession.launchBrowser()
return await this.browserSession.navigateToUrl(url)
case "click":
// 2. Handle click actions with coordinates
return await this.browserSession.click(coordinate)
case "type":
// 3. Handle keyboard input
return await this.browserSession.type(text)
case "close":
// 4. Clean up resources
return await this.browserSession.closeBrowser()
}
}
}
Key aspects:
Browser Control
Interaction Handling
The MCP system consists of:
src/services/mcp/McpHub.tsThe McpHub class:
Cline supports two types of MCP server connections:
The McpHub class provides methods for:
MCP tools are integrated into the Task execution system:
The MCP Marketplace provides:
The Controller class manages MCP servers through the McpHub service:
class Controller {
mcpHub?: McpHub
constructor(context: vscode.ExtensionContext, webviewProvider: WebviewProvider) {
this.mcpHub = new McpHub(this)
}
async downloadMcp(mcpId: string) {
// Fetch server details from marketplace
const response = await axios.post<McpDownloadResponse>(
"https://api.cline.bot/v1/mcp/download",
{ mcpId },
{
headers: { "Content-Type": "application/json" },
timeout: 10000,
}
)
// Create task with context from README
const task = `Set up the MCP server from ${mcpDetails.githubUrl}...`
// Initialize task and show chat view
await this.initClineWithTask(task)
}
}
This guide provides a comprehensive overview of the Cline extension architecture, with special focus on state management, data persistence, and code organization. Following these patterns ensures robust feature implementation with proper state handling across the extension's components.
Remember:
Contributions to the Cline extension are welcome! Please follow these guidelines:
When adding new tools or API providers, follow the existing patterns in the src/integrations/ and src/api/providers/ directories, respectively. Ensure that your code is well-documented and includes appropriate error handling.
The .clineignore file allows users to specify files and directories that Cline should not access. When implementing new features, respect the .clineignore rules and ensure that your code does not attempt to read or modify ignored files.