Files
ai-proj-helper/plugins/skill-manager-plugin/scripts/skill.sh

394 lines
12 KiB
Bash
Executable File

#!/bin/bash
# skill.sh - Skills 管理器主入口
# 用法:
# ./skill.sh list - 列出已安装技能
# ./skill.sh install <repo> - 安装新技能
# ./skill.sh upgrade [name] - 升级技能
# ./skill.sh uninstall <name> - 卸载技能
# ./skill.sh rollback <name> <version> - 回滚版本
# ./skill.sh info <name> - 查看技能详情
# ./skill.sh enable <name> - 启用技能
# ./skill.sh disable <name> - 禁用技能
# ./skill.sh check - 检查所有技能更新
set -e
SKILLS_DIR="$HOME/.claude/skills"
REPOS_DIR="$SKILLS_DIR/repos"
SCRIPTS_DIR="$SKILLS_DIR/skill-manager/scripts"
REGISTRY_SCRIPT="$SCRIPTS_DIR/registry.sh"
GIT_OPS_SCRIPT="$SCRIPTS_DIR/git-ops.sh"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 显示 banner
show_banner() {
echo -e "${CYAN}"
echo "╔═══════════════════════════════════════╗"
echo "║ Skills Manager v1.0.0 ║"
echo "║ 自我进化技能管理器 ║"
echo "╚═══════════════════════════════════════╝"
echo -e "${NC}"
}
# 从 URL 提取技能名称
extract_name() {
local repo="$1"
echo "$repo" | sed -E 's|.*/([^/]+)(\.git)?$|\1|' | sed 's/\.git$//'
}
# 列出所有技能
cmd_list() {
echo -e "${BLUE}已安装技能:${NC}"
echo "════════════════════════════════════════"
"$REGISTRY_SCRIPT" list
}
# 安装技能
cmd_install() {
local repo="$1"
if [ -z "$repo" ]; then
echo -e "${RED}用法: skill install <repo>${NC}"
echo "示例: skill install https://github.com/org/skill-name.git"
exit 1
fi
local name=$(extract_name "$repo")
echo -e "${BLUE}安装技能: $name${NC}"
echo "════════════════════════════════════════"
# 检查是否已安装
if [ -d "$REPOS_DIR/$name" ]; then
echo -e "${YELLOW}技能已安装,使用 'skill upgrade $name' 更新${NC}"
exit 1
fi
# 克隆仓库
"$GIT_OPS_SCRIPT" clone "$repo" "$name"
# 验证 skill.yaml
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
"$SCRIPTS_DIR/validate.sh" "$REPOS_DIR/$name"
fi
# 创建符号链接到 skills 目录
local skill_dir="$SKILLS_DIR/$name"
if [ ! -d "$skill_dir" ]; then
mkdir -p "$skill_dir"
# 复制 SKILL.md
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
cp "$REPOS_DIR/$name/$prompt_file" "$skill_dir/SKILL.md"
fi
fi
# 执行 post_install 钩子
if command -v yq &> /dev/null && [ -f "$skill_yaml" ]; then
local hooks=$(yq e '.hooks.post_install[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行安装后钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
echo ""
echo -e "${GREEN}✓ 技能安装完成: $name${NC}"
}
# 升级技能
cmd_upgrade() {
local name="$1"
echo -e "${BLUE}升级技能${NC}"
echo "════════════════════════════════════════"
if [ -z "$name" ]; then
# 升级所有技能
echo "升级所有技能..."
local skills=$(ls -1 "$REPOS_DIR" 2>/dev/null || echo "")
if [ -z "$skills" ]; then
echo "没有已安装的技能"
exit 0
fi
for skill in $skills; do
echo ""
echo -e "${CYAN}>>> $skill${NC}"
"$GIT_OPS_SCRIPT" pull "$skill" || echo -e "${YELLOW}跳过 $skill${NC}"
done
else
# 升级指定技能
if [ ! -d "$REPOS_DIR/$name" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
"$GIT_OPS_SCRIPT" pull "$name"
# 同步 SKILL.md
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
mkdir -p "$SKILLS_DIR/$name"
cp "$REPOS_DIR/$name/$prompt_file" "$SKILLS_DIR/$name/SKILL.md"
fi
# 执行 post_update 钩子
local hooks=$(yq e '.hooks.post_update[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行更新后钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
fi
echo ""
echo -e "${GREEN}✓ 升级完成${NC}"
}
# 卸载技能
cmd_uninstall() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill uninstall <name>${NC}"
exit 1
fi
echo -e "${BLUE}卸载技能: $name${NC}"
echo "════════════════════════════════════════"
if [ ! -d "$REPOS_DIR/$name" ]; then
echo -e "${YELLOW}技能不存在: $name${NC}"
exit 0
fi
# 确认
echo -e "${YELLOW}确认卸载 $name? [y/N]${NC}"
read -r confirm
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
echo "取消卸载"
exit 0
fi
# 执行 post_uninstall 钩子
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if command -v yq &> /dev/null && [ -f "$skill_yaml" ]; then
local hooks=$(yq e '.hooks.post_uninstall[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行卸载钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
# 删除仓库
rm -rf "$REPOS_DIR/$name"
echo -e "${GREEN}✓ 已删除仓库: $REPOS_DIR/$name${NC}"
# 删除技能目录
rm -rf "$SKILLS_DIR/$name"
echo -e "${GREEN}✓ 已删除技能目录: $SKILLS_DIR/$name${NC}"
# 从 registry 移除
"$REGISTRY_SCRIPT" remove "$name"
echo ""
echo -e "${GREEN}✓ 技能已卸载: $name${NC}"
}
# 回滚版本
cmd_rollback() {
local name="$1"
local version="$2"
if [ -z "$name" ] || [ -z "$version" ]; then
echo -e "${RED}用法: skill rollback <name> <version>${NC}"
exit 1
fi
echo -e "${BLUE}回滚技能: $name$version${NC}"
echo "════════════════════════════════════════"
"$GIT_OPS_SCRIPT" checkout "$name" "$version"
# 同步 SKILL.md
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
mkdir -p "$SKILLS_DIR/$name"
cp "$REPOS_DIR/$name/$prompt_file" "$SKILLS_DIR/$name/SKILL.md"
fi
fi
echo ""
echo -e "${GREEN}✓ 回滚完成${NC}"
}
# 查看技能详情
cmd_info() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill info <name>${NC}"
exit 1
fi
echo -e "${BLUE}技能详情: $name${NC}"
echo "════════════════════════════════════════"
"$GIT_OPS_SCRIPT" status "$name"
# 显示 skill.yaml 信息
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ] && command -v yq &> /dev/null; then
echo ""
echo "技能配置:"
echo "----------------------------------------"
echo "描述: $(yq e '.description' "$skill_yaml" | head -1)"
echo "作者: $(yq e '.author // "未知"' "$skill_yaml")"
local deps=$(yq e '.dependencies[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$deps" ]; then
echo "依赖: $deps"
fi
local keywords=$(yq e '.triggers.keywords[]' "$skill_yaml" 2>/dev/null | tr '\n' ', ' | sed 's/,$//')
if [ -n "$keywords" ]; then
echo "关键词: $keywords"
fi
fi
}
# 启用技能
cmd_enable() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill enable <name>${NC}"
exit 1
fi
"$REGISTRY_SCRIPT" update "$name" status "active"
echo -e "${GREEN}✓ 已启用技能: $name${NC}"
}
# 禁用技能
cmd_disable() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill disable <name>${NC}"
exit 1
fi
"$REGISTRY_SCRIPT" update "$name" status "disabled"
echo -e "${GREEN}✓ 已禁用技能: $name${NC}"
}
# 检查更新
cmd_check() {
echo -e "${BLUE}检查技能更新${NC}"
echo "════════════════════════════════════════"
local skills=$(ls -1 "$REPOS_DIR" 2>/dev/null || echo "")
if [ -z "$skills" ]; then
echo "没有已安装的技能"
exit 0
fi
local has_updates=0
for skill in $skills; do
local result=$("$GIT_OPS_SCRIPT" has-updates "$skill" 2>/dev/null | head -1)
if [ "$result" == "yes" ]; then
local behind=$("$GIT_OPS_SCRIPT" has-updates "$skill" 2>/dev/null | tail -1)
echo -e "${YELLOW}$skill 有更新 ($behind)${NC}"
has_updates=1
else
echo -e "${GREEN}$skill 已是最新${NC}"
fi
done
echo ""
if [ $has_updates -eq 1 ]; then
echo -e "${YELLOW}执行 'skill upgrade' 升级所有技能${NC}"
else
echo -e "${GREEN}所有技能已是最新版本${NC}"
fi
}
# 主命令分发
case "${1:-help}" in
list|ls)
cmd_list
;;
install|add)
cmd_install "$2"
;;
upgrade|update)
cmd_upgrade "$2"
;;
uninstall|remove)
cmd_uninstall "$2"
;;
rollback)
cmd_rollback "$2" "$3"
;;
info|show)
cmd_info "$2"
;;
enable)
cmd_enable "$2"
;;
disable)
cmd_disable "$2"
;;
check)
cmd_check
;;
help|--help|-h)
show_banner
echo "用法: skill <command> [args]"
echo ""
echo "命令:"
echo " list 列出已安装技能"
echo " install <repo> 安装新技能"
echo " upgrade [name] 升级技能(不指定则升级全部)"
echo " uninstall <name> 卸载技能"
echo " rollback <name> <version> 回滚到指定版本"
echo " info <name> 查看技能详情"
echo " enable <name> 启用技能"
echo " disable <name> 禁用技能"
echo " check 检查所有技能更新"
echo ""
echo "示例:"
echo " skill install https://github.com/org/skill-name.git"
echo " skill upgrade coolbuy-paas"
echo " skill rollback coolbuy-paas v1.1.0"
;;
*)
echo -e "${RED}未知命令: $1${NC}"
echo "使用 'skill help' 查看帮助"
exit 1
;;
esac