Cline Core CLI Agent:基于 ClineCore 有状态会话构建交互式终端编码 Agent 的完整实战
【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline
本文为 Cline 仓库中 cline-core-cli-agent 示例的配套技术指南。它演示了如何脱离无状态Agent类、直接使用ClineCore运行时构建一个带会话持久化、内置工具与工具审批能力的交互式终端聊天 Agent,读者读完后可掌握ClineCore.create()/start()/send()/stop()/dispose()的完整生命周期、toolPolicies审批策略配置,以及agent_event事件流的订阅与流式渲染方法。
1. 示例定位:为什么选择 ClineCore 而不是轻量运行时
该示例的 README 开篇即说明了它在示例体系中的定位:
An interactive terminal chat agent powered by the
ClineCoreruntime. This example is similar in spirit tocli-agent, but uses stateful ClineCore sessions and built-in runtime tools instead of the statelessAgentclass, to leverage Cline's internal agent harness.
即:它与 cli-agent 示例 目标相似(都是交互式终端聊天 Agent),但两者在运行时选型上完全不同:
| 维度 | cli-agent / quickstart | cline-core-cli-agent(本示例) |
|---|---|---|
| 运行时 | 无状态Agent类 | 有状态ClineCore运行时 |
| 会话 | 单轮、无状态 | 单sessionId承载多轮对话 |
| 工具 | 需自行定义 | 直接使用 Cline 内置运行时工具 |
| 持久化 | 无 | 具备 sessions 持久化能力 |
| 适用场景 | 最小 SDK 示例 | 需要完整 ClineCore 运行时(会话、持久化、内置工具)的场景 |
README 的 Notes 一节给出了明确的选型建议:当你想要"full ClineCore runtime with sessions, persistence, and built-in tools"时使用本示例;追求最小 SDK 示例时看 quickstart;追求轻量无状态运行时时看 cli-agent。
2. 环境准备与快速上手
2.1 前置依赖
从 package.json 可以看到该示例的技术栈约束:
{ "name": "@cline/example-cline-core-cli-agent", "type": "module", "scripts": { "dev": "bun run src/index.ts", "build:sdk": "bun run --cwd ../../.. build:sdk", "build": "tsc", "start": "node dist/index.js" }, "dependencies": { "@cline/sdk": "workspace:*" }, "engines": { "node": ">=22" } }关键事实:
- 依赖的是 monorepo 内部工作区包
@cline/sdk(workspace:*),因此必须在 Cline 仓库根目录下通过 workspace 安装; build:sdk脚本通过--cwd ../../..委托到仓库根目录执行 SDK 构建,说明示例依赖本地源码构建产物而非已发布的 npm 包;- 运行时要求 Node.js
>=22(engines字段),开发期使用 Bun 直接执行 TypeScript。
2.2 安装与配置
按照 README 的步骤:
# 1. 安装依赖并构建本地 SDK bun install bun run build:sdk # 2. 设置 API Key(Cline 网关密钥) export CLINE_API_KEY="sk_..." # 3. 启动交互式 Agent bun dev启动后终端会打印运行元信息(对应 src/index.ts 第 159–162 行):
ClineCore CLI Agent (type 'exit' to quit) Provider: cline Model: anthropic/claude-sonnet-4.6 CWD: /path/to/current/dir在you:提示符输入任意消息即可看到流式响应,输入exit退出。
2.3 可选的模型配置
README 指出示例默认使用 Cline 网关提供商与 Claude Sonnet,可通过环境变量覆盖:
export CLINE_PROVIDER_ID="cline" export CLINE_MODEL_ID="anthropic/claude-sonnet-4.6"源码中这三个环境变量的解析逻辑位于 src/index.ts 第 8–13 行,并附带一条重要的设计注释:
// ClineCore does not choose a model automatically; each session config must provide one. // These example defaults use the Cline gateway with Claude Sonnet, and can be overridden with env vars. const providerId = process.env.CLINE_PROVIDER_ID ?? "cline"; const modelId = process.env.CLINE_MODEL_ID ?? "anthropic/claude-sonnet-4.6"; const apiKey = process.env.CLINE_API_KEY; const cwd = process.cwd();这里传递了一个关键约束:ClineCore 不会自动选择模型,每个 session 的config必须显式提供providerId和modelId。环境变量只是便捷入口,真正生效的是它们被透传进start()的会话配置(见第 4 节参数表)。
3. 运行时生命周期:从 create 到 dispose
README "What it does" 一节概括了六项核心行为,源码逐条印证了它们。整个生命周期可归纳为五步:
3.1 惰性创建 ClineCore 实例
async function ensureCline(): Promise<ClineCore> { if (cline) { return cline; } cline = await ClineCore.create({ clientName: "cline-core-cli-agent", backendMode: "local", capabilities: { requestToolApproval, }, }); unsubscribe = cline.subscribe((event) => { if (event.type === "agent_event") { handleAgentEvent(event.payload.event); } }); return cline; }三个要点:
ClineCore.create()是ClineCore类(定义于 sdk/packages/core/src/ClineCore.ts)的静态工厂方法,该类是 "The primary entry point for the Cline Core SDK";backendMode: "local"表明运行时进程本地启动(而非连接远端 hub);capabilities.requestToolApproval把"工具执行审批"这一宿主能力注入运行时——这是ClineCoreOptions能力体系的一部分,ToolApprovalRequest类型定义在 sdk/packages/shared/src/agents/types.ts 中;- 通过
cline.subscribe()订阅CoreSessionEvent,并只过滤type === "agent_event"的事件进入本地渲染器(对应 README "Concepts demonstrated" 中 "CoreSessionEventsubscription viacline.subscribe()")。
3.2 启动会话:start() 与完整会话配置
async function startSession(): Promise<string> { const runtime = await ensureCline(); const result = await runtime.start({ source: "cli", interactive: true, config: { providerId, modelId, apiKey, cwd, workspaceRoot: cwd, mode: "act", systemPrompt, maxIterations: 10, enableTools: true, enableSpawnAgent: false, enableAgentTeams: false, disableMcpSettingsTools: true, }, toolPolicies: { "*": { autoApprove: false }, read_files: { autoApprove: true }, search_codebase: { autoApprove: true }, }, }); return result.sessionId; }start()返回result.sessionId,整个多轮对话就围绕这一个sessionId展开(README 所称 "Multi-turn conversation using a singlesessionId")。各会话配置参数含义如下:
| 参数 | 示例取值 | 说明 |
|---|---|---|
providerId | cline | 模型提供方,默认走 Cline 网关,可经CLINE_PROVIDER_ID覆盖 |
modelId | anthropic/claude-sonnet-4.6 | 具体模型 ID,可经CLINE_MODEL_ID覆盖 |
apiKey | $CLINE_API_KEY | 网关鉴权密钥 |
cwd/workspaceRoot | process.cwd() | Agent 工作目录与工作区根,内置工具的文件操作以此为基准 |
mode | "act" | 运行模式为执行态(非纯规划态) |
systemPrompt | 见下文 | 系统提示词,约束行为风格 |
maxIterations | 10 | 单轮内 Agent 循环迭代上限,防止工具调用无限循环 |
enableTools | true | 启用 Cline 内置运行时工具 |
enableSpawnAgent/enableAgentTeams | false | 禁用子 Agent 派生与多 Agent 团队,保持单 Agent 行为 |
disableMcpSettingsTools | true | 禁用 MCP 设置类工具,收窄 CLI 场景的能力面 |
source: "cli"与interactive: true用于标识会话来源与交互属性。系统提示词同样刻意保持克制:
const systemPrompt = `You are a helpful assistant in an interactive terminal chat. Be concise. You can use built-in tools to inspect files, search the workspace, and run shell commands when helpful.`;3.3 逐轮发送:send()
async function runTurn(input: string): Promise<void> { activeSessionId ??= await startSession(); hasPrintedAssistantPrefix = false; const runtime = await ensureCline(); await runtime.send({ sessionId: activeSessionId, prompt: input, }); console.log(); }注意activeSessionId ??= await startSession()的惰性初始化:首轮输入才创建会话,而非进程启动即建会话;后续轮次复用同一sessionId,由 ClineCore 维护上下文状态。
3.4 优雅停机:stop() 与 dispose()
} finally { rl.close(); unsubscribe?.(); if (activeSessionId && cline) { await cline.stop(activeSessionId).catch(() => undefined); } await cline?.dispose(); console.log("Goodbye!"); }finally块保证无论正常exit还是异常退出都完成清理:关闭 readline 接口、取消事件订阅、调用cline.stop(sessionId)结束会话(用.catch(() => undefined)容忍会话尚未建立或已失效的情况)、最后cline.dispose()释放运行时资源。这正是 README 所列 "Callscline.stop()andcline.dispose()during shutdown" 的实现。
4. 工具审批:toolPolicies 静态策略 + requestToolApproval 动态兜底
README "Concepts demonstrated" 中有一条值得展开:"Basic tool policies: file reads/search are auto-approved, other tools request approval"。该示例实际上演示了两层审批机制:
第一层:start()中的toolPolicies声明式策略
toolPolicies: { "*": { autoApprove: false }, // 默认所有工具都需审批 read_files: { autoApprove: true }, // 文件读取自动放行 search_codebase: { autoApprove: true }, // 代码库检索自动放行 },通配符*作为默认策略兜底,具体工具名做白名单式放行——只读类操作(read_files、search_codebase)低风险自动执行,写文件、执行命令等高风险工具落入第二层。
第二层:宿主回调requestToolApproval
async function requestToolApproval(request: ToolApprovalRequest) { console.log(`\n[approval] ${request.toolName} wants to run:`); console.log(formatToolValue(request.input)); const answer = await ask("Approve? [y/N] "); const approved = answer.trim().toLowerCase() === "y"; return { approved, ...(approved ? {} : { reason: "User denied tool execution" }), }; }未被自动审批的工具会触发该回调:打印工具名与输入参数、等待用户y/N应答,拒绝时附带reason说明。这个回调即ClineCore.create()时通过capabilities注入的宿主能力,体现了 Cline SDK 中"运行时发起、宿主决策"的权限模型。
内置工具方面,README 列举了read_files、search_codebase、run_commands等,源码注释也印证了"Uses ClineCore's built-in tools instead of defining custom tools"——示例没有注册任何自定义工具,全部依赖 Cline 的 internal agent harness。
5. 事件流渲染:agent_event 的四种内容类型
流式输出的核心是handleAgentEvent,它按AgentEvent的type分派(src/index.ts 第 36–72 行):
function handleAgentEvent(event: AgentEvent): void { switch (event.type) { case "content_start": if (event.contentType === "text" && event.text) { printAssistantPrefix(); process.stdout.write(event.text); // 文本增量直接写 stdout } if (event.contentType === "tool" && event.toolName) { console.log(`\n[tool] ${event.toolName}(${JSON.stringify(event.input ?? {})})`); } break; case "content_update": if (event.contentType === "tool" && event.toolName) { console.log(`[update] ${event.toolName}: ${formatToolValue(event.update)}`); } break; case "content_end": if (event.contentType === "tool" && event.toolName) { if (event.error) { console.log(`[error] ${event.toolName}: ${event.error}`); } else { console.log(`[result] ${formatToolValue(event.output)}`); } } break; case "notice": console.log(`\n[notice] ${event.message}`); break; case "error": console.error(`\n[error] ${event.error.message}`); break; } }事件语义可以归纳为一张表:
| 事件 type | contentType | 渲染行为 |
|---|---|---|
content_start | text | 打印一次agent:前缀,随后流式写出文本增量 |
content_start | tool | 打印[tool] 工具名(输入参数),标记工具调用开始 |
content_update | tool | 打印[update]工具执行中的增量信息 |
content_end | tool | 成功打印[result],失败打印[error] |
notice | - | 打印运行时提示信息 |
error | - | 以 stderr 打印错误信息 |
两个工程细节值得一提:
- 前缀去重:
printAssistantPrefix()用hasPrintedAssistantPrefix标志保证每个回答只打印一次agent:前缀;runTurn每轮开始时将其重置,对应 README "Streamsagent_eventtext to stdout" 的行为; - 长输出截断:
formatToolValue()对非字符串值做JSON.stringify后限制 200 字符(超出部分以...截断),避免工具结果把终端刷屏。
6. 交互式输入循环
外层是一个基于node:readline的极简 REPL(第 94–103、116–176 行):
const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); function ask(question: string): Promise<string> { return new Promise((resolve) => { rl.question(question, resolve); }); }主循环处理三种输入:exit(忽略大小写)触发退出流程进入finally清理;空输入直接continue等待下一轮;其余内容 trim 后交给runTurn发送。整个循环把"用户输入 → ClineCore 会话 → 事件流 → 终端渲染"串成一条清晰的单向数据流,没有任何多余的状态管理。
7. 小结:本示例的实战价值
这个约 185 行的 index.ts 是理解 ClineCore 编程模型的最小完整样本,它覆盖了:
- 运行时创建:
ClineCore.create()的backendMode与capabilities注入方式; - 有状态会话:
start()显式模型配置、单一sessionId多轮复用、stop()/dispose()资源清理; - 工具审批双层模型:
toolPolicies声明式自动审批 +requestToolApproval宿主回调兜底; - 事件驱动渲染:
subscribe()订阅CoreSessionEvent,按agent_event的 content 生命周期(start/update/end)流式输出文本与工具轨迹。
如果你需要一个能直接运行、带完整会话与内置工具能力的终端编码 Agent 起点,可以直接参考本示例的目录结构(package.json+tsconfig.json+src/index.ts)与依赖声明,在其基础上扩展工具策略、审批策略或事件消费逻辑。
【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考