## 主要更新 ### 1. MCP 动态选择 - ✨ 支持安装时选择 MCP 提供商(ace-tool 或 auggie) - 🔧 自动生成 ~/.ccg/config.toml 配置文件 - 🔄 命令模板根据配置动态路由 MCP 工具 ### 2. 命令模板简化 - 📝 移除外部文档引用(memorys/MCP_USAGE.md) - 🎯 自包含配置读取说明,减少 50% 提示词长度 - 🔍 统一模式:读取 config → 获取 provider → 调用对应工具 ### 3. 配置驱动架构 - 🗂️ 新增 [mcp.tools] 工具名称映射 - 🏷️ 参数名映射(query vs information_request) - 🚫 优雅处理缺失功能(auggie 无 prompt enhancement) ### 4. 文件更改 - 修改: install.py(新增 MCP 选择逻辑) - 修改: config.json(operation: install_mcp) - 修改: 12 个命令模板(dev, enhance, code, analyze, etc.) - 删除: SLASH_COMMAND_FORMAT.md, UPDATES.md(已合并到文档) - 更新: CHANGELOG.md, README.md, CLAUDE.md ### 5. 兼容性 - ✅ 向后兼容:默认使用 ace-tool - ✅ 支持跳过 MCP 安装 - ✅ 配置不存在时使用默认值 Breaking Changes: 无
842 lines
26 KiB
Python
842 lines
26 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Skills-v2 安装器
|
||
Claude Code 多模型协作系统
|
||
|
||
用法:
|
||
python install.py # 安装核心模块(默认)
|
||
python install.py --module core # 安装核心模块
|
||
python install.py --module all # 安装所有启用的模块
|
||
python install.py --list-modules # 列出可用模块
|
||
python install.py --install-dir ~/.claude # 自定义安装目录
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import os
|
||
import platform
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
from typing import Any, Dict, List, Optional, Tuple
|
||
|
||
DEFAULT_INSTALL_DIR = "~/.claude"
|
||
DEFAULT_CONFIG = "config.json"
|
||
|
||
# 检测操作系统
|
||
IS_WINDOWS = platform.system() == "Windows"
|
||
|
||
|
||
def parse_args() -> argparse.Namespace:
|
||
parser = argparse.ArgumentParser(
|
||
description="Skills-v2 安装器 - 多模型协作系统"
|
||
)
|
||
parser.add_argument(
|
||
"--install-dir",
|
||
default=DEFAULT_INSTALL_DIR,
|
||
help=f"安装目录(默认: {DEFAULT_INSTALL_DIR})",
|
||
)
|
||
parser.add_argument(
|
||
"--module",
|
||
default="core",
|
||
help="要安装的模块: 'core', 'codex-only', 'gemini-only', 或 'all'",
|
||
)
|
||
parser.add_argument(
|
||
"--config",
|
||
default=DEFAULT_CONFIG,
|
||
help="配置文件路径",
|
||
)
|
||
parser.add_argument(
|
||
"--list-modules",
|
||
action="store_true",
|
||
help="列出可用模块并退出",
|
||
)
|
||
parser.add_argument(
|
||
"--force",
|
||
action="store_true",
|
||
help="强制覆盖现有文件",
|
||
)
|
||
parser.add_argument(
|
||
"--verbose", "-v",
|
||
action="store_true",
|
||
help="显示详细输出",
|
||
)
|
||
return parser.parse_args()
|
||
|
||
|
||
def load_config(config_path: str) -> Dict[str, Any]:
|
||
path = Path(config_path)
|
||
if not path.exists():
|
||
print(f"错误: 配置文件未找到: {config_path}", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
with path.open("r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
|
||
|
||
def list_modules(config: Dict[str, Any]) -> None:
|
||
print("\n📦 可用模块:\n")
|
||
modules = config.get("modules", {})
|
||
for name, module in modules.items():
|
||
status = "✅ 已启用" if module.get("enabled", False) else "⚪ 未启用"
|
||
desc = module.get("description", "无描述")
|
||
print(f" {name:15} {status:12} - {desc}")
|
||
print()
|
||
|
||
|
||
def ensure_dir(path: Path) -> None:
|
||
path.mkdir(parents=True, exist_ok=True)
|
||
|
||
|
||
def copy_file(src: Path, dst: Path, verbose: bool = False) -> None:
|
||
ensure_dir(dst.parent)
|
||
shutil.copy2(src, dst)
|
||
if verbose:
|
||
print(f" 📄 已复制: {src.name} -> {dst}")
|
||
|
||
|
||
def copy_dir(src: Path, dst: Path, verbose: bool = False) -> None:
|
||
if dst.exists():
|
||
shutil.rmtree(dst)
|
||
shutil.copytree(src, dst)
|
||
if verbose:
|
||
print(f" 📁 已复制目录: {src.name} -> {dst}")
|
||
|
||
|
||
def merge_dir(src: Path, dst: Path, verbose: bool = False) -> None:
|
||
ensure_dir(dst)
|
||
for item in src.iterdir():
|
||
dst_item = dst / item.name
|
||
if item.is_file():
|
||
shutil.copy2(item, dst_item)
|
||
if verbose:
|
||
print(f" 📄 已合并: {item.name}")
|
||
elif item.is_dir():
|
||
if dst_item.exists():
|
||
merge_dir(item, dst_item, verbose)
|
||
else:
|
||
shutil.copytree(item, dst_item)
|
||
if verbose:
|
||
print(f" 📁 已合并目录: {item.name}")
|
||
|
||
|
||
def check_go_installed() -> bool:
|
||
try:
|
||
result = subprocess.run(
|
||
["go", "version"],
|
||
capture_output=True,
|
||
text=True
|
||
)
|
||
return result.returncode == 0
|
||
except FileNotFoundError:
|
||
return False
|
||
|
||
|
||
def check_npm_installed() -> bool:
|
||
"""检查 npm 是否已安装"""
|
||
try:
|
||
result = subprocess.run(
|
||
["npm", "--version"],
|
||
capture_output=True,
|
||
text=True
|
||
)
|
||
return result.returncode == 0
|
||
except FileNotFoundError:
|
||
return False
|
||
|
||
|
||
def choose_mcp_provider() -> Optional[str]:
|
||
"""
|
||
让用户选择 MCP 提供商
|
||
返回: "ace-tool" | "auggie" | None (跳过)
|
||
"""
|
||
print("\n" + "=" * 60)
|
||
print("🔧 MCP 代码检索工具选择")
|
||
print("=" * 60)
|
||
print("\n请选择要安装的 MCP 工具:\n")
|
||
|
||
print(" [1] ace-tool (推荐)")
|
||
print(" • 第三方封装版本,使用更简单")
|
||
print(" • ✅ 内置 Prompt 增强工具 (enhance_prompt)")
|
||
print(" • ✅ 代码库上下文检索 (search_context)")
|
||
print(" • 需要注册获取 API Token: https://augmentcode.com/")
|
||
print()
|
||
|
||
print(" [2] auggie (官方原版)")
|
||
print(" • Augment 官方 MCP (@augmentcode/auggie)")
|
||
print(" • ⚠️ 不包含 Prompt 增强工具")
|
||
print(" • ✅ 代码库上下文检索 (codebase-retrieval)")
|
||
print(" • 📖 配置说明: https://linux.do/t/topic/1280612")
|
||
print()
|
||
|
||
print(" [0] 跳过 MCP 安装 (稍后手动配置)")
|
||
print()
|
||
|
||
while True:
|
||
choice = input("请输入选项 [1/2/0]: ").strip()
|
||
if choice == "1":
|
||
return "ace-tool"
|
||
elif choice == "2":
|
||
return "auggie"
|
||
elif choice == "0":
|
||
print(" ℹ️ 跳过 MCP 安装,稍后可手动配置")
|
||
return None
|
||
else:
|
||
print(" ❌ 无效选项,请输入 1、2 或 0")
|
||
|
||
|
||
def install_ace_tool(verbose: bool = False) -> Tuple[bool, str]:
|
||
"""
|
||
安装并配置 ace-tool MCP
|
||
返回 (成功, 消息)
|
||
|
||
重要: Claude Code CLI 从 ~/.claude.json 读取 MCP 配置
|
||
NOT ~/.mcp.json (那是给 Claude Desktop 的)
|
||
NOT ~/Library/Application Support/Claude/ (那也是 Claude Desktop)
|
||
"""
|
||
print("\n 🔧 配置 ace-tool MCP...")
|
||
|
||
# 检查 npm
|
||
if not check_npm_installed():
|
||
return False, "npm 未安装,请先安装 Node.js: https://nodejs.org/"
|
||
|
||
# 提示用户配置
|
||
print("\n 📋 ace-tool 需要配置 API 访问信息:")
|
||
print(" 获取方式: 访问 https://augmentcode.com/ 注册并获取 API Token")
|
||
print()
|
||
|
||
# 获取 base-url
|
||
default_base_url = "https://api.augmentcode.com"
|
||
base_url = input(f" 请输入 Base URL (直接回车使用默认值 {default_base_url}): ").strip()
|
||
if not base_url:
|
||
base_url = default_base_url
|
||
|
||
# 获取 token
|
||
token = input(" 请输入 API Token: ").strip()
|
||
if not token:
|
||
print(" ⚠️ Token 为空,稍后可手动配置")
|
||
token = ""
|
||
|
||
# Claude Code CLI 的配置文件路径: ~/.claude.json
|
||
config_file = Path.home() / ".claude.json"
|
||
|
||
# 读取现有配置(重要:保留所有其他字段!)
|
||
existing_config = {}
|
||
if config_file.exists():
|
||
try:
|
||
with config_file.open("r", encoding="utf-8") as f:
|
||
existing_config = json.load(f)
|
||
except json.JSONDecodeError as e:
|
||
print(f" ⚠️ ~/.claude.json 解析失败: {e}")
|
||
print(" ⚠️ 请检查文件格式,跳过 MCP 配置")
|
||
return False, f"~/.claude.json 解析失败: {e}"
|
||
except Exception as e:
|
||
print(f" ⚠️ 读取 ~/.claude.json 失败: {e}")
|
||
return False, f"读取配置失败: {e}"
|
||
|
||
# 确保 mcpServers 字段存在
|
||
if "mcpServers" not in existing_config:
|
||
existing_config["mcpServers"] = {}
|
||
|
||
# 添加或更新 ace-tool 配置(使用环境变量方式,更安全)
|
||
existing_config["mcpServers"]["ace-tool"] = {
|
||
"type": "stdio",
|
||
"command": "npx",
|
||
"args": ["-y", "ace-tool@latest"],
|
||
"env": {
|
||
"ACE_BASE_URL": base_url,
|
||
"ACE_TOKEN": token
|
||
}
|
||
}
|
||
|
||
# 写入配置(保留所有其他字段)
|
||
try:
|
||
with config_file.open("w", encoding="utf-8") as f:
|
||
json.dump(existing_config, f, indent=2, ensure_ascii=False)
|
||
if verbose:
|
||
print(f" 📄 已写入配置: {config_file}")
|
||
except Exception as e:
|
||
return False, f"写入配置失败: {e}"
|
||
|
||
# 测试安装
|
||
print("\n 🚀 验证 ace-tool 安装...")
|
||
try:
|
||
result = subprocess.run(
|
||
["npx", "-y", "ace-tool@latest", "--version"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=60
|
||
)
|
||
if result.returncode == 0:
|
||
version = result.stdout.strip() or "unknown"
|
||
if verbose:
|
||
print(f" ✅ ace-tool 版本: {version}")
|
||
else:
|
||
if verbose:
|
||
print(f" ⚠️ ace-tool 验证失败,但配置已保存")
|
||
except subprocess.TimeoutExpired:
|
||
print(" ⚠️ 验证超时,但配置已保存")
|
||
except Exception as e:
|
||
if verbose:
|
||
print(f" ⚠️ 验证异常: {e}")
|
||
|
||
return True, f"ace-tool MCP 配置完成: {config_file}"
|
||
|
||
|
||
def install_auggie(verbose: bool = False) -> Tuple[bool, str]:
|
||
"""
|
||
安装并配置 auggie MCP (Augment 官方版本)
|
||
返回 (成功, 消息)
|
||
"""
|
||
print("\n 🔧 配置 auggie MCP...")
|
||
|
||
# 检查 npm
|
||
if not check_npm_installed():
|
||
return False, "npm 未安装,请先安装 Node.js: https://nodejs.org/"
|
||
|
||
# 提示用户配置说明
|
||
print("\n 📖 auggie 配置说明:")
|
||
print(" 详细配置教程: https://linux.do/t/topic/1280612")
|
||
print(" 获取 API Token: 访问 https://augmentcode.com/ 注册")
|
||
print()
|
||
print(" ⚠️ 注意: auggie 不包含 Prompt 增强工具")
|
||
print(" 如需使用 Prompt 增强,请查看上述教程手动配置")
|
||
print()
|
||
|
||
# 安装 auggie
|
||
print(" 🚀 安装 @augmentcode/auggie@prerelease...")
|
||
try:
|
||
result = subprocess.run(
|
||
["npm", "install", "-g", "@augmentcode/auggie@prerelease"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=120
|
||
)
|
||
if result.returncode != 0:
|
||
return False, f"npm 安装失败: {result.stderr}"
|
||
if verbose:
|
||
print(f" ✅ auggie 安装成功")
|
||
except subprocess.TimeoutExpired:
|
||
return False, "安装超时"
|
||
except Exception as e:
|
||
return False, f"安装异常: {e}"
|
||
|
||
# 获取 token
|
||
print("\n 请输入 API Token (从 https://augmentcode.com/ 获取):")
|
||
token = input(" Token: ").strip()
|
||
if not token:
|
||
print(" ⚠️ Token 为空,稍后可手动配置")
|
||
token = ""
|
||
|
||
# Claude Code CLI 的配置文件路径: ~/.claude.json
|
||
config_file = Path.home() / ".claude.json"
|
||
|
||
# 读取现有配置
|
||
existing_config = {}
|
||
if config_file.exists():
|
||
try:
|
||
with config_file.open("r", encoding="utf-8") as f:
|
||
existing_config = json.load(f)
|
||
except json.JSONDecodeError as e:
|
||
print(f" ⚠️ ~/.claude.json 解析失败: {e}")
|
||
return False, f"~/.claude.json 解析失败: {e}"
|
||
except Exception as e:
|
||
print(f" ⚠️ 读取 ~/.claude.json 失败: {e}")
|
||
return False, f"读取配置失败: {e}"
|
||
|
||
# 确保 mcpServers 字段存在
|
||
if "mcpServers" not in existing_config:
|
||
existing_config["mcpServers"] = {}
|
||
|
||
# 添加或更新 auggie 配置
|
||
existing_config["mcpServers"]["auggie-mcp"] = {
|
||
"type": "stdio",
|
||
"command": "auggie",
|
||
"env": {
|
||
"AUGMENT_API_KEY": token
|
||
}
|
||
}
|
||
|
||
# 写入配置
|
||
try:
|
||
with config_file.open("w", encoding="utf-8") as f:
|
||
json.dump(existing_config, f, indent=2, ensure_ascii=False)
|
||
if verbose:
|
||
print(f" 📄 已写入配置: {config_file}")
|
||
except Exception as e:
|
||
return False, f"写入配置失败: {e}"
|
||
|
||
print("\n ✅ auggie MCP 配置完成")
|
||
print(f" 📖 详细配置说明: https://linux.do/t/topic/1280612")
|
||
|
||
return True, f"auggie MCP 配置完成: {config_file}"
|
||
|
||
|
||
def create_ccg_config(mcp_provider: str, install_dir: Path, verbose: bool = False) -> bool:
|
||
"""
|
||
创建 ~/.ccg/config.toml 配置文件
|
||
|
||
Args:
|
||
mcp_provider: "ace-tool" | "auggie" | "none"
|
||
install_dir: 安装目录 (通常是 ~/.claude)
|
||
verbose: 详细输出
|
||
|
||
Returns:
|
||
成功与否
|
||
"""
|
||
ccg_dir = Path.home() / ".ccg"
|
||
config_file = ccg_dir / "config.toml"
|
||
|
||
# 确保目录存在
|
||
ensure_dir(ccg_dir)
|
||
|
||
# 生成配置内容
|
||
config_content = f"""# CCG (Claude + Codex + Gemini) 多模型协作系统配置
|
||
# 生成时间: {subprocess.run(['date'], capture_output=True, text=True).stdout.strip()}
|
||
|
||
[mcp]
|
||
# MCP 提供商: ace-tool | auggie | none
|
||
provider = "{mcp_provider}"
|
||
|
||
[mcp.tools]
|
||
# 工具名称映射(命令模板会自动读取此配置)
|
||
# 代码检索工具
|
||
code_search_ace = "mcp__ace-tool__search_context"
|
||
code_search_auggie = "mcp__auggie-mcp__codebase-retrieval"
|
||
|
||
# Prompt 增强工具
|
||
prompt_enhance_ace = "mcp__ace-tool__enhance_prompt"
|
||
prompt_enhance_auggie = "" # auggie 不支持
|
||
|
||
# 参数名称映射
|
||
query_param_ace = "query"
|
||
query_param_auggie = "information_request"
|
||
|
||
[mcp.ace-tool]
|
||
# ace-tool (第三方封装) 功能说明
|
||
features = ["Prompt 增强", "代码检索"]
|
||
setup_url = "https://augmentcode.com/"
|
||
|
||
[mcp.auggie]
|
||
# auggie (官方原版) 功能说明
|
||
features = ["代码检索"]
|
||
setup_url = "https://linux.do/t/topic/1280612"
|
||
note = "auggie 不包含 Prompt 增强工具,需手动配置"
|
||
|
||
[routing]
|
||
# 路由模式: smart | parallel | sequential
|
||
mode = "smart"
|
||
|
||
[routing.frontend]
|
||
# 前端任务路由配置
|
||
models = ["gemini", "codex"]
|
||
primary = "gemini"
|
||
strategy = "parallel"
|
||
|
||
[routing.backend]
|
||
# 后端任务路由配置
|
||
models = ["codex", "gemini"]
|
||
primary = "codex"
|
||
strategy = "parallel"
|
||
|
||
[routing.review]
|
||
# 代码审查路由配置
|
||
models = ["codex", "gemini"]
|
||
strategy = "parallel"
|
||
"""
|
||
|
||
try:
|
||
with config_file.open("w", encoding="utf-8") as f:
|
||
f.write(config_content)
|
||
if verbose:
|
||
print(f" 📄 已创建配置: {config_file}")
|
||
return True
|
||
except Exception as e:
|
||
print(f" ⚠️ 创建配置失败: {e}", file=sys.stderr)
|
||
return False
|
||
|
||
|
||
def get_prebuilt_binary(source_dir: Path) -> Optional[Path]:
|
||
"""查找预编译的二进制文件"""
|
||
system = platform.system().lower()
|
||
machine = platform.machine().lower()
|
||
|
||
# 映射架构名称
|
||
arch_map = {
|
||
"x86_64": "amd64",
|
||
"amd64": "amd64",
|
||
"arm64": "arm64",
|
||
"aarch64": "arm64",
|
||
}
|
||
arch = arch_map.get(machine, machine)
|
||
|
||
# 映射系统名称
|
||
if system == "darwin":
|
||
binary_name = f"codeagent-wrapper-darwin-{arch}"
|
||
elif system == "linux":
|
||
binary_name = f"codeagent-wrapper-linux-{arch}"
|
||
elif system == "windows":
|
||
binary_name = f"codeagent-wrapper-windows-{arch}.exe"
|
||
else:
|
||
return None
|
||
|
||
binary_path = source_dir / "bin" / binary_name
|
||
if binary_path.exists():
|
||
return binary_path
|
||
return None
|
||
|
||
|
||
def build_go_binary(
|
||
source_dir: Path,
|
||
binary_name: str,
|
||
verbose: bool = False
|
||
) -> Tuple[bool, Optional[Path]]:
|
||
if not check_go_installed():
|
||
print(" ❌ Go 未安装,请先安装 Go:", file=sys.stderr)
|
||
print(" https://go.dev/doc/install", file=sys.stderr)
|
||
return False, None
|
||
|
||
# Windows 添加 .exe 扩展名
|
||
if IS_WINDOWS and not binary_name.endswith(".exe"):
|
||
output_name = binary_name + ".exe"
|
||
else:
|
||
output_name = binary_name
|
||
|
||
if verbose:
|
||
print(f" 🔨 正在编译 {output_name}...")
|
||
|
||
try:
|
||
result = subprocess.run(
|
||
["go", "build", "-o", output_name, "."],
|
||
cwd=source_dir,
|
||
capture_output=True,
|
||
text=True
|
||
)
|
||
|
||
if result.returncode != 0:
|
||
print(f" ❌ 编译失败: {result.stderr}", file=sys.stderr)
|
||
return False, None
|
||
|
||
binary_path = source_dir / output_name
|
||
if not binary_path.exists():
|
||
print(f" ❌ 编译后未找到二进制文件", file=sys.stderr)
|
||
return False, None
|
||
|
||
if verbose:
|
||
print(f" ✅ 编译完成: {output_name}")
|
||
|
||
return True, binary_path
|
||
|
||
except Exception as e:
|
||
print(f" ❌ 编译错误: {e}", file=sys.stderr)
|
||
return False, None
|
||
|
||
|
||
def install_binary_to_path(
|
||
binary_path: Path,
|
||
verbose: bool = False,
|
||
target_name: Optional[str] = None
|
||
) -> bool:
|
||
# 确定目标文件名
|
||
if target_name:
|
||
if IS_WINDOWS and not target_name.endswith(".exe"):
|
||
final_name = target_name + ".exe"
|
||
else:
|
||
final_name = target_name
|
||
else:
|
||
final_name = binary_path.name
|
||
|
||
# 根据系统选择安装目录
|
||
if IS_WINDOWS:
|
||
install_dirs = [
|
||
Path.home() / ".local" / "bin",
|
||
Path(os.environ.get("LOCALAPPDATA", "")) / "Programs" / "codeagent-wrapper",
|
||
Path.home() / "bin",
|
||
]
|
||
else:
|
||
install_dirs = [
|
||
Path.home() / ".local" / "bin",
|
||
Path("/usr/local/bin"),
|
||
]
|
||
|
||
for install_dir in install_dirs:
|
||
if install_dir.exists() or install_dir == install_dirs[0]:
|
||
try:
|
||
ensure_dir(install_dir)
|
||
target = install_dir / final_name
|
||
shutil.copy2(binary_path, target)
|
||
|
||
# 设置执行权限(仅 Unix)
|
||
if not IS_WINDOWS:
|
||
os.chmod(target, 0o755)
|
||
|
||
if verbose:
|
||
print(f" 📦 已安装到: {target}")
|
||
|
||
# 检查安装目录是否在 PATH 中
|
||
path_dirs = os.environ.get("PATH", "").split(os.pathsep)
|
||
if str(install_dir) not in path_dirs:
|
||
print(f" ⚠️ 注意: {install_dir} 可能不在 PATH 中")
|
||
if IS_WINDOWS:
|
||
print(f" 添加到 PATH: setx PATH \"%PATH%;{install_dir}\"")
|
||
else:
|
||
print(f" 添加到 PATH: export PATH=\"{install_dir}:$PATH\"")
|
||
|
||
return True
|
||
except PermissionError:
|
||
if verbose:
|
||
print(f" ⚠️ {install_dir} 权限不足,尝试下一个...")
|
||
continue
|
||
except Exception as e:
|
||
if verbose:
|
||
print(f" ⚠️ 安装到 {install_dir} 失败: {e}")
|
||
continue
|
||
|
||
print(" ❌ 无法安装二进制文件到 PATH", file=sys.stderr)
|
||
if IS_WINDOWS:
|
||
print(f" 手动安装: 复制 {binary_path} 到 PATH 中的目录", file=sys.stderr)
|
||
else:
|
||
print(f" 手动安装: sudo cp {binary_path} /usr/local/bin/", file=sys.stderr)
|
||
return False
|
||
|
||
|
||
def execute_operation(
|
||
op: Dict[str, Any],
|
||
source_dir: Path,
|
||
install_dir: Path,
|
||
verbose: bool = False
|
||
) -> bool:
|
||
op_type = op.get("type")
|
||
source = op.get("source", "")
|
||
target = op.get("target", source)
|
||
desc = op.get("description", "")
|
||
|
||
src_path = source_dir / source
|
||
dst_path = install_dir / target
|
||
|
||
if verbose:
|
||
print(f"\n 🔧 {desc}")
|
||
|
||
try:
|
||
if op_type == "copy_file":
|
||
if not src_path.exists():
|
||
print(f" ⚠️ 源文件未找到: {src_path}", file=sys.stderr)
|
||
return False
|
||
copy_file(src_path, dst_path, verbose)
|
||
|
||
elif op_type == "copy_dir":
|
||
if not src_path.exists():
|
||
print(f" ⚠️ 源目录未找到: {src_path}", file=sys.stderr)
|
||
return False
|
||
copy_dir(src_path, dst_path, verbose)
|
||
|
||
elif op_type == "merge_dir":
|
||
if not src_path.exists():
|
||
print(f" ⚠️ 源目录未找到: {src_path}", file=sys.stderr)
|
||
return False
|
||
merge_dir(src_path, dst_path, verbose)
|
||
|
||
elif op_type == "build_go":
|
||
binary_name = op.get("binary", "")
|
||
|
||
# 优先使用预编译的二进制文件
|
||
prebuilt = get_prebuilt_binary(source_dir)
|
||
if prebuilt:
|
||
if verbose:
|
||
print(f" 📦 使用预编译二进制: {prebuilt.name}")
|
||
# 重命名为目标名称
|
||
target_name = binary_name
|
||
if IS_WINDOWS and not target_name.endswith(".exe"):
|
||
target_name += ".exe"
|
||
binary_path = prebuilt
|
||
else:
|
||
# 没有预编译二进制,尝试编译
|
||
if verbose:
|
||
print(f" ℹ️ 未找到预编译二进制,尝试编译...")
|
||
if not src_path.exists():
|
||
print(f" ⚠️ 源目录未找到: {src_path}", file=sys.stderr)
|
||
return False
|
||
|
||
success, binary_path = build_go_binary(src_path, binary_name, verbose)
|
||
if not success:
|
||
return False
|
||
|
||
# 安装到 PATH
|
||
if not install_binary_to_path(binary_path, verbose, target_name=binary_name):
|
||
return False
|
||
|
||
elif op_type == "install_ace_tool":
|
||
# 兼容旧配置,直接安装 ace-tool
|
||
success, message = install_ace_tool(verbose)
|
||
if not success:
|
||
print(f" ⚠️ {message}")
|
||
print(f" ℹ️ 可稍后手动配置 ace-tool MCP")
|
||
return True # 不阻止其他安装
|
||
if verbose:
|
||
print(f" ℹ️ {message}")
|
||
# 创建配置文件
|
||
create_ccg_config("ace-tool", install_dir, verbose)
|
||
|
||
elif op_type == "install_mcp":
|
||
# 动态 MCP 选择
|
||
mcp_provider = choose_mcp_provider()
|
||
|
||
if mcp_provider is None:
|
||
print(" ⚠️ 跳过 MCP 安装")
|
||
create_ccg_config("none", install_dir, verbose)
|
||
return True # 不阻止其他安装
|
||
|
||
# 根据选择安装对应的 MCP
|
||
if mcp_provider == "ace-tool":
|
||
success, message = install_ace_tool(verbose)
|
||
elif mcp_provider == "auggie":
|
||
success, message = install_auggie(verbose)
|
||
else:
|
||
print(f" ❌ 未知的 MCP 提供商: {mcp_provider}", file=sys.stderr)
|
||
return False
|
||
|
||
if not success:
|
||
print(f" ⚠️ {message}")
|
||
print(f" ℹ️ 可稍后手动配置 MCP")
|
||
create_ccg_config("none", install_dir, verbose)
|
||
return True # 不阻止其他安装
|
||
|
||
if verbose:
|
||
print(f" ℹ️ {message}")
|
||
|
||
# 创建配置文件
|
||
create_ccg_config(mcp_provider, install_dir, verbose)
|
||
|
||
else:
|
||
print(f" ⚠️ 未知操作类型: {op_type}", file=sys.stderr)
|
||
return False
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f" ❌ 错误: {e}", file=sys.stderr)
|
||
return False
|
||
|
||
|
||
def install_module(
|
||
name: str,
|
||
module: Dict[str, Any],
|
||
source_dir: Path,
|
||
install_dir: Path,
|
||
verbose: bool = False
|
||
) -> bool:
|
||
print(f"\n📦 正在安装模块: {name}")
|
||
print(f" {module.get('description', '')}")
|
||
|
||
operations = module.get("operations", [])
|
||
success_count = 0
|
||
|
||
for op in operations:
|
||
if execute_operation(op, source_dir, install_dir, verbose):
|
||
success_count += 1
|
||
|
||
if success_count == len(operations):
|
||
print(f" ✅ 模块 '{name}' 安装成功!")
|
||
return True
|
||
else:
|
||
print(f" ⚠️ 模块 '{name}' 安装完成,但有 {len(operations) - success_count} 个警告")
|
||
return False
|
||
|
||
|
||
def main() -> int:
|
||
args = parse_args()
|
||
|
||
# 解析路径
|
||
config_path = Path(args.config)
|
||
if not config_path.is_absolute():
|
||
config_path = Path(__file__).parent / args.config
|
||
|
||
source_dir = config_path.parent
|
||
install_dir = Path(args.install_dir).expanduser().resolve()
|
||
|
||
# 加载配置
|
||
config = load_config(str(config_path))
|
||
|
||
# 列出模块模式
|
||
if args.list_modules:
|
||
list_modules(config)
|
||
return 0
|
||
|
||
# 打印标题
|
||
print("\n" + "=" * 50)
|
||
print("🚀 Skills-v2 安装器")
|
||
print(" 多模型协作系统")
|
||
print("=" * 50)
|
||
print(f"\n📁 安装目录: {install_dir}")
|
||
|
||
# 确保安装目录存在
|
||
ensure_dir(install_dir)
|
||
|
||
# 确定要安装的模块
|
||
modules = config.get("modules", {})
|
||
modules_to_install = []
|
||
|
||
if args.module == "all":
|
||
modules_to_install = [
|
||
(name, mod) for name, mod in modules.items()
|
||
if mod.get("enabled", False)
|
||
]
|
||
elif args.module in modules:
|
||
modules_to_install = [(args.module, modules[args.module])]
|
||
else:
|
||
print(f"\n❌ 未知模块: {args.module}", file=sys.stderr)
|
||
print(" 使用 --list-modules 查看可用模块")
|
||
return 1
|
||
|
||
if not modules_to_install:
|
||
print("\n⚠️ 没有可安装的模块")
|
||
return 0
|
||
|
||
# 安装模块
|
||
success = True
|
||
for name, module in modules_to_install:
|
||
if not install_module(name, module, source_dir, install_dir, args.verbose):
|
||
success = False
|
||
|
||
# 打印摘要
|
||
print("\n" + "=" * 50)
|
||
if success:
|
||
print("✅ 安装完成!")
|
||
print("\n📋 已安装命令 (/ccg:xxx):")
|
||
print(" 开发工作流:")
|
||
print(" /ccg:dev - 完整6阶段多模型工作流")
|
||
print(" /ccg:code - 多模型代码生成(智能路由)")
|
||
print(" /ccg:debug - UltraThink 多模型调试")
|
||
print(" /ccg:test - 多模型测试生成")
|
||
print(" /ccg:bugfix - 质量门控修复(90%+ 通过)")
|
||
print(" /ccg:think - 深度分析")
|
||
print(" /ccg:optimize - 性能优化")
|
||
print(" /ccg:frontend - 前端任务 → Gemini")
|
||
print(" /ccg:backend - 后端任务 → Codex")
|
||
print(" /ccg:review - 双模型代码审查")
|
||
print(" /ccg:analyze - 双模型分析")
|
||
print(" /ccg:enhance - Prompt 增强")
|
||
print(" Git 工具:")
|
||
print(" /ccg:commit - 智能提交")
|
||
print(" /ccg:rollback - 交互式回滚")
|
||
print(" /ccg:clean-branches - 清理分支")
|
||
print(" /ccg:worktree - Worktree 管理")
|
||
print(" 项目初始化:")
|
||
print(" /ccg:init - 初始化项目 AI 上下文")
|
||
print("\n🎯 快速开始:")
|
||
print(' /ccg:dev "实现用户认证功能"')
|
||
print(' /ccg:debug "登录接口返回500错误"')
|
||
print(' /ccg:bugfix "密码重置失败"')
|
||
else:
|
||
print("⚠️ 安装完成,但有警告")
|
||
print("=" * 50 + "\n")
|
||
|
||
return 0 if success else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|