217 lines
5.4 KiB
Bash
217 lines
5.4 KiB
Bash
#!/usr/bin/env bash
|
||
# ============================================================
|
||
# spec-coding-skills 安装/更新脚本
|
||
# 用法: bash <(curl -sL <raw-url>/install.sh) [codex|claude|both]
|
||
# 或: bash install.sh [codex|claude|both|目标目录]
|
||
# ============================================================
|
||
set -euo pipefail
|
||
|
||
REPO_URL="https://git.internal.intelligrow.cn/zhangfucai/spec-coding-skills.git"
|
||
DEFAULT_MODE="codex"
|
||
|
||
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||
log_title() { echo -e "${CYAN}$1${NC}"; }
|
||
|
||
MODE=""
|
||
TARGET=""
|
||
SKILLS_SRC=""
|
||
GUIDE_SRC=""
|
||
GUIDE_DST=""
|
||
REQUEST="${1:-$DEFAULT_MODE}"
|
||
TOTAL_NEW=0
|
||
TOTAL_UPDATED=0
|
||
TOTAL_SKIPPED=0
|
||
INSTALLED_TARGETS=""
|
||
|
||
resolve_layout() {
|
||
local input="$1"
|
||
|
||
case "$input" in
|
||
""|codex)
|
||
MODE="codex"
|
||
TARGET=".codex/skills"
|
||
SKILLS_SRC=".codex/skills"
|
||
GUIDE_SRC="AGENTS.md.template"
|
||
GUIDE_DST="AGENTS.md"
|
||
;;
|
||
claude)
|
||
MODE="claude"
|
||
TARGET=".claude/skills"
|
||
SKILLS_SRC=".claude/skills"
|
||
GUIDE_SRC="CLAUDE.md.template"
|
||
GUIDE_DST="CLAUDE.md"
|
||
;;
|
||
both)
|
||
log_warn "both 不是单一布局,请在主流程中单独处理"
|
||
exit 1
|
||
;;
|
||
*)
|
||
TARGET="$input"
|
||
case "$TARGET" in
|
||
*".claude/skills"*)
|
||
MODE="claude"
|
||
SKILLS_SRC=".claude/skills"
|
||
GUIDE_SRC="CLAUDE.md.template"
|
||
GUIDE_DST="CLAUDE.md"
|
||
;;
|
||
*)
|
||
MODE="codex"
|
||
SKILLS_SRC=".codex/skills"
|
||
GUIDE_SRC="AGENTS.md.template"
|
||
GUIDE_DST="AGENTS.md"
|
||
;;
|
||
esac
|
||
;;
|
||
esac
|
||
}
|
||
|
||
TMP_DIR=$(mktemp -d)
|
||
trap "rm -rf $TMP_DIR" EXIT
|
||
|
||
sync_file() {
|
||
local src="$1"
|
||
local dst="$2"
|
||
local create_msg="$3"
|
||
local update_msg="$4"
|
||
|
||
if [ ! -f "$dst" ]; then
|
||
mkdir -p "$(dirname "$dst")"
|
||
cp "$src" "$dst"
|
||
log_info "$create_msg"
|
||
TOTAL_NEW=$((TOTAL_NEW + 1))
|
||
elif ! diff -q "$src" "$dst" >/dev/null 2>&1; then
|
||
cp "$dst" "$dst.local.bak"
|
||
cp "$src" "$dst"
|
||
log_warn "$update_msg"
|
||
TOTAL_UPDATED=$((TOTAL_UPDATED + 1))
|
||
else
|
||
TOTAL_SKIPPED=$((TOTAL_SKIPPED + 1))
|
||
fi
|
||
}
|
||
|
||
sync_guide_file() {
|
||
local src="$1"
|
||
local dst="$2"
|
||
local create_msg="$3"
|
||
local skip_msg="$4"
|
||
|
||
if [ ! -f "$dst" ]; then
|
||
mkdir -p "$(dirname "$dst")"
|
||
cp "$src" "$dst"
|
||
log_info "$create_msg"
|
||
TOTAL_NEW=$((TOTAL_NEW + 1))
|
||
else
|
||
log_info "$skip_msg"
|
||
TOTAL_SKIPPED=$((TOTAL_SKIPPED + 1))
|
||
fi
|
||
}
|
||
|
||
install_layout() {
|
||
local input="$1"
|
||
local skill_dir
|
||
local skill_name
|
||
local src_file
|
||
local rel_path
|
||
local dst_dir
|
||
local dst_file
|
||
local tpl_file
|
||
local tpl_name
|
||
local dst
|
||
local guide_src_path
|
||
|
||
resolve_layout "$input"
|
||
|
||
if [ ! -d "$TMP_DIR/$SKILLS_SRC" ]; then
|
||
log_warn "上游仓库中不存在技能目录: $SKILLS_SRC"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "同步 $MODE: $TARGET"
|
||
mkdir -p "$TARGET"
|
||
|
||
for skill_dir in "$TMP_DIR/$SKILLS_SRC"/*/; do
|
||
[ -d "$skill_dir" ] || continue
|
||
skill_name=$(basename "$skill_dir")
|
||
dst_dir="$TARGET/$skill_name"
|
||
|
||
[ -f "$skill_dir/SKILL.md" ] || continue
|
||
|
||
while IFS= read -r -d '' src_file; do
|
||
rel_path="${src_file#$skill_dir}"
|
||
dst_file="$dst_dir/$rel_path"
|
||
|
||
sync_file \
|
||
"$src_file" \
|
||
"$dst_file" \
|
||
"✨ 新增: $skill_name/$rel_path ($MODE)" \
|
||
"🔄 更新: $skill_name/$rel_path ($MODE) (本地版本已备份为 $(basename "$rel_path").local.bak)"
|
||
done < <(find "$skill_dir" -type f -print0)
|
||
done
|
||
|
||
for tpl_file in "$TMP_DIR/$SKILLS_SRC"/*.template "$TMP_DIR/$SKILLS_SRC"/*.md; do
|
||
[ -f "$tpl_file" ] || continue
|
||
tpl_name=$(basename "$tpl_file")
|
||
dst="$TARGET/$tpl_name"
|
||
|
||
sync_file \
|
||
"$tpl_file" \
|
||
"$dst" \
|
||
"✨ 新增模板: $tpl_name ($MODE)" \
|
||
"🔄 更新模板: $tpl_name ($MODE) (本地版本已备份为 ${tpl_name}.local.bak)"
|
||
done
|
||
|
||
guide_src_path="$TMP_DIR/$GUIDE_SRC"
|
||
if [ -f "$guide_src_path" ]; then
|
||
sync_guide_file \
|
||
"$guide_src_path" \
|
||
"$GUIDE_DST" \
|
||
"✨ 新增项目引导: ${GUIDE_DST}" \
|
||
"⏭️ 跳过项目引导: ${GUIDE_DST}(已存在,保持原样)"
|
||
fi
|
||
|
||
INSTALLED_TARGETS="${INSTALLED_TARGETS}${INSTALLED_TARGETS:+, }$TARGET"
|
||
}
|
||
|
||
# ---------- 拉取最新 ----------
|
||
log_title "📦 spec-coding-skills 安装/更新"
|
||
echo ""
|
||
log_info "拉取最新版本..."
|
||
git clone --depth 1 --quiet "$REPO_URL" "$TMP_DIR"
|
||
|
||
VERSION=$(git -C "$TMP_DIR" describe --tags --always 2>/dev/null || git -C "$TMP_DIR" rev-parse --short HEAD)
|
||
log_info "版本: $VERSION"
|
||
|
||
case "$REQUEST" in
|
||
both)
|
||
log_info "模式: both"
|
||
install_layout codex
|
||
install_layout claude
|
||
;;
|
||
*)
|
||
resolve_layout "$REQUEST"
|
||
log_info "模式: $MODE"
|
||
install_layout "$REQUEST"
|
||
;;
|
||
esac
|
||
|
||
# ---------- 汇总 ----------
|
||
echo ""
|
||
log_title "✅ 完成!"
|
||
echo ""
|
||
echo " 🧭 模式: $REQUEST"
|
||
echo " 📁 目标目录: $INSTALLED_TARGETS"
|
||
echo " 📦 版本: $VERSION"
|
||
echo ""
|
||
echo " ✨ 新增: $TOTAL_NEW"
|
||
echo " 🔄 更新: ${TOTAL_UPDATED}(本地版本已备份为 .local.bak)"
|
||
echo " ⏭️ 无变化: $TOTAL_SKIPPED"
|
||
echo ""
|
||
|
||
if [ "$TOTAL_UPDATED" -gt 0 ]; then
|
||
log_warn "有 ${TOTAL_UPDATED} 个文件被更新,本地修改已备份为 .local.bak"
|
||
log_warn "如需恢复本地版本: mv SKILL.md.local.bak SKILL.md"
|
||
log_warn "如需对比差异: diff SKILL.md SKILL.md.local.bak"
|
||
fi
|