Skip to content

Roo-Code工程结构


顶层目录

目录功能角色
src/主扩展:激活、命令、任务编排、与 Webview 通信、绝大多数业务逻辑。
webview-ui/侧栏 Webview 前端(Vite + React),用户交互主界面。
packages/共享库:跨扩展 / CLI / 评测等复用的类型、核心逻辑、云、遥测、构建工具等。
apps/周边应用:CLI、官网/文档站、E2E、Nightly 变体等。
scripts/安装引导、bootstrap、打 VSIX、code-server 等工程脚本。

src/ - 扩展主体

目录功能角色
src/extension.ts扩展入口(加载 .env、云/遥测、注册能力)
src/activate/激活期注册:命令、Code Action、URI 处理、终端相关等(registerCommands.tshandleTask.ts 等)
src/core/核心业务域(体量最大)
src/services/对外部能力/基础设施的封装
src/integrations/与 VS Code 编辑器/工作区粘合
src/api/providers/(各类模型/API 提供方)、transform/(请求/响应变换)
src/i18n/国际化资源
src/assets/图标等静态资源
src/workers/Worker 线程(例如 token 计数等重活)
src/extension/主要是测试等(当前子目录几乎只有 __tests__
src/shared/src/utils/src/types/扩展内共享类型与工具(与 packages/types 有分工:workspace 级类型常在 @roo-code/types
src/__tests__/src/__mocks__/测试与 mock

src/core - 架构分析

子目录大致职责
task/任务生命周期、与 Agent 循环相关编排。
tools/各类「工具调用」(读文件、改文件、执行命令等)的实现与调度。
prompts/系统提示、模式(Code/Ask/…)相关提示拼装。
context/context-management/context-tracking/上下文收集、压缩、追踪与策略。
message-manager/message-queue/消息状态与队列。
checkpoints/检查点/快照类能力(与撤销、恢复相关)。
auto-approval/自动批准策略。
diff/condense/protect/ignore/差异展示、摘要、保护路径、忽略规则等。
assistant-message/助手消息结构/处理。
config/核心配置相关逻辑。
environment/环境信息注入。
mentions/@ 引用等。
webview/扩展侧与 Webview 协议、消息桥接(与 webview-ui 配对)。
task-persistence/任务持久化。

🦖 core/task - 流程解析

core/task核心源码解析

startTask

ts
private async startTask(task?: string, images?: string[]): Promise<void> {
    try {
        // 清空历史
        this.clineMessages = [] // 界面消息
        this.apiConversationHistory = [] // 发给模型的历史

        // ...
        
        // 提取用户传入图片数组
        const imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images)

        // 开始任务loop
        await this.initiateTaskLoop([
            {
                type: "text",
                // 把用户输入包成<user_message>文本块
                text: `<user_message>\n${task}\n</user_message>`,
            },
            ...imageBlocks,
        ]).catch((error) => {
            // ...
        })
    } catch (error) {
        // ...
    }
}

initiateTaskLoop 外层轮询

ts
private async initiateTaskLoop(userContent: Anthropic.Messages.ContentBlockParam[]): Promise<void> {
    // ...
    let nextUserContent = userContent
    let includeFileDetails = true

    // 只要没取消,就调用内存轮询recursivelyMakeClineRequests
    while (!this.abort) {
        const didEndLoop = await this.recursivelyMakeClineRequests(nextUserContent, includeFileDetails)
        includeFileDetails = false // 第一次轮询会带上详细文件树,之后轮次不带。

        // ...

        if (didEndLoop) {
            // 如果内层返回结束轮询,则终止整段Task
            break
        } else {
            // 第一次轮询塞的是用户的原始输入文本、图片;
            // 后面的轮询塞的是一段内置提示词,这里是提醒模型使用工具进行思考,并提供工具介绍
            nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed() }]
        }
    }
}

recursivelyMakeClineRequests 内层轮询

