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.ts、handleTask.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
}