Cursor 的开源平替产品 Cline介绍
Cursor 确实很强。只需要简单闲聊几句,它就能根据你的需求,开发一个完整的软件应用。
但是 Cursor 对于国内用户来说有很多局限性,比如网络受限,比如价格昂贵,很不接地气。
于是便出现了一些开源平替,其中最著名的当属 VS Code 插件 Cline。
Cline 完全免费,支持接入各种主流大模型 API,包括 DeepSeek,使用体验非常接近 Cursor。
当然 Cline 不会免费提供模型 API,需要自己接入。
Cline 将 AI 能力与开发工具深度集成,提供了一个统一的智能化开发环境。通过智能理解开发者意图,自动化执行开发任务,并保持对项目上下文的持续理解,显著提升了开发效率和体验。
而且它还是开源的,这么一个优秀的开源项目,必须得好好研究学习一下。
本文将从架构设计、核心模块实现等角度,深入解析 Cline 背后的技术原理和实现细节。
概述
Cline 插件采用模块化设计,主要由以下核心组件构成:
- ClineProvider:负责状态管理和 UI 交互
- Cline:核心业务逻辑处理
- 外部服务集成:包括 AI 服务、认证服务等
- 工具系统:提供文件操作、命令执行等能力
通过这些组件的协同工作,Cline 实现了四个核心目标:
- 工具统一
- 在 VS Code 中集成文件操作、命令执行、浏览器控制等能力
- 提供统一的界面来执行各种开发任务
- 减少工具切换带来的认知负担
- 智能自动化
- 通过 AI 理解开发者意图
- 自动完成重复性工作
- 智能化的代码理解和生成
- 上下文保持
- 维护对项目结构的持续理解
- 在任务执行过程中保持状态连贯
- 支持长时间运行的复杂任务
- 安全可控
- 提供操作确认机制
- 支持任务的暂停和恢复
- 实现关键操作的回滚能力
整体架构概览
Cline 的整体架构可以分为四个主要部分:
-
VS Code Extension Layer
-
Webview Panel
-
Sidebar
-
Commands
-
extension.ts:插件入口点
-
用户界面组件:
-
Core System
-
assistant-message:消息处理
-
ignore:忽略规则
-
prompts:提示管理
-
sliding-window:对话管理
-
webview:UI 交互
-
ClineProvider:状态管理和视图控制
-
Cline:核心业务逻辑
-
核心模块:
-
State Management
-
Global State:全局状态
-
Workspace State:工作区状态
-
Secrets Storage:密钥存储
-
External Services
-
Anthropic API
-
OpenAI API
-
OpenRouter
-
AI Services:
-
Authentication:认证服务
-
Browser Control:浏览器控制
核心架构图

插件入口
export function activate(context: vscode.ExtensionContext) {
// 1. 创建输出通道
outputChannel = vscode.window.createOutputChannel("Cline");
// 2. 初始化日志系统
Logger.initialize(outputChannel); // 3. 创建侧边栏提供者
const sidebarProvider = new ClineProvider(context, outputChannel); // 4. 注册侧边栏视图
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
ClineProvider.sideBarId,
sidebarProvider,
{
webviewOptions: {
retainContextWhenHidden: true
}
}
)
);
}// 注册了多个VS Code命令:
- "cline.plusButtonClicked" // 新建对话
- "cline.mcpButtonClicked" // MCP按钮
- "cline.popoutButtonClicked" // 弹出到新标签页
- "cline.settingsButtonClicked" // 设置
- "cline.historyButtonClicked" // 历史记录
- "cline.accountLoginClicked" // 账号登录
ClineProvider.ts

cline.ts

提示词
提示词分为几个部分:
- 角色定义:定义角色信息
- 工具调用:
- 文件操作:读文件,写文件 (覆盖),替换文件内容 (diff 形式),查文件,list 文件,list 代码定义
- 浏览器操作:浏览器操作 (使用 puppeteer 访问和点击操作,执行操作并且返回截图和控制台日志)
- 终端操作:执行命令
- MCP 操作:MCP 工具调用和资源请求
- 基础操作:询问用户问题,输出命令结果 (用于展示任务完成后的结果,标志结束)
- 规划模式:规划模式定义 (帮助用户规划和脑暴解决方案)
- 工具定义
- 内置工具列表:
- 调用例子和调用指南
- MCP 服务:MCP 定义,动态注入 MCP 服务器列表,MCP 创建示例和指南
- 替换文件和创建文件的区别:两者的定义和使用场景以及优势,考虑改写之后用户编辑器的自动格式化 (直接输出最终状态),工作流程的一些建议
- ACT 模式和 PLAN 模式:让 AI 理解 act 模式和 plan 模式,act 模式可以调用除 plan_mode_response 工具之外的所有工具,而 plan 模式则使用 plan_mode_response
- 能力:指定 AI 具有什么能力,比如能调用哪些工具以及这些工具是干什么的
- 规则:指定 AI 需要遵守的规则,能做什么,不能做什么
- 系统信息:操作系统、默认 shell,home 目录,当前工作目录
- 目标:宏观指定 AI 完成任务的步骤
- 用户自定义指南
完整提示词:https://github.com/cline/cline/blob/main/src/core/prompts/system.ts
核心执行流程
任务执行流程
- 任务初始化
- 用户通过 Webview 界面输入任务
- Webview 将消息传递给 ClineProvider
- ClineProvider 创建和初始化 Cline 实例
- 任务执行循环
- Cline 向 AI 服务发送请求
- AI 服务返回响应和可能的工具调用
- Cline 解析响应并执行相应的工具
- 需要用户确认的操作会请求用户确认
- 工具执行结果返回给 Cline
- Cline 将结果返回给 AI 服务继续处理
- 任务完成
- 当所有必要的步骤都完成后
- Cline 向用户返回最终结果
- 任务执行完成

