cc-linker
让手机聊天应用和终端(Claude Code CLI)之间的对话切换,像切换设备一样无缝。
目前已接入飞书,更多聊天平台持续扩展中。
语言: 中文 | English
为什么需要 cc-linker?
你是否遇到过这样的场景:
- 通勤路上用手机聊,到公司终端继续 — 地铁上用手机飞书给 Bot 发消息讨论技术方案,到公司打开终端
cc-linker list找到会话,resume一键恢复上下文 - 飞书快速提问,终端深度调试 — 在飞书里快速问了个 API 用法,发现需要本地调试,终端
cc-linker resume切换到同一会话继续让 Claude 帮你写代码 - 多项目并行,会话不乱 — 同时在
project-a和project-b两个目录与 Claude 对话,/list清晰展示每个会话的目录和状态,卡片按钮一键切换不混淆
cc-linker 就是解决这些痛点的桥接工具。 它在你电脑上维护一个统一的会话注册表,让手机聊天应用和 Claude Code CLI 共享同一套会话状态——无论你在哪个端发起对话,都能无缝切换到另一端继续。
当前已接入飞书,更多聊天平台持续扩展中。
核心特性
| 特性 | 说明 |
|---|---|
| 跨端无缝切换 | 聊天应用发起的对话,终端一键恢复(含上下文和目录);终端创建的会话,聊天应用随时查看 |
| 流式卡片交互 | 聊天应用中实时看到 Claude 的 thinking、tool_use 摘要(≤80 字符)和回复,5s tick 显示已用时,不再是"转圈等待" |
| 交互式权限确认 | SDK 模式下 Claude 需要执行工具时,飞书卡片弹出允许/拒绝按钮 |
| 图片消息支持 | 飞书发送的图片自动下载并传递给 Claude 分析 |
| 目录浏览 | /listDir 命令交互式浏览和切换工作目录 |
| 统一会话管理 | 自动扫描、增量同步,无需手动维护会话列表 |
| 多模型切换 | 在卡片中一键切换模型,无需改配置 |
| 持久化不丢消息 | 文件级消息队列,进程崩溃、重启后消息不丢失 |
| 3 步上手 | install → setup → start,5 分钟完成配置 |
效果展示
聊天应用端体验(飞书)
当前已支持飞书,更多平台开发中。
会话列表/list 查看所有会话 |
开始处理 消息发出后即时反馈 |
流式实时反馈 thinking + tool_use + 回复,5s tick |
![]() |
![]() |
![]() |
| 处理完成 token / 耗时 / 轮数统计 |
处理完成(长回复) 长文本同样展示 |
模型切换 卡片按钮一键切换 |
![]() |
![]() |
![]() |
目录浏览/listDir 交互式浏览文件系统 |
SDK 权限确认 交互式允许/拒绝 Claude 的工具调用 |
图片消息 飞书发送图片,Claude 自动分析 |
![]() |
![]() |
![]() |
终端端体验
查看所有会话 — 清晰的表格展示,状态一目了然:

一键恢复会话 — 支持前缀匹配,自动切换目录并恢复上下文:

