#!/usr/bin/env bash # 黑手党提案 Agent — 一键安装/更新脚本 # 支持两种调用方式: # 1. git clone 后:bash agent/install.sh # 2. curl 下载后:bash /tmp/chanpinhsd-main/agent/install.sh set -euo pipefail GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' NC='\033[0m' info() { printf "${GREEN}[mafia]${NC} %s\n" "$*"; } warn() { printf "${YELLOW}[mafia]${NC} %s\n" "$*"; } error() { printf "${RED}[mafia]${NC} %s\n" "$*" >&2; exit 1; } COPAW_HOME="${COPAW_HOME:-$HOME/.copaw}" AGENT_ID="mafia-expert" WORKSPACE_DIR="$COPAW_HOME/workspaces/$AGENT_ID" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # ── 检测操作系统 ────────────────────────────────────────────────────── OS="$(uname -s)" case "$OS" in Darwin) info "检测到 macOS" ;; Linux) info "检测到 Linux" ;; MINGW*|MSYS*|CYGWIN*) warn "检测到 Windows (Git Bash/MSYS),部分功能可能受限" ;; *) error "不支持的操作系统: $OS" ;; esac # ── 检查 CoPaw 是否已安装 ────────────────────────────────────────────── if [ ! -f "$COPAW_HOME/config.json" ]; then if command -v copaw &>/dev/null || [ -f "$COPAW_HOME/bin/copaw" ]; then info "CoPaw 已安装但未初始化,正在初始化..." export PATH="$COPAW_HOME/bin:$PATH" copaw init --defaults --accept-security else error "CoPaw 未安装。请先安装 CoPaw 桌面客户端:https://copaw.agentscope.io" fi else info "CoPaw 已就绪" fi # ── 创建工作区并复制文件 ─────────────────────────────────────────────── info "配置 $AGENT_ID 工作区..." mkdir -p "$WORKSPACE_DIR/skills" mkdir -p "$WORKSPACE_DIR/cases" mkdir -p "$WORKSPACE_DIR/iteration_reports" mkdir -p "$WORKSPACE_DIR/memory" # 核心配置(skill.json 单独合并,不覆盖) for f in AGENTS.md SOUL.md PROFILE.md HEARTBEAT.md agent.json; do if [ -f "$SCRIPT_DIR/$f" ]; then cp "$SCRIPT_DIR/$f" "$WORKSPACE_DIR/$f" fi done # MEMORY.md 只在首次创建(不覆盖用户积累的记忆) if [ ! -f "$WORKSPACE_DIR/MEMORY.md" ]; then cp "$SCRIPT_DIR/MEMORY.md" "$WORKSPACE_DIR/MEMORY.md" 2>/dev/null || echo "# 黑手党提案专家长期记忆" > "$WORKSPACE_DIR/MEMORY.md" fi # Skills(每次全量覆盖,保证最新) if [ -d "$SCRIPT_DIR/skills" ]; then for skill_dir in "$SCRIPT_DIR/skills"/*/; do skill_name="$(basename "$skill_dir")" # installer skill 不复制到 mafia-expert,它属于 default agent if [ "$skill_name" = "mafia_agent_installer" ]; then continue fi mkdir -p "$WORKSPACE_DIR/skills/$skill_name" cp "$skill_dir"* "$WORKSPACE_DIR/skills/$skill_name/" 2>/dev/null || true done fi info "文件复制完成" # ── 合并 skill.json(注入自定义 skills,保留内置 skills)────────────── info "注册自定义 Skills..." python3 -c " import json, os, time ws_skill = os.path.expanduser('$WORKSPACE_DIR/skill.json') repo_skill = '$SCRIPT_DIR/skill.json' # 读取工作区已有的 manifest(含内置 skills) if os.path.exists(ws_skill): with open(ws_skill) as f: manifest = json.load(f) else: manifest = {'schema_version': 'workspace-skill-manifest.v1', 'version': 0, 'skills': {}} # 读取仓库的自定义 skills 定义 if os.path.exists(repo_skill): with open(repo_skill) as f: repo = json.load(f) for name, skill_cfg in repo.get('skills', {}).items(): manifest['skills'][name] = skill_cfg print(f' + {name}') # 同时扫描 active_skills 目录,确保所有 SKILL.md 都被注册 skills_dir = os.path.expanduser('$WORKSPACE_DIR/skills') if os.path.isdir(skills_dir): for d in os.listdir(skills_dir): if d.startswith('.'): continue skill_md = os.path.join(skills_dir, d, 'SKILL.md') if os.path.exists(skill_md) and d not in manifest['skills']: manifest['skills'][d] = { 'enabled': True, 'channels': ['all'], 'source': 'customized', 'metadata': { 'name': d, 'description': '', 'version_text': '1.0.0', 'source': 'customized', 'protected': False, 'requirements': {'require_bins': [], 'require_envs': []}, }, 'requirements': {'require_bins': [], 'require_envs': []}, } print(f' + {d} (from dir)') manifest['version'] = int(time.time() * 1000) with open(ws_skill, 'w') as f: json.dump(manifest, f, indent=2, ensure_ascii=False) print(' skills 注册完成') " # ── 注册到 config.json ───────────────────────────────────────────────── info "注册 Agent..." python3 -c " import json, os config_path = os.path.expanduser('$COPAW_HOME/config.json') with open(config_path, 'r') as f: cfg = json.load(f) agent_id = '$AGENT_ID' ws_dir = '$WORKSPACE_DIR' changed = False if agent_id not in cfg['agents']['profiles']: cfg['agents']['profiles'][agent_id] = { 'id': agent_id, 'workspace_dir': ws_dir, 'enabled': True } changed = True if agent_id not in cfg['agents']['agent_order']: cfg['agents']['agent_order'].append(agent_id) changed = True if changed: with open(config_path, 'w') as f: json.dump(cfg, f, indent=2, ensure_ascii=False) print(' ✅ 注册成功') else: print(' ✅ 已注册,配置已更新') " # ── 记录来源路径(便于后续更新)───────────────────────────────────────── REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" echo "$REPO_DIR" > "$WORKSPACE_DIR/.repo_path" # ── 清理临时文件(如果是 curl 下载的)────────────────────────────────── if [[ "$SCRIPT_DIR" == /tmp/* ]]; then info "清理临时文件..." rm -rf "$(cd "$SCRIPT_DIR/.." && pwd)" fi # ── 完成 ─────────────────────────────────────────────────────────────── echo "" info "════════════════════════════════════════════════" info " ✅ 黑手党提案专家 安装/更新完成!" info "════════════════════════════════════════════════" echo "" info "下一步:" info " 1. 打开 CoPaw 客户端" info " 2. 刷新页面(Cmd+R / F5)" info " 3. 左上角切换到「黑手党提案专家」" info " 4. 发送:我想为 XX 品牌做一个黑手党提案" echo ""