功能(ClaudeCode): 新增飞书联动工具链与会话管理增强

- 新增命令: lark-delegate(跨项目委派)、lark-inbox(收件箱)、lark-me(自发送)、stop(任务收尾)
- 新增工具: lark-consumer/consumer-ensure/inbox-check(飞书事件实时消费与会话隔离)、tmux-monitor(监控面板)
- runcc.sh 增加 --list/--kill/--send 操作模式与多会话自增命名
- CLAUDE.md 增加强制要求(汇报格式、收尾通知、上下文压缩)
This commit is contained in:
2026-07-03 14:49:44 +08:00
parent cd6a130b29
commit 911503d997
11 changed files with 1455 additions and 39 deletions
+280 -39
View File
@@ -3,6 +3,11 @@
# 支持多 Provider (通过 .env 配置,自动解析 ${PROVIDER}_BASE_URL / ${PROVIDER}_AUTH_TOKEN)
#
# 每天首次启动时自动执行 claude upgrade
# 始终通过 tmux 启动 claude,每次启动创建独立 session(以当前目录名命名)
# 同目录可多开,session 名末尾自增数字(dit-1 → dit-2 → dit-3
# pane 标题与 CC --name 使用 session_model 组合(如 dit-1_glm-5.2),window 名使用模型名
#
# tmux 配置参考: https://gitea.szis.com.cn/isos/team/src/branch/trunk/team/tmux.md
# ── 路径配置 ──────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -33,6 +38,10 @@ fi
SESSION_NAME=""
MODEL_KEY=""
PROVIDER_OVERRIDE=""
ACTION="launch" # launch | list | kill | send
KILL_TARGET=""
SEND_TARGET=""
SEND_MESSAGE=""
while [ $# -gt 0 ]; do
case "$1" in
@@ -44,11 +53,28 @@ while [ $# -gt 0 ]; do
[ $# -ge 2 ] || { echo "错误: --provider 需要参数" >&2; exit 1; }
PROVIDER_OVERRIDE="$2"; shift 2
;;
--list|-l)
ACTION="list"; shift
;;
--kill|-k)
[ $# -ge 2 ] || { echo "错误: --kill 需要 session 名称参数" >&2; exit 1; }
ACTION="kill"; KILL_TARGET="$2"; shift 2
;;
--send|-s)
[ $# -ge 3 ] || { echo "错误: --send 需要 <session> <消息>" >&2; exit 1; }
ACTION="send"; SEND_TARGET="$2"; SEND_MESSAGE="$3"; shift 3
;;
-h|--help)
echo "用法: runcc.sh [选项] [模型]"
echo ""
echo "选项:"
echo " --name, -n <名称> Claude Code 会话显示名称"
echo "操作模式:"
echo " (默认) 启动 Claude Code 会话"
echo " --list, -l 列出所有 CC tmux 会话(含 provider/模型信息)"
echo " --kill, -k <名称> 终止指定 CC 会话"
echo " --send, -s <会话> <消息> 向已有 CC 会话发送提示词"
echo ""
echo "启动选项:"
echo " --name, -n <名称> 会话显示名称"
echo " --provider, -p <名称> API 提供商 (由 .env 配置,如 glm / deepseek)"
echo ""
echo "GLM 模型: 45|45a|46|47|5|5t|51|52"
@@ -59,13 +85,18 @@ while [ $# -gt 0 ]; do
echo "Provider 变量规则: CC_PROVIDER=<name> → <NAME>_BASE_URL + <NAME>_AUTH_TOKEN"
echo " 添加新 Provider 只需在 .env 中按此格式添加变量即可"
echo ""
echo "快捷方式 (通过 ~/.bashrc 别名):"
echo " cc [模型] 默认 glm provider"
echo " ccds [模型] 默认 deepseek provider"
echo ""
echo "示例:"
echo " runcc.sh # 默认 Provider + 默认模型"
echo " runcc.sh 47 # 默认 Provider + glm-4.7"
echo " runcc.sh --provider qwen # Qwen + 默认模型"
echo " runcc.sh -p qwen qw37 # Qwen + qwen3.7-max"
echo " runcc.sh --provider deepseek # DeepSeek + 默认模型"
echo " runcc.sh -p deepseek dsf # DeepSeek + deepseek-v4-flash"
echo " runcc.sh -l # 列出所有 CC 会话"
echo " runcc.sh -k glm-52 # 终止 glm-52 会话"
echo " runcc.sh -s glm-52 '你好' # 向 glm-52 发送消息"
exit 0
;;
*)
@@ -74,10 +105,65 @@ while [ $# -gt 0 ]; do
esac
done
# ── 操作: --list ──────────────────────────────────────────
if [ "$ACTION" = "list" ]; then
echo "CC tmux 会话列表:"
echo "──────────────────────────────────────────────────────"
CC_COUNT=0
for s in $(tmux list-sessions -F '#{session_name}' 2>/dev/null); do
if tmux show-options -t "$s" @cc_provider 2>/dev/null | grep -q .; then
CC_COUNT=$((CC_COUNT + 1))
created=$(tmux display -t "$s" -p '#{session_created}' 2>/dev/null)
attached=$(tmux display -t "$s" -p '#{?session_attached,●,○}' 2>/dev/null)
provider=$(tmux show-options -t "$s" -v @cc_provider 2>/dev/null)
model=$(tmux show-options -t "$s" -v @cc_model 2>/dev/null)
printf " %s %s %s @cc_provider=%s @cc_model=%s\n" "$attached" "$s" "$created" "$provider" "$model"
fi
done
if [ "$CC_COUNT" -eq 0 ]; then
echo " (无 CC 会话)"
fi
echo "──────────────────────────────────────────────────────"
echo "●=已附加 ○=后台运行"
exit 0
fi
# ── 操作: --kill ──────────────────────────────────────────
if [ "$ACTION" = "kill" ]; then
if ! tmux has-session -t "$KILL_TARGET" 2>/dev/null; then
echo "错误: 会话 '$KILL_TARGET' 不存在" >&2
echo "可用 CC 会话:"
CC_LISTED=0
for s in $(tmux list-sessions -F '#{session_name}' 2>/dev/null); do
if tmux show-options -t "$s" @cc_provider 2>/dev/null | grep -q .; then
echo " $s"
CC_LISTED=1
fi
done
[ "$CC_LISTED" -eq 0 ] && echo " (无)"
exit 1
fi
tmux kill-session -t "$KILL_TARGET"
echo "已终止会话: $KILL_TARGET"
exit 0
fi
# ── 操作: --send ──────────────────────────────────────────
if [ "$ACTION" = "send" ]; then
if ! tmux has-session -t "$SEND_TARGET" 2>/dev/null; then
echo "错误: 会话 '$SEND_TARGET' 不存在" >&2
exit 1
fi
# 找第一个 pane(通常是 claude 主进程)
PANE_ID="$(tmux list-panes -t "$SEND_TARGET" -F '#{pane_id}' | head -1)"
tmux send-keys -t "$PANE_ID" -l "$SEND_MESSAGE"
tmux send-keys -t "$PANE_ID" Enter
echo "已向 $SEND_TARGET ($PANE_ID) 发送消息"
exit 0
fi
# ── Provider 动态解析 ─────────────────────────────────────
# CC_PROVIDER=glm → GLM_BASE_URL, GLM_AUTH_TOKEN, GLM_DEFAULT_MODEL
# CC_PROVIDER=deepseek → DEEPSEEK_BASE_URL, DEEPSEEK_AUTH_TOKEN, DEEPSEEK_DEFAULT_MODEL
# CC_PROVIDER=xxx → XXX_BASE_URL, XXX_AUTH_TOKEN, XXX_DEFAULT_MODEL, XXX_CONTEXT_WINDOW
# 优先级:命令行 --provider > .env CC_PROVIDER > 默认 glm
CC_PROVIDER="${PROVIDER_OVERRIDE:-${CC_PROVIDER:-glm}}"
upper_provider="$(echo "$CC_PROVIDER" | tr '[:lower:]' '[:upper:]')"
@@ -115,13 +201,46 @@ else
fi
# ── 更新 settings.json ──────────────────────────────────
# 删除凭据、模型和窗口变量(由环境变量接管)
# SETTINGS_FILE 路径由 .env 配置,默认 ~/.claude/settings.json
# 1) 删除凭据、模型和窗口变量(由环境变量接管)
# 2) 注入 statusLine / UserPromptSubmit hook(不存在则写入绝对路径)
# SETTINGS_FILE / STATUS_LINE_SCRIPT / USER_PROMPT_SUBMIT_SCRIPT 路径由 .env 配置
SETTINGS_FILE="${SETTINGS_FILE:-$HOME/.claude/settings.json}"
# 项目根目录:优先用 .env 的 SKILLS_DIR(语义清晰、跨机器可迁移);未配置则从脚本位置反推兜底
PROJECT_DIR="${SKILLS_DIR:-$(dirname "$(dirname "$SCRIPT_DIR")")}"
STATUS_LINE_SCRIPT="${STATUS_LINE_SCRIPT:-.claude/tools/status_line.sh}"
USER_PROMPT_SUBMIT_SCRIPT="${USER_PROMPT_SUBMIT_SCRIPT:-.claude/tools/user-prompt-submit.sh}"
# 相对路径 → 基于项目根目录的绝对路径
if [[ "$STATUS_LINE_SCRIPT" != /* ]]; then
STATUS_LINE_SCRIPT="${PROJECT_DIR}/${STATUS_LINE_SCRIPT#./}"
fi
if [[ "$USER_PROMPT_SUBMIT_SCRIPT" != /* ]]; then
USER_PROMPT_SUBMIT_SCRIPT="${PROJECT_DIR}/${USER_PROMPT_SUBMIT_SCRIPT#./}"
fi
if command -v jq &>/dev/null && [ -f "$SETTINGS_FILE" ]; then
# 清理残留的环境变量配置
jq 'del(.env.ANTHROPIC_BASE_URL, .env.ANTHROPIC_AUTH_TOKEN, .env.CLAUDE_CODE_AUTO_COMPACT_WINDOW, .env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE, .env.DISABLE_COMPACT, .env.CLAUDE_CODE_MAX_CONTEXT_TOKENS, .env.ANTHROPIC_DEFAULT_OPUS_MODEL, .env.ANTHROPIC_DEFAULT_HAIKU_MODEL, .env.ANTHROPIC_DEFAULT_SONNET_MODEL)' \
"$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp" \
&& mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE"
# 确保 statusLine 已配置且脚本可执行(键不存在或路径失效则写入/纠正)
SL_CUR="$(jq -r '.statusLine.command // empty' "$SETTINGS_FILE" 2>/dev/null)"
if [ -z "$SL_CUR" ] || [ ! -x "${SL_CUR%% *}" ]; then
jq --arg cmd "$STATUS_LINE_SCRIPT" \
'. + {"statusLine": {"type": "command", "command": $cmd}}' \
"$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp" \
&& mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE"
fi
# 确保 UserPromptSubmit hook 已配置且脚本可执行(键不存在或路径失效则写入/纠正)
UPS_CUR="$(jq -r '.hooks.UserPromptSubmit[0].hooks[0].command // empty' "$SETTINGS_FILE" 2>/dev/null)"
if [ -z "$UPS_CUR" ] || [ ! -x "${UPS_CUR%% *}" ]; then
jq --arg cmd "$USER_PROMPT_SUBMIT_SCRIPT" \
'.hooks += {"UserPromptSubmit": [{"hooks": [{"type": "command", "command": $cmd}]}]}' \
"$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp" \
&& mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE"
fi
fi
# ── 模型解析 ──────────────────────────────────────────────
@@ -154,27 +273,20 @@ export ANTHROPIC_DEFAULT_HAIKU_MODEL="$MODEL"
export ANTHROPIC_DEFAULT_SONNET_MODEL="$MODEL"
export ANTHROPIC_DEFAULT_OPUS_MODEL="$MODEL"
export CLAUDE_CODE_SUBAGENT_MODEL="$MODEL"
export CLAUDE_CODE_NO_FLICKER=1
# ── tmux 模型传播(确保子 pane 能获取正确模型)─────────
if [ -n "${TMUX:-}" ]; then
SESSION_ID="$(tmux display-message -p '#{session_id}')"
# ── 会话命名规则:目录名-序号(如 dit-1、szis-2)───────────
DIR_NAME=$(basename "$PWD")
BASE_TITLE="${DIR_NAME}"
if [ -z "$SESSION_NAME" ]; then
SESSION_NAME="${BASE_TITLE}-1"
fi
tmux setenv -t "$SESSION_ID" ANTHROPIC_DEFAULT_OPUS_MODEL "$MODEL"
tmux setenv -t "$SESSION_ID" ANTHROPIC_DEFAULT_HAIKU_MODEL "$MODEL"
tmux setenv -t "$SESSION_ID" ANTHROPIC_DEFAULT_SONNET_MODEL "$MODEL"
tmux setenv -t "$SESSION_ID" CC_CONTEXT_WINDOW "$CONTEXT_WINDOW"
if [ "$DISABLE_COMPACT" = "1" ]; then
tmux setenv -t "$SESSION_ID" DISABLE_COMPACT 1
tmux setenv -t "$SESSION_ID" CLAUDE_CODE_MAX_CONTEXT_TOKENS "${MAX_CONTEXT_TOKENS:-$CONTEXT_WINDOW}"
tmux setenv -u -t "$SESSION_ID" CLAUDE_AUTOCOMPACT_PCT_OVERRIDE 2>/dev/null || true
else
tmux setenv -t "$SESSION_ID" CLAUDE_AUTOCOMPACT_PCT_OVERRIDE "$COMPACT_PCT"
tmux setenv -u -t "$SESSION_ID" DISABLE_COMPACT 2>/dev/null || true
tmux setenv -u -t "$SESSION_ID" CLAUDE_CODE_MAX_CONTEXT_TOKENS 2>/dev/null || true
fi
tmux set-option -p @anthropic_model "$MODEL"
# ── 构建 claude 启动参数 ─────────────────────────────────
CC_ARGS=(--ide --dangerously-skip-permissions --allow-dangerously-skip-permissions --effort max)
# 公共 skills 仓库(由 .env 的 SKILLS_DIR 配置,便于跨电脑迁移)
if [ -n "${SKILLS_DIR:-}" ]; then
CC_ARGS+=(--add-dir "$SKILLS_DIR")
fi
# ── 每日自动更新 ────────────────────────────────────────
@@ -190,17 +302,146 @@ if [ ! -f "$UPGRADE_MARKER" ] || [ "$(cat "$UPGRADE_MARKER" 2>/dev/null)" != "$T
fi
fi
# ── 启动 Claude Code ────────────────────────────────────
CC_ARGS=(--ide --dangerously-skip-permissions --allow-dangerously-skip-permissions --effort max)
# CC_ARGS=(--verbose)
# 公共 skills 仓库(由 .env 的 SKILLS_DIR 配置,便于跨电脑迁移)
if [ -n "${SKILLS_DIR:-}" ]; then
CC_ARGS+=(--add-dir "$SKILLS_DIR")
# ── 辅助函数:自动编号(列表中出现同名则递增)─────────────
# 用法: _auto_number "名字列表(换行分隔)" "基础名" → 输出下一个可用名
_auto_number() {
local list="$1"
local base="$2"
local name="$base"
local n=1
while echo "$list" | grep -qxF "$name"; do
n=$((n + 1))
name="${base}-${n}"
done
echo "$name"
}
# ════════════════════════════════════════════════════════════
# ── 启动 Claude Code(始终通过 tmux)──────────────────────
# ════════════════════════════════════════════════════════════
ORIG_PWD="$PWD"
WINDOW_NAME="$MODEL"
# ── 构建 -e 参数数组 ────────────────────────────────────
E_FLAGS=()
E_FLAGS+=(-e "ANTHROPIC_BASE_URL=$ANTHROPIC_BASE_URL")
E_FLAGS+=(-e "ANTHROPIC_AUTH_TOKEN=$ANTHROPIC_AUTH_TOKEN")
E_FLAGS+=(-e "ANTHROPIC_MODEL=$MODEL")
E_FLAGS+=(-e "ANTHROPIC_DEFAULT_OPUS_MODEL=$MODEL")
E_FLAGS+=(-e "ANTHROPIC_DEFAULT_HAIKU_MODEL=$MODEL")
E_FLAGS+=(-e "ANTHROPIC_DEFAULT_SONNET_MODEL=$MODEL")
E_FLAGS+=(-e "CLAUDE_CODE_SUBAGENT_MODEL=$MODEL")
E_FLAGS+=(-e "CC_CONTEXT_WINDOW=$CONTEXT_WINDOW")
E_FLAGS+=(-e "CLAUDE_CODE_NO_FLICKER=1")
if [ "$DISABLE_COMPACT" = "1" ]; then
E_FLAGS+=(-e "DISABLE_COMPACT=1")
E_FLAGS+=(-e "CLAUDE_CODE_MAX_CONTEXT_TOKENS=${MAX_CONTEXT_TOKENS:-$CONTEXT_WINDOW}")
else
E_FLAGS+=(-e "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=$COMPACT_PCT")
fi
if [ -n "$SESSION_NAME" ]; then
INITIAL_PROMPT="/rename $SESSION_NAME"
fi
if [ -n "${TMUX:-}" ]; then
# ── 已在 tmux 中:pane 标题 = session_model → 设置 env → 直接启动
TMUX_TITLE="${SESSION_NAME}_${MODEL}"
CC_ARGS+=(--name "$TMUX_TITLE")
echo "启动 Claude Code (provider: $CC_PROVIDER, 模型: $MODEL${SESSION_NAME:+, 会话: $SESSION_NAME})"
claude "${CC_ARGS[@]}" ${INITIAL_PROMPT:+"$INITIAL_PROMPT"}
echo "启动 Claude Code (provider: $CC_PROVIDER, 模型: $MODEL, 会话: $SESSION_NAME)"
tmux select-pane -T "$TMUX_TITLE" 2>/dev/null || true
# 传播环境变量到 tmux session(供子 pane/window 继承)
SID="$(tmux display-message -p '#{session_id}')"
tmux setenv -t "$SID" ANTHROPIC_BASE_URL "$ANTHROPIC_BASE_URL"
tmux setenv -t "$SID" ANTHROPIC_AUTH_TOKEN "$ANTHROPIC_AUTH_TOKEN"
tmux setenv -t "$SID" ANTHROPIC_MODEL "$MODEL"
tmux setenv -t "$SID" ANTHROPIC_DEFAULT_OPUS_MODEL "$MODEL"
tmux setenv -t "$SID" ANTHROPIC_DEFAULT_HAIKU_MODEL "$MODEL"
tmux setenv -t "$SID" ANTHROPIC_DEFAULT_SONNET_MODEL "$MODEL"
tmux setenv -t "$SID" CLAUDE_CODE_SUBAGENT_MODEL "$MODEL"
tmux setenv -t "$SID" CC_CONTEXT_WINDOW "$CONTEXT_WINDOW"
tmux setenv -t "$SID" CLAUDE_CODE_NO_FLICKER 1
if [ "$DISABLE_COMPACT" = "1" ]; then
tmux setenv -t "$SID" DISABLE_COMPACT 1
tmux setenv -t "$SID" CLAUDE_CODE_MAX_CONTEXT_TOKENS "${MAX_CONTEXT_TOKENS:-$CONTEXT_WINDOW}"
tmux setenv -u -t "$SID" CLAUDE_AUTOCOMPACT_PCT_OVERRIDE 2>/dev/null || true
else
tmux setenv -t "$SID" CLAUDE_AUTOCOMPACT_PCT_OVERRIDE "$COMPACT_PCT"
tmux setenv -u -t "$SID" DISABLE_COMPACT 2>/dev/null || true
tmux setenv -u -t "$SID" CLAUDE_CODE_MAX_CONTEXT_TOKENS 2>/dev/null || true
fi
# 设置 pane 级属性(供 status_line 等读取)
tmux set-option -p @anthropic_model "$MODEL" 2>/dev/null || true
# 环境变量已在当前 shell 中 export,直接 exec
cd "$ORIG_PWD"
exec claude "${CC_ARGS[@]}"
else
# ── 不在 tmux 中:以目录名-序号创建独立 session(如 dit-1、dit-2
n=1
while tmux has-session -t "${BASE_TITLE}-${n}" 2>/dev/null; do
n=$((n + 1))
done
TMUX_SESSION="${BASE_TITLE}-${n}"
SESSION_NAME="$TMUX_SESSION"
TMUX_TITLE="${SESSION_NAME}_${MODEL}"
CC_ARGS+=(--name "$TMUX_TITLE")
echo "启动 Claude Code (provider: $CC_PROVIDER, 模型: $MODEL, tmux session: $TMUX_SESSION)"
# 构建 claude 启动命令
CC_CMD="claude"
for arg in "${CC_ARGS[@]}"; do
CC_CMD+=" $arg"
done
BASH_WRAPPER="if ! command -v claude &>/dev/null; then
echo '错误: claude 命令未找到'
echo -n '按 Enter 退出...'; read
exit 1
fi
exec ${CC_CMD}"
# 创建独立 tmux sessionsession 名 = MODEL 名)
tmux new-session -d -s "$TMUX_SESSION" -n "$WINDOW_NAME" -c "$ORIG_PWD" \
"${E_FLAGS[@]}" bash -c "$BASH_WRAPPER"
# 设置 pane 标题
tmux select-pane -t "$TMUX_SESSION:$WINDOW_NAME" -T "$TMUX_TITLE" 2>/dev/null || true
# ── tmux 会话级默认配置 ──────────────────────────────
# 参考: https://gitea.szis.com.cn/isos/team/src/branch/trunk/team/tmux.md
_apply_tmux_defaults() {
local sess="$1"
# 基础选项
tmux set-option -t "$sess" mouse on 2>/dev/null || true
tmux set-option -t "$sess" base-index 1 2>/dev/null || true
# tmux set-option -t "$sess" default-terminal "screen-256color" 2>/dev/null || true
tmux set-option -t "$sess" escape-time 0 2>/dev/null || true
tmux set-option -t "$sess" history-limit 50000 2>/dev/null || true
tmux set-option -t "$sess" set-titles on 2>/dev/null || true
tmux set-option -t "$sess" focus-events on 2>/dev/null || true
tmux set-option -t "$sess" set-clipboard on 2>/dev/null || true
tmux set-option -t "$sess" renumber-windows on 2>/dev/null || true
# 状态栏
tmux set-option -t "$sess" status-left "#S |" 2>/dev/null || true
tmux set-option -t "$sess" status-left-length 20 2>/dev/null || true
tmux set-option -t "$sess" status-right "%Y-%m-%d %H:%M" 2>/dev/null || true
tmux set-option -t "$sess" status-right-length 20 2>/dev/null || true
# 窗口选项
tmux set-window-option -t "$sess" pane-base-index 1 2>/dev/null || true
tmux set-window-option -t "$sess" mode-keys vi 2>/dev/null || true
tmux set-window-option -t "$sess" aggressive-resize on 2>/dev/null || true
# 存储 provider/model 元信息(供 --list 查询)
tmux set-option -t "$sess" @cc_provider "$CC_PROVIDER" 2>/dev/null || true
tmux set-option -t "$sess" @cc_model "$MODEL" 2>/dev/null || true
}
_apply_tmux_defaults "$TMUX_SESSION"
# 附加到会话(前台运行)
tmux attach-session -t "$TMUX_SESSION"
fi