快速开始
1. 安装 Bun(必需运行时)
cc-linker 基于 Bun 构建,Bun >= 1.0 是硬性前置依赖。
macOS / Linux:
curl -fsSL https://bun.sh/install | bash
macOS(Homebrew):
brew tap oven-sh/bun && brew install bun
装完开新终端验证:
bun --version # 应输出 >= 1.0.0
没生效? PATH 没更新。手动把
~/.bun/bin加入PATH,或直接重启终端。
2. 安装 cc-linker
方式 1:npm 全局安装(需要 Node.js >= 20 提供 npm,运行 cc-linker 仍需 Bun)
npm install -g cc-linker@latest
方式 2:Bun 全局安装(推荐)
bun add -g cc-linker
更新提示:
npm install -g cc-linker@latest时,如果 daemon 正在运行,会自动调用cc-linker restart升级到新版,无需手动重启。
3. 一键配置
cc-linker setup
交互式向导会引导你完成:
- 初始化会话注册表
- 安装 Claude Code 自动注册钩子
- 配置聊天应用 Bot(当前仅飞书:App ID + App Secret + 开机自启)
仅需终端侧功能? 运行
cc-linker setup --skip-feishu跳过聊天应用配置。
4. 开始使用
| 场景 | 操作 |
|---|---|
| 聊天应用中给 Bot 发消息(飞书) | 直接对话,流式卡片实时更新 |
| 终端查看所有会话 | cc-linker list |
| 终端恢复某个会话 | cc-linker resume <UUID> |
| 聊天应用切换会话(飞书) | /switch <序号|UUID> |
| 聊天应用选择新会话目录(飞书) | /listDir 浏览目录,为下一条消息选择新会话目录 |
| 聊天应用创建新会话(飞书) | /new [路径] [--model <别名>] [-- 提示词] |
命令参考
CLI 命令
cc-linker list # 列出所有会话
cc-linker resume <UUID> # 恢复指定会话到终端(支持前缀匹配)
cc-linker show <UUID> # 查看会话详情
cc-linker sync # 手动同步两端会话
cc-linker search <关键词> # 搜索会话
cc-linker export <UUID> # 导出会话为 Markdown/JSON/Text
cc-linker clean # 清理无效记录
cc-linker status # 查看桥接状态
聊天应用 Bot 命令(飞书)
在飞书私聊中给 Bot 发送:
| 命令 | 说明 |
|---|---|
/help |
显示帮助 |
/list |
列出会话(带切换/恢复按钮卡片) |
/listDir |
浏览目录,并为下一条消息选择新会话目录 |
/new [路径] [--model <别名>] [-- 提示词] |
立即创建新会话,或只预设新会话目录/模型 |
/switch <序号|UUID> |
切换会话 |
/resume <序号|UUID> |
获取终端恢复命令 |
/model [序号|别名|--clear] |
查看、设置或清除默认模型 |
/agents |
查看终端后台 claude session 列表(按 busy / waiting / idle / completed 分组),见下文「 Agent View 集成」 |
/status |
查看状态 |
/stop |
硬杀当前会话的 claude 进程(普通 session / bg 都生效,状态自动清) |
/cancel |
软退 Agent View 等待输入状态——只清 pending_agent_reply,bg 继续跑,下一条 chat 消息会走默认路径而非 rendezvous |
/whoami |
获取你的 open_id |
说明 1:
/switch和/resume中的数字序号来自最近一次/list生成的列表快照,默认 10 分钟内有效;超时后请重新执行/list。说明 2:
/new支持”先选目录/模型,后发第一条消息再真正创建会话”的用法;/model设置的是当前用户的默认模型,直到/model --clear清除。说明 3:
/stopvs/cancel区别——/stop杀进程(不可恢复),/cancel只清等待状态(bg 继续)。想立刻退掉”我刚点了 [Reply]”的等待状态时用/cancel;想杀掉卡死的 Claude 进程时用/stop。两者互不影响。
Bot 运行管理
| 命令 | 说明 |
|---|---|
cc-linker start |
前台启动(阻塞终端) |
cc-linker start --daemon |
后台守护进程模式 |
cc-linker stop |
停止后台 Bot |
cc-linker restart |
重启 Bot 服务 |
cc-linker daemon install |
配置开机自动启动 |
cc-linker daemon uninstall |
移除开机自启 |
cc-linker daemon status |
查看后台服务状态 |
Agent View 集成
Agent View 是 cc-linker 的"远端会话接管"能力:在飞书里查看终端后台 claude session 的实时状态,直接 Peek 日志、Reply 文字、Stop 进程或 Attach 回主对话流。主数据源是 ~/.claude/jobs/<short>/state.json(Claude CLI 维护的权威状态机),需 Claude Code CLI ≥ 2.1.139 且 daemon 正在运行。
Fork 自动续接(v2.6+): 当 bg session 被
claude --resume --fork续接到新 TUI 时(例如你开了多个 TUI 切换),cc-linker 会自动用最新的活 session 接收你的 reply。不需要手动指定 ——/agents列表 / [Peek] / [Reply] / [Attach] 全部按"对话实际活跃位置"工作。旧 card 上点 [Reply] 也会自动跳到 fork 的 TUI,不会报 "Claude Code process exited with code 1"。
命令与按钮语义
| 入口 | 行为 |
|---|---|
/agents |
拉取所有 background session 快照,按 busy / waiting / idle / completed 四组展示(completed 组最多 5 条,超出折叠为 "… N more"),发一张可交互列表卡;列表超过 25KB 自动降级为纯文本 |
列表卡 [Peek] |
三级降级获取 session 最新 assistant 输出:Tier 1 直读 JSONL(通过 state.json.linkScanPath 或 JsonlIndex)→ Tier 2 读 roster parent JSONL(resume-from 场景)→ Tier 3 兜底 claude logs;发独立 peek 卡 |
列表卡 [Reply] |
仅 waiting 状态出现:弹" 等待输入回复"卡片,5 分钟内直接发文字即可。处理中卡每 5s 自动刷新(thinking + tool_use + 回复)。bg 跑了又问新问题时,直接在 chat 接着打字即可,无需再点 [Reply](v2.4.x 链式回复) |
列表卡 [Stop] |
仅 busy 状态出现:先弹二次确认卡(防误触),确认后 claude stop <shortId> |
列表卡 [Attach] |
把 openId 切到该 session,后续普通消息自动走 SDK 注入(保留用户级 defaultProvider);同时自动启动 Attached Card 实时刷新(见下文) |
列表卡 [Refresh] |
patch 原卡(2 秒防抖);messageId 不匹配则发新卡,避免误 patch 已被覆盖的旧卡 |
列表卡 [返回聊天] |
纯文本回复,无状态变更,退回到普通消息流 |
Peek 卡 [❌ 取消等待] |
清除 pending_agent_reply 状态(仅当 Reply 等待中出现时显示) |
/cancel |
同上,文字命令版本 |
配置
config.toml 新增 [agent_view] 段(全部可选项,默认值已适用大多数场景):
[agent_view]
# 总开关。设为 false 关闭 /agents 命令和所有 card action 处理
# enabled = true
# /agents 列表卡 [Refresh] 按钮防抖间隔(ms)
# refresh_min_interval_ms = 2000
# Tier 3 兜底(claude logs)取最近多少行输出;Tier 1/2 直读 JSONL 时此值不生效
# peek_lines = 30
# Peek 卡 recentOutput 字段字节上限(超出按字符截断,避免飞书卡片体积超限)
# peek_max_bytes = 2048
# waiting → 用户多久不发文字就自动取消(ms)
# expected_reply_timeout_ms = 300000
# 是否过滤 daemon 派生的 sub-agent(source=spare)session;只保留用户主动发起的 session
# background_only = true
# Stop 按钮是否需要二次确认卡
# stop_requires_confirm = true
# Claude CLI 最低版本要求,低于则不启用 Agent View
# min_claude_version = "2.1.139"
# Reply 路径下相邻两个 reply 之间的最小间隔(ms),防止 spam
# reply_throttle_ms = 500
# v2.4 GA: 通过 rendezvous socket 提交 + state.json 轮询注入 reply
# 默认开启(v2.4 GA 之前为 opt-in)。设为 false 退回 v2.3.5 SDK 路径
# rendezvous_enabled = true
# v2.4: 提交 reply 后等待 state.json 进入终态的最大时间(ms)
# rendezvous_timeout_ms = 60000
v2.4 GA 默认行为:Reply 默认直接走 rendezvous 通道,无需手动开启。如果 bg 不支持 rendezvous(daemon 离线、bg 正在真 processing、socket 不存在),自动降级到旧 SDK 路径。如需强制走旧行为,显式设
rendezvous_enabled = false。
对应环境变量(优先级高于配置文件;min_claude_version、rendezvous_enabled、rendezvous_timeout_ms 仅支持配置文件):
| 变量 | 字段 |
|---|---|
CC_LINKER_AGENT_VIEW_ENABLED |
enabled |
CC_LINKER_AGENT_VIEW_REFRESH_MIN_INTERVAL_MS |
refresh_min_interval_ms |
CC_LINKER_AGENT_VIEW_PEEK_LINES |
peek_lines |
CC_LINKER_AGENT_VIEW_PEEK_MAX_BYTES |
peek_max_bytes |
CC_LINKER_AGENT_VIEW_EXPECTED_REPLY_TIMEOUT_MS |
expected_reply_timeout_ms |
CC_LINKER_AGENT_VIEW_BACKGROUND_ONLY |
background_only |
CC_LINKER_AGENT_VIEW_STOP_REQUIRES_CONFIRM |
stop_requires_confirm |
CC_LINKER_AGENT_VIEW_REPLY_THROTTLE_MS |
reply_throttle_ms |
前提条件:本机需运行
claudedaemon(>=agent_view.min_claude_version)。/agents会先用claude --version做版本守卫,再检查~/.claude/daemon/roster.json是否存在,最后读~/.claude/jobs/*/state.json构建快照。版本不达标或 daemon 未运行时返回红色错误卡,不污染用户主流程。
waiting 状态判定(v2.4.x)
简单说:只要 bg 在等用户回答(无论是显式 blocked 还是"伪 busy 实 waiting"),就会出现在 waiting 组、带 [Reply] 按钮。 真正的实现走 checkRendezvousEligibility 决策树,覆盖三种情况:
tempo=blocked && needs— bg 显式等待用户回答state in {running, working} && needs— CLI 2.1.163 行为:worker 进程在跑但实际在等用户("伪 busy 实 waiting")state=blocked— 无 needs 也归 waiting(保守策略,让用户主动看 [Peek] 决定)
不在上述三种里的 busy/idle session 不会出现 [Reply] 按钮 —— 这时候要么 bg 在真 processing(tempo=active 无 needs),要么已经 done / stopped。
Reply UX 改进(v2.4.x)
用户视角:点 [Reply] 之后看到的是一张分层卡 —— 原" 回复"等待卡保留,飞书侧新发一张处理中卡。处理中卡上有三个区域(每 5 秒刷新一次):
- 思考过程:Claude 当前的 thinking 块(拼接显示)
- 当前操作:Claude 正在调用的工具名 + input 摘要(≤80 字符,方便扫一眼知道在干啥)
- 回复:Claude 已经生成的最终回复文本
bg 跑了又问新问题时,处理中卡无缝切回"等待输入"卡(同卡 patch,不重建),用户直接在 chat 接着打字就行。5 秒刷新是设计而非卡顿 —— 想强制刷新就点 [Refresh]。
实现细节(可选阅读)
- 分层卡片:原" 回复"等待卡保留为历史,新发处理中卡(
buildProcessingCard() = buildStreamingCard('', '', 0),card-updater.ts:483-484);bg 再问新问题(new_needs)→ 同卡patchWaitingCard(bot.ts:1602-1618),不调cancel(),避免" 已取消"灰色卡误报 - 5s tick elapsed time:流式 patch 节流
bot.ts:1484 - JSONL 富内容:
readLastAssistantTurn解析 thinking 块(\n拼接)+ tool_use 名 + input 摘要(INPUT_SUMMARY_MAX = 80) - new_needs 链式回复:rendezvous 返回
new_needs→runChatSDK透传bgAskedNewQuestion给handleReply→handleReply重新set()expectedReply - markSent (M1) 防双重 reply:T2 立即
expectedReply.markSent(openId),60s 等待期内 chat 消息不再走 rendezvous(manager.ts:915-917+expected-reply-state.ts:130)
Live Progress Card(实时进度卡)
/switch 到一个正在处理的 session 时,飞书侧的概览卡会每 10 秒自动刷新(LiveProgressWatcher),展示最新 assistant 输出、已运行时间、距上次输出的间隔,直到 session 结束或用户发新消息/切换/手动停止。
[feishu_bot.live_progress]
# interval_ms = 10000 # 轮询间隔(约 133 分钟最大时长 = 800 ticks)
# max_ticks = 800
# max_patch_failures = 3 # 连续 3 次飞书 API 失败自动停止
Attached Card 自动刷新
[Attach] 成功后,飞书侧自动创建 AttachedCardWatcher,每 10 秒轮询目标 session 并 patch 卡片,展示最新 assistant 输出和运行状态。卡片设置 update_multi: true 防止飞书并发 patch 导致内容回退。停止条件与 Live Progress Card 相同(session 结束、patch 连续失败、用户干预、bot 关闭等)。
Session Activity Sync(CLI 侧活跃检测)
当 CLI 端正在使用某个 session 时,从飞书发消息到同一 session 会弹出黄色警告卡,提示并发冲突风险。用户可选择:
- 等待 CLI 处理完毕
- 点击
⚠️ 我了解风险,仍要发送强制发送(60 秒无操作自动强发) - 发新消息自动覆盖旧强发请求
检测信号优先级:activity marker sidecar 文件 > OS 进程检测(按 cwd 找 claude 进程 + 子进程采样 CPU)> JSONL mtime 双采样。
可通过 Claude Code hooks 配置 cc-linker activity-hook 子命令,实现 100% 精确的 CLI 侧活跃上报(详见下文"启用 CLI 端 activity marker"节)。
BG-Conflict 拒绝卡(安全保护)
当用户 Attach 到一个仍有 daemon background worker 运行的 session 后发消息,飞书弹出三选一恢复卡:
| 按钮 | 行为 |
|---|---|
[停 bg 后继续发送] |
执行 claude stop → 等待 worker 退出 → fallback 到 parent session → 发送消息 |
[开新会话发送] |
创建全新 session 发送消息,不影响原 worker |
[取消] |
取消发送,bg worker 不受影响 |
此机制防止两个 claude 进程共享同一 cwd 导致文件写入冲突(安全 > 便利)。
接入飞书(第一个支持的聊天平台)
cc-linker 的架构设计支持接入多种聊天应用,飞书是第一个已实现的平台。后续可扩展支持其他 IM 平台。
在配置飞书 Bot 前,需要在 飞书开放平台 创建应用并配置权限。
创建应用
- 访问 https://open.feishu.cn/app → 创建企业自建应用
- 在「应用功能」→「机器人」中启用 Bot 能力
- 获取 App ID 和 App Secret(凭证与基础信息)
必需权限
进入「权限管理」,搜索并开通以下权限:
| 权限 | 用途 |
|---|---|
im:message |
读取和发送消息 |
im:message:send_as_bot |
以应用身份发送消息 |
im:message:readonly |
获取消息详情(REST 主动读取) |
im:message.p2p_msg:readonly |
接收用户发给 Bot 的单聊消息(事件推送必装) |
im:resource |
下载用户发送的图片资源 |
陷阱提醒:
im:message.p2p_msg:readonly≠im:message:readonly。 飞书im.message.receive_v1事件订阅页的"所需权限"列表只列前者; 如果只开通im:message:readonly(REST 主动读取),即使 WebSocket 连上了, Bot 也收不到任何私聊消息。cc-linker 只处理chat_type === 'p2p'私聊消息, 所以这条权限是必装的。漏配会导致cc-linker setup卡 120s 抓不到open_id。
必需事件订阅
进入「事件与回调」,按以下两个位置分别添加:
事件配置:
| 事件 | 用途 |
|---|---|
im.message.receive_v1 |
接收用户发给 Bot 的消息 |
im.chat.member.bot.added_v1 |
Bot 被邀请进群时触发(可选) |
回调配置:
| 回调 | 用途 |
|---|---|
card.action.trigger |
接收卡片按钮点击(/list 切换会话、模型切换、SDK 权限确认等交互) |
重要:订阅方式选择 WebSocket(不是 HTTP 回调)。
说明:
card.action.trigger是卡片交互的基础,不添加会导致/list、/model、SDK 权限确认等所有卡片按钮点击无响应。
发布应用
配置完权限后,进入「版本管理与发布」→ 创建版本 → 发布。只有发布后的权限才会生效。
配置说明
配置文件:~/.cc-linker/config.toml(可选,不创建则使用默认值)
[general]
log_level = "info"
claude_bin = "claude"
[feishu_bot]
# owner_open_id = "ou_xxx"
# default_cwd = "/path/to/workspace"
[stream]
enabled = true
throttle_ms = 1500
show_thinking = true
max_card_bytes = 25000
fallback_to_text = true
[claude]
# 权限模式:控制 Claude Code 执行操作时的交互确认行为
# 由于飞书端无法完成终端式交互确认,默认自动接受文件编辑
# 可选值:acceptEdits / auto / bypassPermissions / default / dontAsk / plan
permission_mode = "acceptEdits"
# 工具白名单(可选):显式允许的工具列表
# 默认空数组,表示遵从 Claude Code 本地设置(~/.claude/settings.json)
# 若配置此项,会覆盖本地设置。示例:["Read", "Edit", "Bash(git *)"]
# allowed_tools = []
# 工具黑名单(可选):显式禁止的工具列表
# 默认空数组,表示遵从 Claude Code 本地设置
# 若配置此项,会覆盖本地设置。示例:["Bash", "Write"]
# disallowed_tools = []
[sdk]
# Agent SDK 模式(支持飞书卡片上的交互式权限确认)
# 默认开启。如需关闭,设为 false
# enabled = true # 默认 true,支持飞书端交互式权限确认
# permission_mode = "acceptEdits" # SDK 基础权限模式
# timeout_ms = 600000 # 权限确认超时(10分钟)
# claude_executable = "claude" # Claude 可执行文件路径
[images]
# 图片消息处理(默认开启)
# enabled = true # 默认 true,自动下载飞书图片并传递给 Claude
# max_size_bytes = 10485760 # 图片大小限制(默认 10MB)
# cleanup_max_age_hours = 24 # 过期图片清理周期(默认 24 小时)
注意: SDK 模式需要系统已安装 claude 命令行工具(npm install -g @anthropic-ai/claude-code)。如需自定义可执行文件路径,可使用 general.claude_bin 或 sdk.claude_executable。
cc-linker 默认使用 SDK 自带的 claude 二进制(保证版本兼容)。若该二进制在 npm install 时被 omit(如
--omit=optional、NODE_ENV=production),cc-linker 会自动 fallback 到general.claude_bin(通常是系统 PATH 中的claude),并在日志中输出 WARN。如遇版本不兼容,可显式设置sdk.claude_executable指向兼容版本。
补充: SDK 权限确认卡片如果发送失败,或用户在超时时间内未确认,当前实现会自动拒绝该次工具调用。
环境变量覆盖:
| 环境变量 | 说明 |
|---|---|
CC_LINKER_DIR |
覆盖 cc-linker 数据目录(默认 ~/.cc-linker) |
CC_LINKER_CONFIG_PATH |
指定配置文件路径 |
CC_LINKER_REGISTRY_PATH |
指定 registry 文件路径 |
CC_LINKER_LOG_PATH |
指定日志文件路径 |
CC_LINKER_FEISHU_APP_ID |
飞书 App ID |
CC_LINKER_FEISHU_APP_SECRET |
飞书 App Secret |
CC_LINKER_FEISHU_OWNER_OPEN_ID |
限制仅指定用户使用 |
CC_LINKER_FEISHU_DEFAULT_CWD |
默认工作目录 |
CC_LINKER_STREAM_ENABLED |
流式响应开关 |
CC_LINKER_LOG_LEVEL |
日志级别 |
CC_LINKER_CLAUDE_PERMISSION_MODE |
Claude Code 权限模式 |
CC_LINKER_CLAUDE_ALLOWED_TOOLS |
允许的工具列表(逗号分隔) |
CC_LINKER_CLAUDE_DISALLOWED_TOOLS |
禁止的工具列表(逗号分隔) |
CC_LINKER_SDK_ENABLED |
启用 Agent SDK 模式(true/false,默认 true) |
CC_LINKER_SDK_PERMISSION_MODE |
SDK 权限模式 |
CC_LINKER_SDK_TIMEOUT_MS |
权限确认超时(毫秒) |
CC_LINKER_SDK_CLAUDE_EXECUTABLE |
Claude 可执行文件路径 |
CC_LINKER_MAX_CONCURRENT_SESSIONS |
最大并发会话数(默认 5) |
CC_LINKER_SESSION_LOCK_TIMEOUT_MS |
单个会话锁超时(毫秒) |
CC_LINKER_MAX_QUEUE_SIZE |
消息队列最大积压数量 |
CC_LINKER_CONFIRM_RISKY_ACTIONS |
是否确认高风险操作(true/false) |
CC_LINKER_IMAGES_ENABLED |
图片处理开关(默认 true) |
CC_LINKER_IMAGES_MAX_SIZE |
图片大小限制(字节) |
CC_LINKER_IMAGES_CLEANUP_HOURS |
图片清理周期(小时) |
启用 CLI 端 activity marker(可选)
cc-linker 默认通过 OS 信号检测 CLI 端活跃度。如需更精确的检测,可在 ~/.claude/settings.json 中配置 hooks(Claude Code 通过 stdin 传入 JSON 事件,session_id 字段需自行用 jq 提取):
{
"hooks": {
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": "SESSION_ID=$(jq -r '.session_id' </dev/stdin); cc-linker activity-hook --platform=cli --action=start --session=\"$SESSION_ID\""
}]
}],
"Stop": [{
"hooks": [{
"type": "command",
"command": "SESSION_ID=$(jq -r '.session_id' </dev/stdin); cc-linker activity-hook --platform=cli --action=end --session=\"$SESSION_ID\""
}]
}]
}
}
这样 cc-linker 可以 100% 准确检测 CLI 侧活跃状态,而不是依赖 CPU/子进程采样。
架构概览
┌──────────────────────────────────────────────────────┐
│ Claude Code CLI ←→ Registry ←→ 聊天应用 Bot │
│ (session JSONL) (registry.json) (当前: 飞书) │
│ ↑ │
│ SessionStart hook │
│ │
│ 内置模块: │
│ - SDK 模式 (默认): Agent SDK + 交互式权限确认 │
│ - 流式模式: stream-json + 卡片实时更新 │
│ - 图片处理: 自动下载 + prompt 注入 │
│ - 目录浏览: /listDir 交互式切换 cwd │
│ - 文件队列: 消息持久化 + 崩溃恢复 │
└──────────────────────────────────────────────────────┘
- Registry (
~/.cc-linker/registry.json): 统一会话索引,带文件锁和自动备份 - User Mapping (
~/.cc-linker/user-mapping.json): 飞书用户 open_id → 当前会话目标的映射(CAS 原子更新) - Scanner: 增量扫描 Claude Code JSONL 文件,保持注册表最新
- Hook: Claude Code 启动时自动注册新会话
- Spool Queue: 持久化消息队列,崩溃后可恢复(pending → processing → replied → done/failed)
- Stream Parser: 解析 Claude
stream-json输出 - Card Updater: 流式卡片 + 权限卡片的发送与节流
- Permission Handler: SDK 模式下工具权限的交互式确认(允许/拒绝/超时自动拒绝)
- Image Processor: 飞书图片下载、prompt 注入、过期清理
- Startup Reconciler: 进程启动时自动修复不一致状态(恢复卡住的消息、回滚超时 claim、补齐 jsonl_path)
- Provider Manager: 多模型管理,支持 CC Switch 集成和手动配置
- Activity Detection: CLI 端会话活跃检测,防止飞书和 CLI 同时操作同一会话
详细架构见 docs/产品设计文档-自建方案.md。
开发者指南
git clone https://github.com/yujuntea/cc-linker.git
cd cc-linker
bun install
bun run dev <命令> # 开发模式
bun run typecheck # 类型检查
bun test # 运行测试
bun test --coverage # 带覆盖率
两种构建产物
cc-linker 支持两种分发形式,构建脚本不同:
| 产物 | 构建命令 | 输出 | 用途 |
|---|---|---|---|
| 独立二进制 | bun run build |
dist/cc-linker |
单机使用,无需额外运行时 |
| npm 包 | bun run build:npm |
dist/cli.js |
npm install -g 全局安装(运行时仍需 Bun) |
开发命令速查
| 命令 | 行为 | 适用场景 |
|---|---|---|
bun run dev <命令> |
直接运行 src/index.ts(不构建) |
开发调试 |
bun run build:npm |
构建 npm 包 → dist/cli.js |
构建产物 |
bun run reload |
构建 → 如果 daemon 在运行则自动重启 | 开发时快速生效(适合 bun link 安装) |
bun run reload:force |
构建 → 强制重启(没在运行也会启动) | 开发时确保 daemon 在运行 |
bun run deploy |
构建 → 打包 → 全局安装 → 自动重启 | 正式发布到全局并生效 |
bun run deploy:force |
构建 → 打包 → 全局安装 → 强制重启 | 同上,确保启动 |
bun run pack:test |
构建 → 打包 → 安装到隔离目录 | 验证 npm 包完整性 |
bun run typecheck |
TypeScript 类型检查 | CI/提交前 |
bun test |
运行所有测试 | CI/提交前 |
deploy与reload的区别:
reload只更新源码目录的dist/cli.js,重启的是旧的全局安装版本(如果通过npm install -g安装)deploy会npm install -g ./cc-linker-x.y.z.tgz,确保全局安装的是最新代码,再重启- 通过
bun link本地链接安装的用户,reload即可生效(符号链接自动指向源码目录)
npm 包本地测试
正式发布前,建议先在本地打包安装验证,确保 files 字段和 bin 入口正确。
方法 1:pack + install(最接近真实发布)
# 1. 构建并打包
bun run build:npm # 生成 dist/cli.js
npm pack # → cc-linker-x.y.z.tgz
# 2. 在干净环境安装测试
mkdir -p /tmp/test-cc-linker && cd /tmp/test-cc-linker
npm install /path/to/cc-linker-0.2.0.tgz
npx cc-linker --version # 验证命令可用
cc-linker list # 验证功能正常
# 3. 特别验证 daemon install 生成的 plist/service 中可执行路径正确
cc-linker daemon install # 检查生成的配置文件中 ProgramArguments/ExecStart
方法 2:bun link(开发迭代最快)
# 创建全局符号链接,修改代码后重新 build:npm 立即生效
bun run build:npm
bun link # 或 npm link
# 全局任意位置测试
cc-linker list
cc-linker daemon install
# 解除链接
bun unlink cc-linker
bun link是符号链接到源码目录,不经过files字段过滤。发布前务必用方法 1 验证一次,避免files遗漏必要文件。
发布
# 独立二进制(本地分发)
bun run build # → dist/cc-linker
# npm 发布
npm version minor # 或 patch / major
npm publish # prepublishOnly 自动触发 build:npm
git push --tags
详细文档
| 文档 | 说明 |
|---|---|
| docs/产品设计文档-自建方案.md | 产品设计文档 |
| docs/验收指南.md | 功能验收指南 |
| docs/验收测试报告.md | 验收测试结果 |
| docs/Product.md | 产品需求文档 |
| docs/model-switch-design.md | 模型切换设计 |
License
MIT