工具执行流程

核心组件实现
ClineProvider Cline 核心类
class ClineProvider implements vscode.WebviewViewProvider {
private cline?: Cline
private view?: vscode.WebviewView
private context: vscode.ExtensionContext
// 核心管理功能
async initClineWithTask(task?: string)
async handleMessage(message: Message)
async updateState(state: State) // 定义全局文件名
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json", // API对话历史
uiMessages: "ui_messages.json", // UI消息
openRouterModels: "openrouter_models.json", // OpenRouter模型
mcpSettings: "cline_mcp_settings.json", // MCP设置
clineRules: ".clinerules", // Cline规则
}
}
3
class ClineProvider {
constructor(context: vscode.ExtensionContext) {
this.context = context
this.cline = undefined // 延迟初始化
}
async initClineWithTask(task?: string) {
await this.clearTask() // 清理现有任务 // 获取配置
const config = await this.getState() // 创建新实例
this.cline = new Cline(
this,
config.apiConfiguration,
config.autoApprovalSettings,
config.browserSettings,
config.chatSettings,
config.customInstructions
)
}
}
1
// 启动任务循环
await this.initiateTaskLoop(
[{
type: "text",
text: <task>n${task}n</task>,
},
...imageBlocks],
true // isNewTask 标记
);
}
}
// 状态控制
private didEditFile: boolean = false;
private abort: boolean = false;
private consecutiveMistakeCount: number = 0;
// 历史记录
apiConversationHistory: [Anthropic](https://www.explinks.com/api/ai_anthropic_brand).MessageParam[] = [];
clineMessages: ClineMessage[] = [];
// 配置系统
autoApprovalSettings: AutoApprovalSettings;
private browserSettings: BrowserSettings;
private chatSettings: ChatSettings;
// 任务执行核心方法
async startTask(task?: string, images?: string[]): Promise {
// 初始化状态
this.clineMessages = [];
this.apiConversationHistory = [];
// 更新界面
await this.providerRef.deref()?.postStateToWebview();
// 显示任务
await this.say("text", task, images);
// 标记初始化完成
this.isInitialized = true;
// 构建初始消息
let imageBlocks = formatResponse.imageBlocks(images);
// 启动任务循环
await this.initiateTaskLoop(
[{
type: "text",
text: <task>n${task}n</task>,
},
...imageBlocks],
true // isNewTask 标记
);
}
}
任务循环实现
private async initiateTaskLoop(
userContent: UserContent,
isNewTask: boolean
): Promise {
let nextUserContent = userContent;
let includeFileDetails = true;
while (!this.abort) {
// 核心请求处理
const didEndLoop = await this.recursivelyMakeClineRequests(
nextUserContent,
includeFileDetails,
isNewTask
); includeFileDetails = false; if (didEndLoop) {
break;
} else {
// 处理未使用工具的情况
nextUserContent = [{
type: "text",
text: formatResponse.noToolsUsed(),
}];
this.consecutiveMistakeCount++;
}
}
}
任务结束的条件
// 1. this.abort 为 true 的情况:
- 用户手动中止
- 发生严重错误
- Cline 实例被销毁
// 2. didEndLoop 为 true 的情况:
- 任务成功完成(使用 attempt_completion 工具)
- 达到最大错误次数
- 任务被标记为完成// 3. 其他终止条件:
- 连续错误次数过多
- API 调用失败
- 工具执行失败
请求处理系统
递归请求处理
private async recursivelyMakeClineRequests(
userContent: UserContent,
includeFileDetails: boolean,
isNewTask: boolean
): Promise {
// 1. 错误检查
if (this.consecutiveMistakeCount >= MAX_CONSECUTIVE_MISTAKES) {
throw new Error("Too many consecutive mistakes")
}
try {
// 2. 准备消息
const messages = this.prepareMessages(userContent, includeFileDetails) // 3. 发送 API 请求
const response = await this.api.sendRequest(messages) // 4. 处理响应
if (response.type === "completion") {
return true // 任务完成
} else if (response.type === "tool_use") {
await this.handleToolUse(response.tool)
return false // 继续执行
}
} catch (error) {
this.handleError(error)
return true
}
}
消息准备
private prepareMessages(
userContent: UserContent,
includeFileDetails: boolean
): MessageParam[] {
// 1. 构建基础消息
const messages = [...this.apiConversationHistory]
// 2. 添加环境详情
if (includeFileDetails) {
messages.push({
type: "environment_details",
content: this.getEnvironmentDetails()
})
} // 3. 添加用户内容
messages.push({
type: "user_content",
content: userContent
}) return messages
}
API 通信系统
class ApiHandler {
async *attemptApiRequest(previousApiReqIndex: number) {
// 1. 系统提示准备
let systemPrompt = await SYSTEM_PROMPT(
cwd,
this.api.getModel().info.supportsComputerUse,
mcpHub,
this.browserSettings
);
// 2. 上下文预处理
const { truncatedHistory, deletedRange } = this.truncateHistoryToFitContext(
previousApiReqIndex
); // 3. 流式通信
const stream = this.api.createMessage(systemPrompt, truncatedHistory);
yield* this.handleStreamResponse(stream);
} private truncateHistoryToFitContext(previousIndex: number) {
const totalTokens = this.calculateTokensUsage(); switch (this.contextWindow) {
case 64_000: // deepseek models
this.maxAllowedSize = this.contextWindow - 27_000;
break;
case 128_000: // most models
this.maxAllowedSize = this.contextWindow - 30_000;
break;
} if (totalTokens >= this.maxAllowedSize) {
return this.performTruncation();
} return { truncatedHistory: this.history };
}
}
工具
export const toolUseNames = [
"execute_command",
"read_file",
"write_to_file",
"replace_in_file",
"search_files",
"list_files",
"list_code_definition_names",
"browser_action",
"use_mcp_tool",
"access_mcp_resource",
"ask_followup_question",
"plan_mode_response",
"attempt_completion",
] as const
终端命令执行工具
文件操作工具
class FileOperations implements Tool {
// 文件读取
async readFile(path: string): Promise {
return fs.readFile(path, 'utf8');
}
// 文件写入
async writeFile(path: string, content: string): Promise {
await this.ensureDirectoryExists(path);
await fs.writeFile(path, content);
} // 文件替换
async replaceInFile(path: string, diff: string): Promise {
const content = await this.readFile(path);
const newContent = await constructNewFileContent(diff, content);
await this.writeFile(path, newContent);
} // 文件列表
async listFiles(path: string, recursive: boolean): Promise {
if (recursive) {
return this.recursiveListFiles(path);
}
return fs.readdir(path);
}
}
安全机制实现
文件操作安全
class FileOperationApproval {
async checkFileAccess(operation: FileOperation): Promise {
// 1. 路径安全检查
if (!this.isPathSafe(operation.path)) {
return false;
}
// 2. 操作权限检查
if (!this.hasPermission(operation.type)) {
return false;
} // 3. 内容安全检查
if (operation.type === 'write' && !this.isContentSafe(operation.content)) {
return false;
} return true;
} private isPathSafe(path: string): boolean {
// 检查路径是否在工作目录内
// 防止目录遍历攻击
const normalizedPath = normalizePath(path);
return isWithinWorkspace(normalizedPath);
}
}
命令执行安全
class CommandSecurity {
private readonly restrictedCommands: Set = new Set([
'rm -rf',
'format',
'shutdown',
// ... 其他危险命令
]);
async validateCommand(command: string): Promise {
// 1. 检查是否是受限命令
if (this.isRestrictedCommand(command)) {
return false;
} // 2. 检查命令权限
if (!await this.checkCommandPermissions(command)) {
return false;
} // 3. 检查资源限制
if (!this.checkResourceLimits(command)) {
return false;
} return true;
} private isRestrictedCommand(command: string): boolean {
return Array.from(this.restrictedCommands).some(
restricted => command.includes(restricted)
);
}
}
检查点系统
三种情况:task (恢复任务消息)、workspace (恢复工作区文件)、taskAndWorkspace (同时恢复任务消息和工作区文件)
核心机制:ShadowGit (主仓库的变更不会影响到 Shadow 仓库,shadow 仓库的变更会形成主仓库的未提交的更改)
核心流程:
- 如果恢复 workspace 或 taskAndWorkspace,则会首先初始化 checkpointTracker,并通过 resetHead 直接将工作区恢复到指定 git commit。
- 对于 task 或 taskAndWorkspace,我们还需要恢复 task 历史消息记录,同时记录回滚的 APi 资源消耗
class CheckpointSystem {
private checkpoints: Map = new Map();
// 创建检查点
async createCheckpoint(): Promise {
const checkpoint = {
id: generateId(),
timestamp: Date.now(),
state: await this.captureCurrentState(),
files: await this.captureFileState()
}; this.checkpoints.set(checkpoint.id, checkpoint);
return checkpoint.id;
} // 恢复检查点
async restoreCheckpoint(id: string): Promise {
const checkpoint = this.checkpoints.get(id);
if (!checkpoint) {
throw new Error('Checkpoint not found');
} await this.restoreState(checkpoint.state);
await this.restoreFiles(checkpoint.files);
}
}
文件存储
文件存储主要分为以下几类:
Task 对话流,配置文件 (主要是 mcp 服务器信息),mcp 服务端代码,自定义 prompt (。clinerules)
Task 存储
存储位置:~/Library/Application Support/Code/User/globalStorage/扩展名/tasks/任务 ID
- api_conversation_history.json:存储消息和上下文
[
{
"role": "user",
"content": [
{ "type": "text", "text": "n你给我优化一下代码n" },
{
"type": "text",
"text": "n
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:08 (Asia/Shanghai, UTC+8:00)nn# Current Working Directory (/Users/mlhiter/personal-projects/cherrix) Filesn.env.templaten.gitignorenauth.config.tsnauth.tsncomponents.jsonneslint.config.mjsnmiddleware.tsnnext-auth.d.tsnnext.config.tsnpackage.jsonnpnpm-lock.yamlnpostcss.config.mjsnprettier.config.jsnREADME.mdnroutes.tsntailwind.config.tsntsconfig.jsonnactions/nactions/login.tsnactions/logout.tsnactions/new-password.tsnactions/new-verification.tsxnactions/register.tsnactions/reset.tsnactions/settings.tsnapp/napp/favicon.iconapp/globals.cssnapp/layout.tsxnapp/page.tsxnapp/(protected)/napp/api/napp/api/auth/napp/api/auth/[...nextauth]/napp/auth/napp/auth/layout.tsxnapp/auth/error/napp/auth/error/page.tsxnapp/auth/login/napp/auth/login/page.tsxnapp/auth/new-password/napp/auth/new-password/page.tsxnapp/auth/new-verification/napp/auth/new-verification/page.tsxnapp/auth/register/napp/auth/register/page.tsxnapp/auth/reset/napp/auth/reset/page.tsxncomponents/ncomponents/form-error.tsxncomponents/form-success.tsxncomponents/user-info.tsxncomponents/auth/ncomponents/auth/back-button.tsxncomponents/auth/card-wrapper.tsxncomponents/auth/error-card.tsxncomponents/auth/header.tsxncomponents/auth/login-button.tsxncomponents/auth/login-form.tsxncomponents/auth/logout-button.tsxncomponents/auth/new-password-form.tsxncomponents/auth/new-verification-form.tsxncomponents/auth/register-form.tsxncomponents/auth/reset-form.tsxncomponents/auth/role-gate.tsxncomponents/auth/social.tsxncomponents/auth/user-button.tsxncomponents/ui/ncomponents/ui/avatar.tsxncomponents/ui/badge.tsxncomponents/ui/button.tsxncomponents/ui/card.tsxncomponents/ui/dialog.tsxncomponents/ui/dropdown-menu.tsxncomponents/ui/form.tsxncomponents/ui/input.tsxncomponents/ui/label.tsxncomponents/ui/select.tsxncomponents/ui/sonner.tsxncomponents/ui/switch.tsxndata/ndata/account.tsndata/password-reset-token.tsndata/two-factor-confirmation.tsndata/two-factor-token.tsndata/user.tsndata/verification-token.tsnhooks/nhooks/use-current-role.tsnhooks/use-current.user.tsnlib/nlib/auth.tsnlib/db.tsnlib/mail.tsnlib/tokens.tsnlib/utils.tsnprisma/nprisma/schema.prismanschemas/nschemas/index.tsnn# Current ModenACT MODEn"
}
]
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "n1. 需要优化代码,但用户没有指定具体文件。从环境信息中看到当前打开的文件是 lib/auth.tsn2. 首先需要查看 lib/auth.ts 的内容,了解当前代码结构,才能进行优化n3. 使用 read_file 工具读取 lib/auth.ts 文件内容nnnnlib/auth.tsn"
}
]
},
{
"role": "user",
"content": [
{ "type": "text", "text": "[read_file for 'lib/auth.ts'] Result:" },
{
"type": "text",
"text": "import { auth } from '@/auth'nnexport const currentUser = async () => {n const session = await auth()n return session?.usern}nnexport const currentRole = async () => {n const session = await auth()n return session?.user.rolen}n"
},
{
"type": "text",
"text": "n# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:15 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn"
}
]
},
{
"role": "assistant",
"content": [
{
"type": "text", "text": "n1. 当前代码存在以下可以优化的地方:n - 两个函数都调用了 auth(),存在重复代码n - 没有错误处理机制n - 返回类型不明确n - 可以添加缓存机制提高性能nn2. 优化方案:n - 提取公共的 auth() 调用n - 添加类型定义n - 添加错误处理n - 添加简单的内存缓存nnnnlib/auth.tsnn<<<<<< {n const session = await auth()n return session?.usern}nnexport const currentRole = async () => {n const session = await auth()n return session?.user.rolen}n=======nimport { auth } from '@/auth'nimport { User } from 'next-auth'nn// Simple in-memory cachenlet cachedSession: { user: User } | null = nullnlet lastFetchTime = 0nconst CACHE_DURATION = 1000 * 60 // 1 minutennasync function getSession() {n const now = Date.now()n if (!cachedSession || now - lastFetchTime > CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}n>>>>>>> REPLACEnn" }
]
},
...
]
- ui_messages.json:存储 UI 上显示的信息
VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:12:54 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:15167,tokensOut:73,cacheWrites:191,cacheReads:14976,cost:0.000256844}",
"conversationHistoryIndex": 11
},
{
"ts": 1737537176056,
"type": "say",
"say": "text",
"text": "1. 这是一个 Next.js 项目,从文件结构可以看出n2. 需要运行开发服务器来启动项目n3. 使用 execute_command 工具运行开发服务器",
"partial": false,
"conversationHistoryIndex": 12
},
{
"ts": 1737537177951,
"type": "say",
"say": "command",
"text": "pnpm dev",
"conversationHistoryIndex": 12
},
{
"ts": 1737537179473,
"type": "ask",
"ask": "command_output",
"text": "",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179473,
"type": "ask",
"ask": "command_output",
"text": "pnpm> pnpm dev",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "pnpm devu0007",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "> cherrix@0.1.0 dev /Users/mlhiter/personal-projects/cherrix",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "> next dev –turbopack",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " ▲ Next.js 15.1.3 (Turbopack)",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " – Local: http://localhost:3000",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " – Network: http://192.168.0.123:3000",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " – Environments: .env",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": "",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " ✓ Starting…",
"conversationHistoryIndex": 13
},
{
"ts": 1737537181139,
"type": "ask",
"ask": "command_output",
"text": " ✓ Compiled in 338ms",
"conversationHistoryIndex": 13
},
{
"ts": 1737537181414,
"type": "ask",
"ask": "command_output",
"text": " ✓ Ready in 1017ms",
"conversationHistoryIndex": 13
}
]
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:08 (Asia/Shanghai, UTC+8:00)nn# Current Working Directory (/Users/mlhiter/personal-projects/cherrix) Filesn.env.templaten.gitignorenauth.config.tsnauth.tsncomponents.jsonneslint.config.mjsnmiddleware.tsnnext-auth.d.tsnnext.config.tsnpackage.jsonnpnpm-lock.yamlnpostcss.config.mjsnprettier.config.jsnREADME.mdnroutes.tsntailwind.config.tsntsconfig.jsonnactions/nactions/login.tsnactions/logout.tsnactions/new-password.tsnactions/new-verification.tsxnactions/register.tsnactions/reset.tsnactions/settings.tsnapp/napp/favicon.iconapp/globals.cssnapp/layout.tsxnapp/page.tsxnapp/(protected)/napp/api/napp/api/auth/napp/api/auth/[...nextauth]/napp/auth/napp/auth/layout.tsxnapp/auth/error/napp/auth/error/page.tsxnapp/auth/login/napp/auth/login/page.tsxnapp/auth/new-password/napp/auth/new-password/page.tsxnapp/auth/new-verification/napp/auth/new-verification/page.tsxnapp/auth/register/napp/auth/register/page.tsxnapp/auth/reset/napp/auth/reset/page.tsxncomponents/ncomponents/form-error.tsxncomponents/form-success.tsxncomponents/user-info.tsxncomponents/auth/ncomponents/auth/back-button.tsxncomponents/auth/card-wrapper.tsxncomponents/auth/error-card.tsxncomponents/auth/header.tsxncomponents/auth/login-button.tsxncomponents/auth/login-form.tsxncomponents/auth/logout-button.tsxncomponents/auth/new-password-form.tsxncomponents/auth/new-verification-form.tsxncomponents/auth/register-form.tsxncomponents/auth/reset-form.tsxncomponents/auth/role-gate.tsxncomponents/auth/social.tsxncomponents/auth/user-button.tsxncomponents/ui/ncomponents/ui/avatar.tsxncomponents/ui/badge.tsxncomponents/ui/button.tsxncomponents/ui/card.tsxncomponents/ui/dialog.tsxncomponents/ui/dropdown-menu.tsxncomponents/ui/form.tsxncomponents/ui/input.tsxncomponents/ui/label.tsxncomponents/ui/select.tsxncomponents/ui/sonner.tsxncomponents/ui/switch.tsxndata/ndata/account.tsndata/password-reset-token.tsndata/two-factor-confirmation.tsndata/two-factor-token.tsndata/user.tsndata/verification-token.tsnhooks/nhooks/use-current-role.tsnhooks/use-current.user.tsnlib/nlib/auth.tsnlib/db.tsnlib/mail.tsnlib/tokens.tsnlib/utils.tsnprisma/nprisma/schema.prismanschemas/nschemas/index.tsnn# Current ModenACT MODEn,tokensIn:11870,tokensOut:87,cacheWrites:11614,cacheReads:256,cost:0.001653904}",
"conversationHistoryIndex": -1
},
{
"ts": 1737537012356,
"type": "say",
"say": "text",
"text": "1. 需要优化代码,但用户没有指定具体文件。从环境信息中看到当前打开的文件是 lib/auth.tsn2. 首先需要查看 lib/auth.ts 的内容,了解当前代码结构,才能进行优化n3. 使用 read_file 工具读取 lib/auth.ts 文件内容",
"partial": false,
"conversationHistoryIndex": 0
},
{
"ts": 1737537014438,
"type": "say",
"say": "tool",
"text": "{tool:readFile,path:lib/auth.ts,content:/Users/mlhiter/personal-projects/cherrix/lib/auth.ts}",
"partial": false,
"conversationHistoryIndex": 0,
"lastCheckpointHash": "a6d8812056f5cf1220148390772dba1a528b95ac"
},
{
"ts": 1737537015217,
"type": "say",
"say": "api_req_started",
"text": "{request:[read_file for 'lib/auth.ts'] Result:nnimport { auth } from '@/auth'nnexport const currentUser = async () => {n const session = await auth()n return session?.usern}nnexport const currentRole = async () => {n const session = await auth()n return session?.user.rolen}nnnn
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:15 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:12095,tokensOut:405,cacheWrites:255,cacheReads:11840,cost:0.00031486}",
"conversationHistoryIndex": 1
},
{
"ts": 1737537017423,
"type": "say",
"say": "text",
"text": "1. 当前代码存在以下可以优化的地方:n - 两个函数都调用了 auth(),存在重复代码n - 没有错误处理机制n - 返回类型不明确n - 可以添加缓存机制提高性能nn2. 优化方案:n - 提取公共的 auth() 调用n - 添加类型定义n - 添加错误处理n - 添加简单的内存缓存",
"partial": false,
"conversationHistoryIndex": 2
},
{
"ts": 1737537020284,
"type": "say",
"say": "tool",
"text": "{tool:editedExistingFile,path:lib/auth.ts,content:<<<<<< {n const session = await auth()n return session?.usern}nnexport const currentRole = async () => {n const session = await auth()n return session?.user.rolen}n=======nimport { auth } from '@/auth'nimport { User } from 'next-auth'nn// Simple in-memory cachenlet cachedSession: { user: User } | null = nullnlet lastFetchTime = 0nconst CACHE_DURATION = 1000 * 60 // 1 minutennasync function getSession() {n const now = Date.now()n if (!cachedSession || now - lastFetchTime > CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}n>>>>>>> REPLACE}",
"partial": false,
"conversationHistoryIndex": 2,
"lastCheckpointHash": "212e268f2f768fce47f65215b40725a967e6c2b4"
},
{
"ts": 1737537031730,
"type": "say",
"say": "api_req_started",
"text": "{request:[replace_in_file for 'lib/auth.ts'] Result:nnThe content was successfully saved to lib/auth.ts.nnHere is the full, updated content of the file that was saved:nnnimport { auth } from '@/auth'nimport { User } from 'next-auth'nn// Simple in-memory cachenlet cachedSession: { user: User } | null = nullnlet lastFetchTime = 0nconst CACHE_DURATION = 1000 * 60 // 1 minutennasync function getSession() {n const now = Date.now()n if (!cachedSession || now - lastFetchTime > CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}nnnnIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.nnnnNew problems detected after saving the file:nlib/auth.tsn- [ts Error] Line 30: 类型“User”上不存在属性“role”。nnn
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:31 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:0,tokensOut:0,cacheWrites:0,cacheReads:0,cost:0}",
"conversationHistoryIndex": 3
},
{
"ts": 1737537034848,
"type": "say",
"say": "text",
"text": "1. 出现类型错误,因为 User 类型上缺少 role 属性n2. 需要扩展 next-auth 的 User 类型定义n3. 在 next-auth.d.ts 文件中添加类型扩展",
"partial": false,
"conversationHistoryIndex": 4
},
{
"ts": 1737537036421,
"type": "say",
"say": "tool",
"text": "{tool:editedExistingFile,path:next-auth.d.ts,content:<<<<<<< SEARCHnimport { DefaultSession } from next-auth;nndeclare module next-auth {n interface Session {n user: {n id: string;n } & DefaultSession[user];n }n}n======}",
"partial": true,
"conversationHistoryIndex": 4
},
{
"ts": 1737537037472,
"type": "say",
"say": "diff_error",
"text": "next-auth.d.ts",
"conversationHistoryIndex": 4
},
{
"ts": 1737537037561,
"type": "say",
"say": "api_req_started",
"text": "{request:[replace_in_file for 'next-auth.d.ts'] Result:nnThe tool execution failed with the following error:nnThe SEARCH block:nimport { DefaultSession } from next-auth;nndeclare module next-auth {n interface Session {n user: {n id: string;n } & DefaultSession[user];n }n}n...does not match anything in the file.nnThis is likely because the SEARCH block content doesn't match exactly with what's in the file, or if you used multiple SEARCH/REPLACE blocks they may not have been in the order they appear in the file.nnThe file was reverted to its original state:nnnimport { type DefaultSession } from 'next-auth'nimport { UserRole } from '@prisma/client'nexport type ExtendedUser = DefaultSession['user'] & {n role: UserRolen isTwoFactorEnabled: booleann isOAuth: booleann}nndeclare module 'next-auth' {n interface Session {n user: ExtendedUsern }n}nnnnTry again with a more precise SEARCH block.n(If you keep running into this error, you may use the write_to_file tool as a workaround.)nnnn
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:37 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:13424,tokensOut:534,cacheWrites:496,cacheReads:12928,cost:0.00039995199999999997}",
"conversationHistoryIndex": 5
},
{
"ts": 1737537039528,
"type": "say",
"say": "text",
"text": "1. 从错误信息中看到 next-auth.d.ts 文件已经定义了 ExtendedUser 类型,包含 role 属性n2. 需要更新 lib/auth.ts 中的类型定义以使用 ExtendedUsern3. 使用 replace_in_file 工具更新 lib/auth.ts 的类型定义",
"partial": false,
"conversationHistoryIndex": 6
},
{
"ts": 1737537041539,
"type": "say",
"say": "tool",
"text": "{tool:editedExistingFile,path:lib/auth.ts,content:<<<<<< CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}n=======nimport { auth } from '@/auth'nimport { ExtendedUser } from 'next-auth'nn// Simple in-memory cachenlet cachedSession: { user: ExtendedUser } | null = nullnlet lastFetchTime = 0nconst CACHE_DURATION = 1000 * 60 // 1 minutennasync function getSession() {n const now = Date.now()n if (!cachedSession || now - lastFetchTime > CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}n>>>>>>> REPLACE}",
"partial": false,
"conversationHistoryIndex": 6,
"lastCheckpointHash": "c70f2140b610333799ec281a4004e462e2434b89"
},
{
"ts": 1737537056632,
"type": "say",
"say": "api_req_started",
"text": "{request:[replace_in_file for 'lib/auth.ts'] Result:nnThe content was successfully saved to lib/auth.ts.nnHere is the full, updated content of the file that was saved:nnnimport { auth } from '@/auth'nimport { ExtendedUser } from 'next-auth'nn// Simple in-memory cachenlet cachedSession: { user: ExtendedUser } | null = nullnlet lastFetchTime = 0nconst CACHE_DURATION = 1000 * 60 // 1 minutennasync function getSession() {n const now = Date.now()n if (!cachedSession || now - lastFetchTime > CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}nnnnIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.nnnnNew problems detected after saving the file:nlib/auth.tsn- [ts Error] Line 2: 模块 \next-auth\ 没有导出的成员 ExtendedUser。你是想改用 import ExtendedUser from next-auth\ 吗?nnn
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:10:56 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:14423,tokensOut:141,cacheWrites:1047,cacheReads:13376,cost:0.000373324}",
"conversationHistoryIndex": 7
},
{
"ts": 1737537058830,
"type": "say",
"say": "text",
"text": "1. 出现新的类型错误,因为 ExtendedUser 没有从 next-auth 模块正确导出n2. 需要从 next-auth.d.ts 中导入 ExtendedUser 类型n3. 使用 replace_in_file 工具更新 lib/auth.ts 的导入语句",
"partial": false,
"conversationHistoryIndex": 8
},
{
"ts": 1737537060971,
"type": "say",
"say": "tool",
"text": "{tool:editedExistingFile,path:lib/auth.ts,content:<<<<<<>>>>>> REPLACE}",
"partial": false,
"conversationHistoryIndex": 8,
"lastCheckpointHash": "1a7d627dcc7386d2185abf42a35c26ca161ab4bb"
},
{
"ts": 1737537067107,
"type": "say",
"say": "api_req_started",
"text": "{request:[replace_in_file for 'lib/auth.ts'] Result:nnThe content was successfully saved to lib/auth.ts.nnHere is the full, updated content of the file that was saved:nnnimport { auth } from '@/auth'nimport type { ExtendedUser } from '@/next-auth.d.ts'nn// Simple in-memory cachenlet cachedSession: { user: ExtendedUser } | null = nullnlet lastFetchTime = 0nconst CACHE_DURATION = 1000 * 60 // 1 minutennasync function getSession() {n const now = Date.now()n if (!cachedSession || now - lastFetchTime > CACHE_DURATION) {n try {n cachedSession = await auth()n lastFetchTime = nown } catch (error) {n console.error('Failed to fetch session:', error)n return nulln }n }n return cachedSessionn}nnexport const currentUser = async (): Promise => {n const session = await getSession()n return session?.user ?? nulln}nnexport const currentRole = async (): Promise => {n const session = await getSession()n return session?.user?.role ?? nulln}nnnnIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.nnnnn
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:11:07 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:14981,tokensOut:73,cacheWrites:581,cacheReads:14400,cost:0.00030337999999999996}",
"conversationHistoryIndex": 9
},
{
"ts": 1737537068631,
"type": "say",
"say": "text",
"text": "",
"partial": false,
"conversationHistoryIndex": 10
},
{
"ts": 1737537068761,
"type": "say",
"say": "completion_result",
"text": "已完成代码优化:n1. 添加了内存缓存机制,减少重复调用 auth()n2. 添加了错误处理n3. 完善了类型定义n4. 解决了类型错误问题nn优化后的代码具有更好的性能、更强的健壮性和更好的类型安全性。HAS_CHANGES",
"partial": false,
"conversationHistoryIndex": 10,
"lastCheckpointHash": "ba03330c546aabe2673b238725cd5584813c9101"
},
{
"ts": 1737537071615,
"type": "ask",
"ask": "completion_result",
"text": "",
"conversationHistoryIndex": 11
},
{
"ts": 1737537174467,
"type": "say",
"say": "user_feedback",
"text": "帮我运行起来这个服务器",
"images": [],
"conversationHistoryIndex": 11
},
{
"ts": 1737537174500,
"type": "say",
"say": "api_req_started",
"text": "{request:[attempt_completion] Result:nnThe user has provided feedback on the results. Consider their input to continue the task, and then attempt completion again.nn帮我运行起来这个服务器nnnn
# VSCode Visible Filesnlib/auth.tsnn# VSCode Open Tabsnlib/auth.tsnn# Current Timen2025/1/22 下午5:12:54 (Asia/Shanghai, UTC+8:00)nn# Current ModenACT MODEn,tokensIn:15167,tokensOut:73,cacheWrites:191,cacheReads:14976,cost:0.000256844}",
"conversationHistoryIndex": 11
},
{
"ts": 1737537176056,
"type": "say",
"say": "text",
"text": "1. 这是一个 Next.js 项目,从文件结构可以看出n2. 需要运行开发服务器来启动项目n3. 使用 execute_command 工具运行开发服务器",
"partial": false,
"conversationHistoryIndex": 12
},
{
"ts": 1737537177951,
"type": "say",
"say": "command",
"text": "pnpm dev",
"conversationHistoryIndex": 12
},
{
"ts": 1737537179473,
"type": "ask",
"ask": "command_output",
"text": "",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179473,
"type": "ask",
"ask": "command_output",
"text": "pnpm> pnpm dev",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "pnpm devu0007",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "> cherrix@0.1.0 dev /Users/mlhiter/personal-projects/cherrix",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "> next dev --turbopack",
"conversationHistoryIndex": 13
},
{
"ts": 1737537179679,
"type": "ask",
"ask": "command_output",
"text": "",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " ▲ Next.js 15.1.3 (Turbopack)",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " - Local: http://localhost:3000",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " - Network: http://192.168.0.123:3000",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " - Environments: .env",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": "",
"conversationHistoryIndex": 13
},
{
"ts": 1737537180524,
"type": "ask",
"ask": "command_output",
"text": " ✓ Starting...",
"conversationHistoryIndex": 13
},
{
"ts": 1737537181139,
"type": "ask",
"ask": "command_output",
"text": " ✓ Compiled in 338ms",
"conversationHistoryIndex": 13
},
{
"ts": 1737537181414,
"type": "ask",
"ask": "command_output",
"text": " ✓ Ready in 1017ms",
"conversationHistoryIndex": 13
}
]
- checkpoints:检查点信息
- git:存储记录检查点的影子仓库数据
配置信息存储
存储位置:~/Library/Application Support/Code/User/globalStorage/扩展名/settings
cline_mcp_settings.json:存储 mcp 的信息,包括 ID、参数,环境变量等信息
{
"mcpServers": {
"npm-docs": {
"command": "node",
"args": [
"/Users/mlhiter/Documents/Cline/MCP/npm-docs-server/build/index.js"
],
"env": {},
"disabled": false,
"autoApprove": [
"get_docs"
]
}
}
}
mcp 服务端代码存储位置
- 存储位置:
~/Documents/Cline/MCP
总结
通过对 Cline 开源项目的深入分析,我们可以看到其优秀的架构设计和工程实现为 AI 辅助开发工具树立了新的标杆。Cline 的模块化架构设计、基于 ShadowGit 的检查点系统、多层级文件存储方案以及安全可控的任务执行机制,为构建智能开发环境提供了重要参考。
热门API
- 1. AI文本生成
- 2. AI图片生成_文生图
- 3. AI图片生成_图生图
- 4. AI图像编辑
- 5. AI视频生成_文生视频
- 6. AI视频生成_图生视频
- 7. AI语音合成_文生语音
- 8. AI文本生成(中国)
最新文章
- 如何使用 node.js 和 express 创建 rest api
- 「Flask + Python」RESTful API 极速上手:从 Hello World 到 Docker 容器化 + Auth0 鉴权(含 AI 提效外挂)
- 「API 设计」7 步全流程指南:从需求到最佳实践,一篇就够!
- 「电子签名 API」18 强全景速通:功能、集成、KPI、代码一次给全!
- 2025年暑假大学生AI副业+联盟营销指南:自动化文章与链接实现月入过万
- 如何在Python中使用ChatGPT API?
- FastAPI 异步编程:提升 API 性能
- 什么是 LangChain
- Google News API 的热门话题与趋势分析
- GraphQL API渗透测试指南
- GitHub Copilot API接入指南
- Bun API 入门指南 – Apidog