- 新增命令: lark-delegate(跨项目委派)、lark-inbox(收件箱)、lark-me(自发送)、stop(任务收尾) - 新增工具: lark-consumer/consumer-ensure/inbox-check(飞书事件实时消费与会话隔离)、tmux-monitor(监控面板) - runcc.sh 增加 --list/--kill/--send 操作模式与多会话自增命名 - CLAUDE.md 增加强制要求(汇报格式、收尾通知、上下文压缩)
448 lines
20 KiB
Bash
Executable File
448 lines
20 KiB
Bash
Executable File
#!/bin/bash
|
||
# Claude Code 启动脚本
|
||
# 支持多 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)"
|
||
ENV_FILE="${SCRIPT_DIR}/.env"
|
||
|
||
# ── 加载 .env 文件 ──────────────────────────────────────
|
||
if [ -f "$ENV_FILE" ]; then
|
||
while IFS= read -r line || [ -n "$line" ]; do
|
||
# 跳过注释和空行
|
||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||
[[ -z "${line// /}" ]] && continue
|
||
# 解析 key=value
|
||
key="${line%%=*}"
|
||
value="${line#*=}"
|
||
# 去除引号
|
||
[[ "$value" =~ ^\"(.*)\"$ ]] && value="${BASH_REMATCH[1]}"
|
||
[[ "$value" =~ ^\'(.*)\'$ ]] && value="${BASH_REMATCH[1]}"
|
||
# 展开 value 中的环境变量引用(如 $HOME → /home/arno)
|
||
eval "value=\"$value\""
|
||
# 仅导出合法变量名
|
||
if [[ "$key" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
|
||
export "$key=$value"
|
||
fi
|
||
done < "$ENV_FILE"
|
||
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
|
||
--name|-n)
|
||
[ $# -ge 2 ] || { echo "错误: --name 需要参数" >&2; exit 1; }
|
||
SESSION_NAME="$2"; shift 2
|
||
;;
|
||
--provider|-p)
|
||
[ $# -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 " (默认) 启动 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"
|
||
echo "Qwen 模型: qw37|qw36f"
|
||
echo "DeepSeek 模型: ds|dsf"
|
||
echo "其他: 直接传入模型名称"
|
||
echo ""
|
||
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 -l # 列出所有 CC 会话"
|
||
echo " runcc.sh -k glm-52 # 终止 glm-52 会话"
|
||
echo " runcc.sh -s glm-52 '你好' # 向 glm-52 发送消息"
|
||
exit 0
|
||
;;
|
||
*)
|
||
MODEL_KEY="$1"; shift
|
||
;;
|
||
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 动态解析 ─────────────────────────────────────
|
||
# 优先级:命令行 --provider > .env CC_PROVIDER > 默认 glm
|
||
CC_PROVIDER="${PROVIDER_OVERRIDE:-${CC_PROVIDER:-glm}}"
|
||
upper_provider="$(echo "$CC_PROVIDER" | tr '[:lower:]' '[:upper:]')"
|
||
|
||
base_url_var="${upper_provider}_BASE_URL"
|
||
token_var="${upper_provider}_AUTH_TOKEN"
|
||
default_model_var="${upper_provider}_DEFAULT_MODEL"
|
||
context_window_var="${upper_provider}_CONTEXT_WINDOW"
|
||
compact_pct_var="${upper_provider}_COMPACT_PCT"
|
||
disable_compact_var="${upper_provider}_DISABLE_COMPACT"
|
||
max_ctx_var="${upper_provider}_MAX_CONTEXT_TOKENS"
|
||
|
||
ANTHROPIC_BASE_URL="${!base_url_var}"
|
||
ANTHROPIC_AUTH_TOKEN="${!token_var}"
|
||
DEFAULT_MODEL="${!default_model_var:-}"
|
||
CONTEXT_WINDOW="${!context_window_var:-200000}"
|
||
COMPACT_PCT="${!compact_pct_var:-75}"
|
||
DISABLE_COMPACT="${!disable_compact_var:-}"
|
||
MAX_CONTEXT_TOKENS="${!max_ctx_var:-}"
|
||
|
||
if [ -z "$ANTHROPIC_BASE_URL" ] || [ -z "$ANTHROPIC_AUTH_TOKEN" ]; then
|
||
echo "错误: Provider '$CC_PROVIDER' 未配置 BASE_URL 或 AUTH_TOKEN" >&2
|
||
echo "请在 ${ENV_FILE} 中添加: ${upper_provider}_BASE_URL, ${upper_provider}_AUTH_TOKEN" >&2
|
||
exit 1
|
||
fi
|
||
|
||
export ANTHROPIC_BASE_URL ANTHROPIC_AUTH_TOKEN
|
||
export CC_CONTEXT_WINDOW="$CONTEXT_WINDOW"
|
||
|
||
# DISABLE_COMPACT 模式: 禁用自动压缩,覆盖上下文窗口(适用于 API 报告值 < 实际值的模型)
|
||
if [ "$DISABLE_COMPACT" = "1" ]; then
|
||
export DISABLE_COMPACT=1
|
||
export CLAUDE_CODE_MAX_CONTEXT_TOKENS="${MAX_CONTEXT_TOKENS:-$CONTEXT_WINDOW}"
|
||
else
|
||
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE="$COMPACT_PCT"
|
||
fi
|
||
|
||
# ── 更新 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
|
||
|
||
# ── 模型解析 ──────────────────────────────────────────────
|
||
MODEL_KEY="${MODEL_KEY:-$DEFAULT_MODEL}"
|
||
|
||
case "$MODEL_KEY" in
|
||
# GLM 系列
|
||
45) MODEL="glm-4.5" ;;
|
||
45a) MODEL="glm-4.5-air" ;;
|
||
46) MODEL="glm-4.6" ;;
|
||
47) MODEL="glm-4.7" ;;
|
||
5) MODEL="glm-5" ;;
|
||
5t) MODEL="glm-5-turbo" ;;
|
||
51) MODEL="glm-5.1" ;;
|
||
52) MODEL="glm-5.2" ;;
|
||
# Qwen 系列
|
||
qw37) MODEL="qwen3.7-max" ;;
|
||
qw36f) MODEL="qwen3.6-flash" ;;
|
||
qwim) MODEL="qwen-image-max" ;;
|
||
# DeepSeek 系列
|
||
ds) MODEL="deepseek-v4-pro[1m]" ;;
|
||
dsf) MODEL="deepseek-v4-flash" ;;
|
||
# 其他: 直接作为模型名称
|
||
*) MODEL="$MODEL_KEY" ;;
|
||
esac
|
||
|
||
# ── 模型环境变量(三个层级统一为同一模型)────────────────
|
||
export ANTHROPIC_MODEL="$MODEL"
|
||
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
|
||
|
||
# ── 会话命名规则:目录名-序号(如 dit-1、szis-2)───────────
|
||
DIR_NAME=$(basename "$PWD")
|
||
BASE_TITLE="${DIR_NAME}"
|
||
if [ -z "$SESSION_NAME" ]; then
|
||
SESSION_NAME="${BASE_TITLE}-1"
|
||
fi
|
||
|
||
# ── 构建 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
|
||
|
||
# ── 每日自动更新 ────────────────────────────────────────
|
||
UPGRADE_MARKER="${HOME}/.claude/.last-upgrade-date"
|
||
TODAY="$(date +%Y-%m-%d)"
|
||
if [ ! -f "$UPGRADE_MARKER" ] || [ "$(cat "$UPGRADE_MARKER" 2>/dev/null)" != "$TODAY" ]; then
|
||
echo "每日首次启动,检查 Claude Code 更新..."
|
||
if claude upgrade 2>&1; then
|
||
mkdir -p "$(dirname "$UPGRADE_MARKER")"
|
||
echo "$TODAY" > "$UPGRADE_MARKER"
|
||
else
|
||
echo "警告: claude upgrade 失败,跳过更新继续启动"
|
||
fi
|
||
fi
|
||
|
||
# ── 辅助函数:自动编号(列表中出现同名则递增)─────────────
|
||
# 用法: _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 "${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)"
|
||
|
||
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 session(session 名 = 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
|