npm.io
0.7.5 • Published yesterdayCLI

cc-linker

Licence
MIT
Version
0.7.5
Deps
9
Size
18.9 MB
Vulns
0
Weekly
0
Stars
3
Install scriptsThis package runs scripts during installation (preinstall/install/postinstall)

cc-linker

让手机聊天应用和终端(Claude Code CLI)之间的对话切换,像切换设备一样无缝。

目前已接入飞书,更多聊天平台持续扩展中。

npm version License: MIT

语言: 中文 | English

为什么需要 cc-linker?

你是否遇到过这样的场景:

  • 通勤路上用手机聊,到公司终端继续 — 地铁上用手机飞书给 Bot 发消息讨论技术方案,到公司打开终端 cc-linker list 找到会话,resume 一键恢复上下文
  • 飞书快速提问,终端深度调试 — 在飞书里快速问了个 API 用法,发现需要本地调试,终端 cc-linker resume 切换到同一会话继续让 Claude 帮你写代码
  • 多项目并行,会话不乱 — 同时在 project-aproject-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
飞书会话列表 开始处理 流式 thinking + tool_use + 回复
处理完成
token / 耗时 / 轮数统计
处理完成(长回复)
长文本同样展示
模型切换
卡片按钮一键切换
处理完成 处理完成-长回复 模型选择
目录浏览
/listDir 交互式浏览文件系统
SDK 权限确认
交互式允许/拒绝 Claude 的工具调用
图片消息
飞书发送图片,Claude 自动分析
目录浏览 SDK 权限确认 图片消息分析
终端端体验

查看所有会话 — 清晰的表格展示,状态一目了然:

终端会话列表

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

终端恢复会话

快速开始

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_replybg 继续跑,下一条 chat 消息会走默认路径而非 rendezvous
/whoami 获取你的 open_id

说明 1/switch/resume 中的数字序号来自最近一次 /list 生成的列表快照,默认 10 分钟内有效;超时后请重新执行 /list

说明 2/new 支持”先选目录/模型,后发第一条消息再真正创建会话”的用法;/model 设置的是当前用户的默认模型,直到 /model --clear 清除。

说明 3/stop vs /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_versionrendezvous_enabledrendezvous_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

前提条件:本机需运行 claude daemon(>= 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)→ 同卡 patchWaitingCardbot.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_needsrunChatSDK 透传 bgAskedNewQuestionhandleReplyhandleReply 重新 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 前,需要在 飞书开放平台 创建应用并配置权限。

创建应用
  1. 访问 https://open.feishu.cn/app → 创建企业自建应用
  2. 在「应用功能」→「机器人」中启用 Bot 能力
  3. 获取 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:readonlyim: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_binsdk.claude_executable

cc-linker 默认使用 SDK 自带的 claude 二进制(保证版本兼容)。若该二进制在 npm install 时被 omit(如 --omit=optionalNODE_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/提交前

deployreload 的区别

  • reload 只更新源码目录的 dist/cli.js,重启的是旧的全局安装版本(如果通过 npm install -g 安装)
  • deploynpm 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

Keywords