ts
public async recursivelyMakeClineRequests(
    userContent: Anthropic.Messages.ContentBlockParam[],
    includeFileDetails: boolean = false,
): Promise<boolean> {
    interface StackItem {
        userContent: Anthropic.Messages.ContentBlockParam[]
        includeFileDetails: boolean
        retryAttempt?: number
        userMessageWasRemoved?: boolean
    }

    const stack: StackItem[] = [{ userContent, includeFileDetails, retryAttempt: 0 }]

    while (stack.length > 0) {
        const currentItem = stack.pop()!
        // ...

        // 【发请求前】1. 提示"正在请求"
        await this.say(
            "api_req_started",
            JSON.stringify({
                apiProtocol,
            }),
        )

        // ...

        // 【发请求前】2. 解析文本中的`@文件`的内容,塞入上下文中
        const { content: parsedUserContent, mode: slashCommandMode } = await processUserContentMentions({
            userContent: currentUserContent,
            cwd: this.cwd,
            fileContextTracker: this.fileContextTracker,
            rooIgnoreController: this.rooIgnoreController,
            showRooIgnoredFiles,
            includeDiagnosticMessages,
            maxDiagnosticMessages,
            skillsManager: provider?.getSkillsManager(),
            currentMode,
        })

        // ...

        // 【发请求前】3. 拼一大段当前工程/环境说明
        const environmentDetails = await getEnvironmentDetails(this, currentIncludeFileDetails)

        // 【发请求前】4. 历史用户输入信息及环境信息处理,避免重复塞入上下文
        // ...

        try {
            // ...

            // 【真正调用模型】1. 基于底层ApiHandler(详见src/api, 基于各类模型供应商封装的请求体),主要提供能力:
            // 读模型供应商配置、拼系统提示词、上下文压缩、组工具,调用最底层openAI接口发请求
            const stream = this.attemptApiRequest(currentItem.retryAttempt ?? 0, { skipProviderRateLimit: true })
            
            // ...

            try {
                // ...
                while (!item.done) {
                    // ...
                    
                    // 【真正调用模型】2. 对模型返回的stream流做asyncIterator迭代(模型分chunk流式返回),根据chunk.type分类处理
                    switch (chunk.type) {
                        // `reasoning`,在窗口展示思考链路文本
                        case "reasoning": {
                            // ...

                        }
                        // `usage`,更新token/费用
                        case "usage":
                            // ...

                        //  `tool_call_partial`,工具调用还在「边传边拼」(例如参数 JSON 还没传完)
                        // `assistantMessageContent`:当前轮次模型输出的结构化草稿,非最终发送给用户结果
                        // `presentAssistantMessage`:从assistantMessageContent中取出指令,真正执行的地方
                        case "tool_call_partial": {
                            // ...

                            for (const event of events) {
                                // 新开一个工具槽位,确认id+工具名
                                if (event.type === "tool_call_start") {
                                    // ...
                                    this.assistantMessageContent.push(partialToolUse) // 提前push一个工具占位
                                    // ...
                                    presentAssistantMessage(this) // 窗口显示`调用xxx工具`
                                } else if (event.type === "tool_call_delta") { // 传入工具参数JSON
                                    // ...

                                    this.assistantMessageContent[toolUseIndex] = partialToolUse // 工具占位内更新参数

                                    presentAssistantMessage(this) // 窗口流式显示参数信息
                                    // ...
                                } else if (event.type === "tool_call_end") { // 工具参数传递完毕,工具定义封装结束
                                    // ...

                                    // 将工具完整内容替换掉占位内
                                    this.assistantMessageContent[toolUseIndex] = finalToolUse
                                    
                                    // ...

                                    // 真正执行工具(问批准、跑命令)
                                    presentAssistantMessage(this)

                                    // ...
                                }
                            }
                        }

                        case "tool_call": { // 一条完整的工具定义封装,含ID、工具名、参数定义,相当于完整的`tool_call_partial`过程,不用重新执行`start`、`delta、`end`
                            // ...
                            this.assistantMessageContent.push(toolUse)

                            // ...
                            presentAssistantMessage(this)
                            // ... 
                        }
                        case "text": { // 助手回复的自然语言,和`工具调用无关`
                            // ...
                            this.assistantMessageContent.push({
                                type: "text",
                                content: assistantMessage,
                                partial: true,
                            })

                            // ...
                            presentAssistantMessage(this)

                            // ...
                        }
                    }

                    // ...
                }

            } 


            // ...

            if (hasTextContent || hasToolUses) {
                
                // 如果本轮有工具定义,等待 presentAssistantMessage 执行工具完成
                await pWaitFor(() => this.userMessageContentReady)

                // ...

                // 结果塞回堆栈,在内层轮询中再加一轮
                stack.push({
                    userContent: [...this.userMessageContent], // Create a copy to avoid mutation issues
                    includeFileDetails: false, // Subsequent iterations don't need file details
                })

            } else {
                // ...
                
                // 本轮没有使用工具,累加`连续不用工具计数`,必要时报错并再一轮塞入要求使用工具
                stack.push({
                    userContent: currentUserContent,
                    includeFileDetails: false,
                    retryAttempt: (currentItem.retryAttempt ?? 0) + 1,
                    userMessageWasRemoved: true,
                })

                // ...
                } else {

                    // 其他场景重新执行轮询
                    stack.push({
                        userContent: currentUserContent,
                        includeFileDetails: false,
                        retryAttempt: (currentItem.retryAttempt ?? 0) + 1,
                    })
                }
            }

            // 一轮走完,没有命中任何一个判断逻辑中的continue或者throw,则这里抛出false,在外层`initiateTaskLoop` 里会判断再加一轮工具调用提示词
            return false
        } catch (error) {
            // 兜底策略,未知BUG,则抛出true,直接中断外层`initiateTaskLoop`,整个任务结束
            return true
        }
    }

    return false
}