Skill Manager 管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。 前置条件 - Git 已安装(用于 GitHub 克隆) - 有写入目标 Agent 配置目录的权限,例如 、 、 安装行为 - 本地路径 (Skill) → 符号链接(保持与源同步) - 本地路径 (Command) → 符号链接(保持与源同步) - 本地集合目录 → 批量符号链接 - GitHub 仓库/子目录 → 克隆后删除 .git(静态复制)+ 自动安全检查 目标目录识别 执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录: - 在 或其子目录调用时,目标为 - 在项目根目录包含 、 或 时,目标为对应配置目录下的 或 - 在 、 、 内调用时,目标为其上级配置目录 - 如需显式指定目标根目录,可使用 参数或设置 - 从全局配置目录(如 )调用时,会尝试通过 git 自动发现项目本地目录,并打印告警 支持的来源类型 本地路径(符号链接) GitHub 仓库根目录(克隆,删除 .git) GitHub 子目录(稀疏克隆,删除 .git) 工作流程 安装 1. 检测来源类型 - 自动识别本地路径、GitHub 仓库或子目录 2. 检测 Item 类型 - 自动识别是 Skill(目录)还是 Co…

, # 可能是 requests 的拼写错误\n r'^numpy

Skill Manager 管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。 前置条件 - Git 已安装(用于 GitHub 克隆) - 有写入目标 Agent 配置目录的权限,例如 、 、 安装行为 - 本地路径 (Skill) → 符号链接(保持与源同步) - 本地路径 (Command) → 符号链接(保持与源同步) - 本地集合目录 → 批量符号链接 - GitHub 仓库/子目录 → 克隆后删除 .git(静态复制)+ 自动安全检查 目标目录识别 执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录: - 在 或其子目录调用时,目标为 - 在项目根目录包含 、 或 时,目标为对应配置目录下的 或 - 在 、 、 内调用时,目标为其上级配置目录 - 如需显式指定目标根目录,可使用 参数或设置 - 从全局配置目录(如 )调用时,会尝试通过 git 自动发现项目本地目录,并打印告警 支持的来源类型 本地路径(符号链接) GitHub 仓库根目录(克隆,删除 .git) GitHub 子目录(稀疏克隆,删除 .git) 工作流程 安装 1. 检测来源类型 - 自动识别本地路径、GitHub 仓库或子目录 2. 检测 Item 类型 - 自动识别是 Skill(目录)还是 Co…

, # 检查是否来自官方\n r'^pytol

Skill Manager 管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。 前置条件 - Git 已安装(用于 GitHub 克隆) - 有写入目标 Agent 配置目录的权限,例如 、 、 安装行为 - 本地路径 (Skill) → 符号链接(保持与源同步) - 本地路径 (Command) → 符号链接(保持与源同步) - 本地集合目录 → 批量符号链接 - GitHub 仓库/子目录 → 克隆后删除 .git(静态复制)+ 自动安全检查 目标目录识别 执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录: - 在 或其子目录调用时,目标为 - 在项目根目录包含 、 或 时,目标为对应配置目录下的 或 - 在 、 、 内调用时,目标为其上级配置目录 - 如需显式指定目标根目录,可使用 参数或设置 - 从全局配置目录(如 )调用时,会尝试通过 git 自动发现项目本地目录,并打印告警 支持的来源类型 本地路径(符号链接) GitHub 仓库根目录(克隆,删除 .git) GitHub 子目录(稀疏克隆,删除 .git) 工作流程 安装 1. 检测来源类型 - 自动识别本地路径、GitHub 仓库或子目录 2. 检测 Item 类型 - 自动识别是 Skill(目录)还是 Co…

, # pylint typo\n r'^djanga

Skill Manager 管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。 前置条件 - Git 已安装(用于 GitHub 克隆) - 有写入目标 Agent 配置目录的权限,例如 、 、 安装行为 - 本地路径 (Skill) → 符号链接(保持与源同步) - 本地路径 (Command) → 符号链接(保持与源同步) - 本地集合目录 → 批量符号链接 - GitHub 仓库/子目录 → 克隆后删除 .git(静态复制)+ 自动安全检查 目标目录识别 执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录: - 在 或其子目录调用时,目标为 - 在项目根目录包含 、 或 时,目标为对应配置目录下的 或 - 在 、 、 内调用时,目标为其上级配置目录 - 如需显式指定目标根目录,可使用 参数或设置 - 从全局配置目录(如 )调用时,会尝试通过 git 自动发现项目本地目录,并打印告警 支持的来源类型 本地路径(符号链接) GitHub 仓库根目录(克隆,删除 .git) GitHub 子目录(稀疏克隆,删除 .git) 工作流程 安装 1. 检测来源类型 - 自动识别本地路径、GitHub 仓库或子目录 2. 检测 Item 类型 - 自动识别是 Skill(目录)还是 Co…

, # django typo\n r'^flaskk

Skill Manager 管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。 前置条件 - Git 已安装(用于 GitHub 克隆) - 有写入目标 Agent 配置目录的权限,例如 、 、 安装行为 - 本地路径 (Skill) → 符号链接(保持与源同步) - 本地路径 (Command) → 符号链接(保持与源同步) - 本地集合目录 → 批量符号链接 - GitHub 仓库/子目录 → 克隆后删除 .git(静态复制)+ 自动安全检查 目标目录识别 执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录: - 在 或其子目录调用时,目标为 - 在项目根目录包含 、 或 时,目标为对应配置目录下的 或 - 在 、 、 内调用时,目标为其上级配置目录 - 如需显式指定目标根目录,可使用 参数或设置 - 从全局配置目录(如 )调用时,会尝试通过 git 自动发现项目本地目录,并打印告警 支持的来源类型 本地路径(符号链接) GitHub 仓库根目录(克隆,删除 .git) GitHub 子目录(稀疏克隆,删除 .git) 工作流程 安装 1. 检测来源类型 - 自动识别本地路径、GitHub 仓库或子目录 2. 检测 Item 类型 - 自动识别是 Skill(目录)还是 Co…

, # flask typo\n]\n\n# 敏感依赖(可能带来安全风险)\nSENSITIVE_DEPENDENCIES = [\n 'pickles', 'marshal', 'shelve', # 序列化风险\n 'paramiko', 'fabric', 'asyncssh', # SSH\n 'pexpect', 'ptyprocess', # 伪终端\n 'cryptography', 'pycrypto', 'pycryptodome', # 加密\n]\n\n# 提示词安全检测规则 - 用于检测 SKILL.md 中的恶意提示\nPROMPT_SECURITY_PATTERNS = {\n 'prompt_injection': {\n 'patterns': [\n # 经典提示注入\n r'(?i)ignore\\s+(all\\s+)?(previous|above|prior)\\s+instructions?',\n r'(?i)disregard\\s+(all\\s+)?(previous|above)\\s+instructions?',\n r'(?i)forget\\s+(all\\s+)?(previous|above)\\s+instructions?',\n r'(?i)override\\s+(all\\s+)?(previous|default)\\s+(instructions?|settings?|rules?)',\n r'(?i)bypass\\s+(all\\s+)?(security|safety|restrictions?|filters?)',\n # 越狱尝试\n r'(?i)jailbreak',\n r'(?i)DAN\\s+mode',\n r'(?i)developer\\s+mode',\n r'(?i)ignore\\s+your\\s+training',\n r'(?i)you\\s+are\\s+now\\s+free',\n r'(?i)no\\s+restrictions?',\n r'(?i)unrestricted\\s+mode',\n # 角色扮演欺骗\n r'(?i)pretend\\s+(you\\s+are|to\\s+be)\\s+(a\\s+)?(admin|root|system|sudo)',\n r'(?i)act\\s+as\\s+(if\\s+you\\s+are\\s+)?(admin|root|system|sudo)',\n r'(?i)role[ -]?play\\s+as\\s+(admin|root|system)',\n ],\n 'severity': 'critical',\n 'message': '检测到提示注入模式,可能试图绕过安全限制',\n },\n\n 'data_collection_instruction': {\n 'patterns': [\n # 收集敏感数据指令\n r'(?i)(collect|gather|harvest|extract)\\s+.*\\s+(password|token|key|secret|credential)',\n r'(?i)(send|upload|transmit|exfiltrate)\\s+.*\\s+(to\\s+)?(external|third[ -]?party|remote)',\n r'(?i)(read|access|scan)\\s+.*\\s+\\.env\\b',\n r'(?i)(read|access|scan)\\s+.*\\s+(user\\s+)?(ssh|aws|gpg)\\s+(keys?|credentials?)',\n r'(?i)(copy|steal|exfiltrate)\\s+.*\\s+(file|data|credential)',\n r'(?i)(export|output)\\s+.*\\s+(environment|env)\\s+variables?',\n r'(?i)(log|record|save)\\s+.*\\s+(user\\s+)?(password|token|api[ -]?key)',\n r'(?i)(capture|intercept)\\s+.*\\s+(clipboard|input|keystroke)',\n ],\n 'severity': 'critical',\n 'message': '检测到数据收集指令,可能试图窃取敏感信息',\n },\n\n 'execution_instruction': {\n 'patterns': [\n # 在提示词中指令执行命令\n r'(?i)(execute|run|invoke)\\s+.*\\s+(shell|terminal|command|script)',\n r'(?i)(must|should|always)\\s+.*\\s+(run|execute)\\s+.*\\s+(on\\s+)?(install|startup|load)',\n r'(?i)(download|fetch)\\s+.*\\s+and\\s+(execute|run|install)',\n r'(?i)curl\\s+\\S+\\s*\\|\\s*(bash|sh|python|node)',\n r'(?i)wget\\s+\\S+\\s*\\|\\s*(bash|sh|python|node)',\n r'(?i)(install|setup)\\s+.*\\s+(automatically|silently|background)',\n r'(?i)(spawn|fork|launch)\\s+.*\\s+(process|subprocess|child)',\n ],\n 'severity': 'high',\n 'message': '检测到执行指令,可能在提示词中隐藏恶意执行逻辑',\n },\n\n 'privilege_escalation_instruction': {\n 'patterns': [\n # 权限提升指令\n r'(?i)(run|execute)\\s+.*\\s+with\\s+(root|admin|sudo|elevated)\\s+(privileges?|access|permissions?)',\n r'(?i)(grant|give|obtain)\\s+.*\\s+(root|admin|full)\\s+(access|permissions?)',\n r'(?i)sudo\\s+(apt|yum|brew|pip|npm)',\n r'(?i)(disable|turn\\s+off|bypass)\\s+.*\\s+(security|firewall|antivirus|protection)',\n r'(?i)(modify|edit|change)\\s+.*\\s+(system|host)\\s+(files?|settings?|config)',\n ],\n 'severity': 'high',\n 'message': '检测到权限提升指令',\n },\n\n 'deceptive_description': {\n 'patterns': [\n # 欺骗性描述 - 描述与功能不符\n r'(?i)(simple|harmless|safe|innocent)\\s+.*\\s+(tool|script|utility)',\n r'(?i)(just|only|merely)\\s+.*\\s+(checks?|reads?|displays?)\\s+.*\\s+(file|system|info)',\n r'(?i)for\\s+(educational|research|testing)\\s+purposes?\\s+only',\n r'(?i)(hidden|secret|stealth|covert)\\s+.*\\s+(feature|mode|function)',\n r'(?i)do\\s+not\\s+(run|use|execute)\\s+.*\\s+(in\\s+)?(production|real)',\n ],\n 'severity': 'medium',\n 'message': '检测到可疑的描述模式,可能存在欺骗性说明',\n },\n\n 'network_instruction': {\n 'patterns': [\n # 网络外泄指令\n r'(?i)(connect|phone\\s+home|beacon)\\s+.*\\s+(to\\s+)?(server|endpoint|api)',\n r'(?i)(post|send|upload)\\s+.*\\s+(data|file|credential)\\s+.*\\s+to\\s+\\S+',\n r'(?i)(establish|create|open)\\s+.*\\s+(reverse|backdoor|covert)\\s+(shell|connection)',\n r'(?i)(ping|check[ -]in)\\s+.*\\s+(with|to)\\s+.*\\s+(server|c2|command[ -]and[ -]control)',\n r'(?i)websocket\\s+.*\\s+(connect|client)',\n ],\n 'severity': 'high',\n 'message': '检测到网络通信指令,可能存在数据外泄风险',\n },\n\n 'persistence_instruction': {\n 'patterns': [\n # 持久化指令\n r'(?i)(add|create|install)\\s+.*\\s+(cron|scheduled\\s+task|startup|launch)',\n r'(?i)(persist|remain|stay)\\s+.*\\s+(running|active|installed)',\n r'(?i)(auto[ -]?start|auto[ -]?run|auto[ -]?launch)',\n r'(?i)(background|daemon)\\s+.*\\s+(process|service|task)',\n r'(?i)(modify|edit)\\s+.*\\s+(\\.\\w*rc|profile|bashrc|zshrc)',\n ],\n 'severity': 'high',\n 'message': '检测到持久化指令,可能试图建立长期驻留',\n },\n\n 'hidden_instruction': {\n 'patterns': [\n # 隐藏指令 - 使用特殊字符或编码\n r'[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]', # 控制字符\n r'\\\\u[0-9a-fA-F]{4}', # Unicode 转义\n r'&#x[0-9a-fA-F]+;', # HTML 实体\n r'&#[0-9]+;', # HTML 数字实体\n r'\\\\x[0-9a-fA-F]{2}', # 十六进制转义\n # 零宽字符\n r'[\\u200b-\\u200f\\u2028-\\u202f\\u205f-\\u206f]',\n ],\n 'severity': 'medium',\n 'message': '检测到隐藏字符或编码,可能隐藏恶意内容',\n },\n\n 'social_engineering': {\n 'patterns': [\n # 社会工程学模式\n r'(?i)(urgent|critical|important)\\s+.*\\s+(update|security|patch)',\n r'(?i)(verify|confirm|validate)\\s+.*\\s+(your\\s+)?(identity|account|credential)',\n r'(?i)(winner|won|selected|chosen)\\s+.*\\s+(for|to)',\n r'(?i)(limited|expires?\\s+soon|act\\s+now)',\n r'(?i)(trust|safe|verified|official|authentic)',\n ],\n 'severity': 'medium',\n 'message': '检测到社会工程学模式,可能用于欺骗用户',\n },\n}\n\n\nclass SecurityAnalyzer:\n \"\"\"AI Agent/Skill 安全分析器\"\"\"\n\n def __init__(self, repo_path: str):\n \"\"\"初始化分析器\n\n Args:\n repo_path: 仓库本地路径\n \"\"\"\n self.repo_path = Path(repo_path)\n if not self.repo_path.exists():\n raise ValueError(f\"仓库路径不存在: {repo_path}\")\n\n self.findings: List[SecurityFinding] = []\n self.exclude_dirs = {'.git', 'node_modules', '__pycache__', 'venv', '.venv',\n 'dist', 'build', 'target', '.cache', 'vendor'}\n\n def analyze(self) -> Dict:\n \"\"\"执行完整安全分析\n\n Returns:\n 安全分析结果\n \"\"\"\n self.findings = []\n\n # 执行各项检测\n skill_structure = self._analyze_skill_structure()\n dangerous_patterns = self._detect_dangerous_patterns()\n dependencies = self._analyze_dependencies()\n network = self._analyze_network_activity()\n file_ops = self._analyze_file_operations()\n secrets = self._detect_secrets()\n prompt_security = self._analyze_prompt_security() # 新增:提示词安全检测\n\n # 计算风险汇总\n risk_summary = self._calculate_risk_summary()\n\n return {\n 'risk_summary': risk_summary,\n 'skill_structure': skill_structure,\n 'dangerous_patterns': dangerous_patterns,\n 'dependencies': dependencies,\n 'network': network,\n 'file_operations': file_ops,\n 'secrets': secrets,\n 'prompt_security': prompt_security, # 新增\n 'findings': self._serialize_findings(),\n }\n\n def _analyze_skill_structure(self) -> Dict:\n \"\"\"分析 Skill 结构\"\"\"\n result = {\n 'is_skill': False,\n 'has_skill_md': False,\n 'has_scripts': False,\n 'has_hooks': False,\n 'has_mcp': False,\n 'warnings': [],\n }\n\n # 检查是否是 skill 结构\n skill_md = self.repo_path / 'SKILL.md'\n if skill_md.exists():\n result['has_skill_md'] = True\n result['is_skill'] = True\n\n # 解析 SKILL.md\n try:\n content = skill_md.read_text(encoding='utf-8')\n result['description'] = self._extract_description(content)\n\n # 检测 MCP 相关\n if any(p in content.lower() for p in ['mcp', 'modelcontextprotocol']):\n result['has_mcp'] = True\n result['warnings'].append('MCP 服务器配置检测到')\n except Exception:\n pass\n\n # 检查脚本目录\n scripts_dir = self.repo_path / 'scripts'\n if scripts_dir.exists() and scripts_dir.is_dir():\n result['has_scripts'] = True\n script_files = list(scripts_dir.glob('*.py')) + list(scripts_dir.glob('*.js'))\n result['script_count'] = len(script_files)\n\n # 检查 package.json 中的 hooks\n package_json = self.repo_path / 'package.json'\n if package_json.exists():\n try:\n data = json.loads(package_json.read_text(encoding='utf-8'))\n if 'scripts' in data:\n hooks = ['postinstall', 'preinstall', 'poststart', 'prestart']\n for hook in hooks:\n if hook in data['scripts']:\n result['has_hooks'] = True\n result['warnings'].append(f'检测到 npm hook: {hook}')\n except Exception:\n pass\n\n return result\n\n def _extract_description(self, content: str) -> str:\n \"\"\"从 SKILL.md 提取描述\"\"\"\n # 尝试从 frontmatter 提取\n if content.startswith('---'):\n parts = content.split('---', 2)\n if len(parts) >= 3:\n frontmatter = parts[1]\n for line in frontmatter.split('\\n'):\n if line.startswith('description:'):\n return line.split(':', 1)[1].strip().strip('\"').strip(\"'\")\n\n # 尝试从第一段提取\n lines = content.split('\\n')\n for line in lines:\n line = line.strip()\n if line and not line.startswith('#') and not line.startswith('---'):\n return line[:200]\n\n return \"\"\n\n def _detect_dangerous_patterns(self) -> Dict:\n \"\"\"检测危险代码模式\"\"\"\n results = defaultdict(list)\n\n # 获取所有代码文件\n code_extensions = {'.py', '.js', '.jsx', '.ts', '.tsx', '.go', '.rs',\n '.java', '.sh', '.bash', '.ps1', '.rb', '.php'}\n code_files = []\n for ext in code_extensions:\n code_files.extend(self.repo_path.rglob(f'*{ext}'))\n\n # 过滤排除目录\n code_files = [f for f in code_files\n if not any(exc in f.parts for exc in self.exclude_dirs)]\n\n for file_path in code_files:\n self._scan_file_for_patterns(file_path, results)\n\n # 合并 Skill 特有检测\n self._scan_skill_risks(results)\n\n return dict(results)\n\n def _scan_file_for_patterns(self, file_path: Path, results: Dict):\n \"\"\"扫描文件中的危险模式\"\"\"\n try:\n content = file_path.read_text(encoding='utf-8', errors='ignore')\n lines = content.split('\\n')\n rel_path = str(file_path.relative_to(self.repo_path))\n\n for category, config in DANGEROUS_PATTERNS.items():\n for pattern in config['patterns']:\n try:\n regex = re.compile(pattern, re.IGNORECASE)\n for i, line in enumerate(lines, 1):\n if regex.search(line):\n finding = SecurityFinding(\n category=category,\n severity=config['severity'],\n file=rel_path,\n line=i,\n code=line.strip()[:100],\n message=config['message'],\n pattern=pattern,\n )\n self.findings.append(finding)\n results[category].append({\n 'file': rel_path,\n 'line': i,\n 'code': line.strip()[:100],\n 'severity': config['severity'],\n 'message': config['message'],\n })\n except re.error:\n pass\n except Exception:\n pass\n\n def _scan_skill_risks(self, results: Dict):\n \"\"\"扫描 Skill 特有风险\"\"\"\n for category, config in SKILL_RISKS.items():\n if 'patterns' not in config:\n continue\n\n for file_path in self.repo_path.rglob('*'):\n if not file_path.is_file():\n continue\n if any(exc in file_path.parts for exc in self.exclude_dirs):\n continue\n\n try:\n content = file_path.read_text(encoding='utf-8', errors='ignore')\n lines = content.split('\\n')\n rel_path = str(file_path.relative_to(self.repo_path))\n\n for pattern in config['patterns']:\n try:\n regex = re.compile(pattern, re.IGNORECASE)\n for i, line in enumerate(lines, 1):\n if regex.search(line):\n finding = SecurityFinding(\n category=f'skill_{category}',\n severity=config['severity'],\n file=rel_path,\n line=i,\n code=line.strip()[:100],\n message=config['message'],\n pattern=pattern,\n )\n self.findings.append(finding)\n results[f'skill_{category}'].append({\n 'file': rel_path,\n 'line': i,\n 'code': line.strip()[:100],\n 'severity': config['severity'],\n 'message': config['message'],\n })\n except re.error:\n pass\n except Exception:\n pass\n\n def _analyze_dependencies(self) -> Dict:\n \"\"\"分析依赖安全\"\"\"\n result = {\n 'package_managers': [],\n 'total_dependencies': 0,\n 'high_risk_dependencies': [],\n 'warnings': [],\n }\n\n # Python 依赖\n req_files = ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile']\n for req_file in req_files:\n path = self.repo_path / req_file\n if path.exists():\n result['package_managers'].append('python')\n deps = self._parse_python_deps(path)\n result['total_dependencies'] += len(deps)\n result['high_risk_dependencies'].extend(\n self._check_sensitive_deps(deps, 'python')\n )\n\n # Node.js 依赖\n package_json = self.repo_path / 'package.json'\n if package_json.exists():\n result['package_managers'].append('nodejs')\n try:\n data = json.loads(package_json.read_text(encoding='utf-8'))\n deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}\n result['total_dependencies'] += len(deps)\n result['high_risk_dependencies'].extend(\n self._check_sensitive_deps(list(deps.keys()), 'nodejs')\n )\n except Exception:\n pass\n\n if result['high_risk_dependencies']:\n result['warnings'].append(\n f\"发现 {len(result['high_risk_dependencies'])} 个敏感依赖\"\n )\n\n return result\n\n def _parse_python_deps(self, path: Path) -> List[str]:\n \"\"\"解析 Python 依赖文件\"\"\"\n deps = []\n try:\n content = path.read_text(encoding='utf-8')\n for line in content.split('\\n'):\n line = line.strip()\n if line and not line.startswith('#'):\n # 提取包名\n match = re.match(r'^([a-zA-Z0-9_-]+)', line)\n if match:\n deps.append(match.group(1))\n except Exception:\n pass\n return deps\n\n def _check_sensitive_deps(self, deps: List[str], manager: str) -> List[Dict]:\n \"\"\"检查敏感依赖\"\"\"\n high_risk = []\n for dep in deps:\n dep_lower = dep.lower()\n for sensitive in SENSITIVE_DEPENDENCIES:\n if sensitive in dep_lower:\n high_risk.append({\n 'name': dep,\n 'manager': manager,\n 'risk': f'敏感依赖: {sensitive}',\n })\n return high_risk\n\n def _analyze_network_activity(self) -> Dict:\n \"\"\"分析网络活动\"\"\"\n result = {\n 'external_urls': [],\n 'websockets': [],\n 'api_endpoints': [],\n 'warnings': [],\n }\n\n url_pattern = r'https?://[^\\s\\'\"\u003c>]+'\n ws_pattern = r'wss?://[^\\s\\'\"\u003c>]+'\n\n for file_path in self.repo_path.rglob('*'):\n if not file_path.is_file():\n continue\n if any(exc in file_path.parts for exc in self.exclude_dirs):\n continue\n if file_path.suffix not in {'.py', '.js', '.ts', '.jsx', '.tsx', '.json',\n '.md', '.yml', '.yaml', '.sh'}:\n continue\n\n try:\n content = file_path.read_text(encoding='utf-8', errors='ignore')\n rel_path = str(file_path.relative_to(self.repo_path))\n\n # 检测 URL\n urls = re.findall(url_pattern, content)\n for url in urls:\n # 过滤常见的安全 URL\n if not any(safe in url for safe in\n ['github.com', 'example.com', 'localhost', '127.0.0.1',\n 'docs.', 'help.', 'readme']):\n result['external_urls'].append({\n 'file': rel_path,\n 'url': url[:100],\n })\n\n # 检测 WebSocket\n ws_urls = re.findall(ws_pattern, content)\n for url in ws_urls:\n result['websockets'].append({\n 'file': rel_path,\n 'url': url[:100],\n })\n\n except Exception:\n pass\n\n # 去重\n seen_urls = set()\n unique_urls = []\n for item in result['external_urls']:\n if item['url'] not in seen_urls:\n seen_urls.add(item['url'])\n unique_urls.append(item)\n result['external_urls'] = unique_urls[:20] # 限制数量\n\n if result['external_urls']:\n result['warnings'].append(f\"发现 {len(result['external_urls'])} 个外部 URL\")\n\n return result\n\n def _analyze_file_operations(self) -> Dict:\n \"\"\"分析文件操作\"\"\"\n result = {\n 'sensitive_paths': [],\n 'file_operations': [],\n 'warnings': [],\n }\n\n sensitive_paths = [\n '~/.ssh', '~/.aws', '~/.gnupg', '.env', '.pem', '.key',\n '/etc/passwd', '/etc/shadow', '/etc/hosts',\n 'id_rsa', 'id_ed25519', 'authorized_keys',\n '.bashrc', '.zshrc', '.profile',\n ]\n\n for file_path in self.repo_path.rglob('*'):\n if not file_path.is_file():\n continue\n if any(exc in file_path.parts for exc in self.exclude_dirs):\n continue\n\n try:\n content = file_path.read_text(encoding='utf-8', errors='ignore')\n rel_path = str(file_path.relative_to(self.repo_path))\n\n for sensitive in sensitive_paths:\n if sensitive in content:\n result['sensitive_paths'].append({\n 'file': rel_path,\n 'path': sensitive,\n })\n\n except Exception:\n pass\n\n if result['sensitive_paths']:\n result['warnings'].append(\n f\"发现 {len(result['sensitive_paths'])} 处敏感路径引用\"\n )\n\n return result\n\n def _detect_secrets(self) -> Dict:\n \"\"\"检测硬编码的敏感信息\"\"\"\n result = {\n 'findings': [],\n 'warnings': [],\n }\n\n # 使用 hardcoded_secrets 规则\n if 'hardcoded_secrets' not in DANGEROUS_PATTERNS:\n return result\n\n patterns = DANGEROUS_PATTERNS['hardcoded_secrets']['patterns']\n\n for file_path in self.repo_path.rglob('*'):\n if not file_path.is_file():\n continue\n if any(exc in file_path.parts for exc in self.exclude_dirs):\n continue\n # 跳过某些文件类型\n if file_path.suffix in {'.png', '.jpg', '.jpeg', '.gif', '.ico',\n '.pdf', '.zip', '.tar', '.gz'}:\n continue\n\n try:\n content = file_path.read_text(encoding='utf-8', errors='ignore')\n lines = content.split('\\n')\n rel_path = str(file_path.relative_to(self.repo_path))\n\n for pattern in patterns:\n try:\n regex = re.compile(pattern, re.IGNORECASE)\n for i, line in enumerate(lines, 1):\n match = regex.search(line)\n if match:\n # 脱敏处理\n masked_line = self._mask_secret(line, match.group())\n result['findings'].append({\n 'file': rel_path,\n 'line': i,\n 'type': 'potential_secret',\n 'masked': masked_line[:100],\n })\n except re.error:\n pass\n except Exception:\n pass\n\n if result['findings']:\n result['warnings'].append(\n f\"发现 {len(result['findings'])} 处可能的硬编码凭证\"\n )\n\n return result\n\n def _mask_secret(self, line: str, secret: str) -> str:\n \"\"\"脱敏处理\"\"\"\n if len(secret) > 8:\n masked = secret[:4] + '*' * (len(secret) - 8) + secret[-4:]\n return line.replace(secret, masked)\n return line\n\n def _analyze_prompt_security(self) -> Dict:\n \"\"\"分析 SKILL.md 和其他 Markdown 文件中的提示词安全\n\n 检测提示注入、数据收集指令、执行指令等恶意模式\n \"\"\"\n result = {\n 'files_analyzed': 0,\n 'findings': [],\n 'warnings': [],\n 'categories_found': set(),\n }\n\n # 获取所有 Markdown 文件\n md_files = list(self.repo_path.rglob('*.md'))\n # 过滤排除目录\n md_files = [f for f in md_files\n if not any(exc in f.parts for exc in self.exclude_dirs)]\n\n for file_path in md_files:\n try:\n content = file_path.read_text(encoding='utf-8', errors='ignore')\n rel_path = str(file_path.relative_to(self.repo_path))\n result['files_analyzed'] += 1\n\n # 对每个文件应用提示词安全检测规则\n self._scan_prompt_content(content, rel_path, result)\n\n except Exception:\n pass\n\n # 转换 set 为 list 以便 JSON 序列化\n result['categories_found'] = list(result['categories_found'])\n\n if result['findings']:\n result['warnings'].append(\n f\"在 {result['files_analyzed']} 个 Markdown 文件中发现 \"\n f\"{len(result['findings'])} 处提示词安全问题\"\n )\n\n return result\n\n def _scan_prompt_content(self, content: str, file_path: str, result: Dict):\n \"\"\"扫描 Markdown 内容中的提示词安全风险\"\"\"\n lines = content.split('\\n')\n\n for category, config in PROMPT_SECURITY_PATTERNS.items():\n for pattern in config['patterns']:\n try:\n regex = re.compile(pattern, re.IGNORECASE | re.MULTILINE)\n for i, line in enumerate(lines, 1):\n match = regex.search(line)\n if match:\n # 记录发现\n finding = {\n 'file': file_path,\n 'line': i,\n 'category': category,\n 'severity': config['severity'],\n 'message': config['message'],\n 'matched_text': match.group()[:50], # 限制长度\n 'code': line.strip()[:100],\n }\n result['findings'].append(finding)\n result['categories_found'].add(category)\n\n # 同时添加到全局 findings 列表\n security_finding = SecurityFinding(\n category=f'prompt_{category}',\n severity=config['severity'],\n file=file_path,\n line=i,\n code=line.strip()[:100],\n message=f\"[提示词安全] {config['message']}\",\n pattern=pattern,\n )\n self.findings.append(security_finding)\n\n except re.error:\n pass\n\n def _calculate_risk_summary(self) -> Dict:\n \"\"\"计算风险汇总\"\"\"\n severity_counts = defaultdict(int)\n for finding in self.findings:\n severity_counts[finding.severity] += 1\n\n # 计算总体风险等级\n if severity_counts['critical'] > 0:\n overall_risk = 'critical'\n elif severity_counts['high'] > 3:\n overall_risk = 'high'\n elif severity_counts['high'] > 0 or severity_counts['medium'] > 5:\n overall_risk = 'medium'\n elif severity_counts['medium'] > 0 or severity_counts['low'] > 0:\n overall_risk = 'low'\n else:\n overall_risk = 'none'\n\n # 按类别统计\n category_counts = defaultdict(int)\n for finding in self.findings:\n category_counts[finding.category] += 1\n\n return {\n 'overall_risk': overall_risk,\n 'total_findings': len(self.findings),\n 'severity_counts': dict(severity_counts),\n 'category_counts': dict(category_counts),\n 'risk_emoji': {\n 'critical': '🔴',\n 'high': '🟠',\n 'medium': '🟡',\n 'low': '🟢',\n 'none': '✅',\n }.get(overall_risk, '⚪'),\n }\n\n def _serialize_findings(self) -> List[Dict]:\n \"\"\"序列化发现结果\"\"\"\n return [\n {\n 'category': f.category,\n 'severity': f.severity,\n 'file': f.file,\n 'line': f.line,\n 'code': f.code,\n 'message': f.message,\n }\n for f in self.findings\n ]\n\n def generate_report(self) -> str:\n \"\"\"生成安全分析报告\"\"\"\n analysis = self.analyze()\n summary = analysis['risk_summary']\n\n lines = [\n \"# 安全分析报告\",\n \"\",\n f\"> **分析目标**: {self.repo_path.name}\",\n f\"> **总体风险等级**: {summary['risk_emoji']} **{summary['overall_risk'].upper()}**\",\n \"\",\n \"---\",\n \"\",\n \"## 风险概览\",\n \"\",\n ]\n\n # 严重程度统计\n if summary['total_findings'] > 0:\n lines.append(\"### 发现统计\")\n lines.append(\"\")\n lines.append(\"| 严重程度 | 数量 |\")\n lines.append(\"|:---------|:-----|\")\n for severity in ['critical', 'high', 'medium', 'low']:\n count = summary['severity_counts'].get(severity, 0)\n emoji = {'critical': '🔴', 'high': '🟠', 'medium': '🟡', 'low': '🟢'}.get(severity, '⚪')\n if count > 0:\n lines.append(f\"| {emoji} {severity.upper()} | {count} |\")\n lines.append(\"\")\n else:\n lines.append(\"✅ **未发现安全风险**\")\n lines.append(\"\")\n\n # Skill 结构分析\n skill_info = analysis['skill_structure']\n if skill_info['is_skill']:\n lines.append(\"## Skill 结构分析\")\n lines.append(\"\")\n lines.append(f\"- **SKILL.md**: {'✅ 存在' if skill_info['has_skill_md'] else '❌ 缺失'}\")\n lines.append(f\"- **脚本目录**: {'✅ 存在' if skill_info['has_scripts'] else '❌ 缺失'}\")\n if skill_info.get('has_hooks'):\n lines.append(f\"- **安装钩子**: ⚠️ 检测到\")\n if skill_info.get('has_mcp'):\n lines.append(f\"- **MCP 配置**: ⚠️ 检测到\")\n lines.append(\"\")\n\n # 按类别详细展示\n if analysis['findings']:\n lines.append(\"## 详细发现\")\n lines.append(\"\")\n\n # 按严重程度分组\n for severity in ['critical', 'high', 'medium', 'low']:\n findings = [f for f in analysis['findings'] if f['severity'] == severity]\n if not findings:\n continue\n\n emoji = {'critical': '🔴', 'high': '🟠', 'medium': '🟡', 'low': '🟢'}.get(severity, '⚪')\n lines.append(f\"### {emoji} {severity.upper()} 风险 ({len(findings)})\")\n lines.append(\"\")\n\n # 按类别分组\n by_category = defaultdict(list)\n for f in findings:\n by_category[f['category']].append(f)\n\n for category, items in by_category.items():\n lines.append(f\"#### {category}\")\n lines.append(\"\")\n for item in items[:5]: # 每个类别最多显示 5 个\n lines.append(f\"- `{item['file']}:{item['line']}`\")\n lines.append(f\" - {item['message']}\")\n lines.append(f\" - 代码: `{item['code']}`\")\n if len(items) > 5:\n lines.append(f\" - ... 还有 {len(items) - 5} 个\")\n lines.append(\"\")\n\n # 依赖分析\n deps = analysis['dependencies']\n if deps['high_risk_dependencies']:\n lines.append(\"## 敏感依赖\")\n lines.append(\"\")\n for dep in deps['high_risk_dependencies']:\n lines.append(f\"- **{dep['name']}** ({dep['manager']}): {dep['risk']}\")\n lines.append(\"\")\n\n # 外部 URL\n network = analysis['network']\n if network['external_urls']:\n lines.append(\"## 外部网络请求\")\n lines.append(\"\")\n lines.append(\"| URL | 文件 |\")\n lines.append(\"|:----|:-----|\")\n for item in network['external_urls'][:10]:\n lines.append(f\"| `{item['url'][:50]}` | `{item['file']}` |\")\n if len(network['external_urls']) > 10:\n lines.append(f\"| ... | 还有 {len(network['external_urls']) - 10} 个 |\")\n lines.append(\"\")\n\n # 提示词安全分析\n prompt_sec = analysis.get('prompt_security', {})\n if prompt_sec.get('findings'):\n lines.append(\"## 提示词安全分析\")\n lines.append(\"\")\n lines.append(f\"> 分析了 {prompt_sec['files_analyzed']} 个 Markdown 文件\")\n lines.append(\"\")\n\n # 按类别分组\n by_category = defaultdict(list)\n for f in prompt_sec['findings']:\n by_category[f['category']].append(f)\n\n # 类别中文名映射\n category_names = {\n 'prompt_injection': '提示注入',\n 'data_collection_instruction': '数据收集指令',\n 'execution_instruction': '执行指令',\n 'privilege_escalation_instruction': '权限提升指令',\n 'deceptive_description': '欺骗性描述',\n 'network_instruction': '网络通信指令',\n 'persistence_instruction': '持久化指令',\n 'hidden_instruction': '隐藏指令',\n 'social_engineering': '社会工程学',\n }\n\n for category, items in by_category.items():\n cat_name = category_names.get(category, category)\n severity = items[0]['severity']\n emoji = {'critical': '🔴', 'high': '🟠', 'medium': '🟡', 'low': '🟢'}.get(severity, '⚪')\n lines.append(f\"### {emoji} {cat_name} ({len(items)})\")\n lines.append(\"\")\n lines.append(f\"*{items[0]['message']}*\")\n lines.append(\"\")\n for item in items[:5]:\n lines.append(f\"- `{item['file']}:{item['line']}`\")\n lines.append(f\" - 匹配: `{item['matched_text']}`\")\n if len(items) > 5:\n lines.append(f\"- ... 还有 {len(items) - 5} 个\")\n lines.append(\"\")\n\n if prompt_sec.get('warnings'):\n lines.append(\"**警告:**\")\n for warning in prompt_sec['warnings']:\n lines.append(f\"- {warning}\")\n lines.append(\"\")\n\n # 安全建议\n lines.append(\"## 安全建议\")\n lines.append(\"\")\n\n if summary['overall_risk'] == 'critical':\n lines.append(\"### 🔴 高危警告\")\n lines.append(\"\")\n lines.append(\"- **强烈建议**:不要在生产环境运行此 skill\")\n lines.append(\"- 需要进行完整的代码审计后才能使用\")\n lines.append(\"- 考虑在沙箱环境中隔离运行\")\n lines.append(\"\")\n elif summary['overall_risk'] == 'high':\n lines.append(\"### 🟠 警告\")\n lines.append(\"\")\n lines.append(\"- 建议在代码审计后使用\")\n lines.append(\"- 注意检查上述发现的安全问题\")\n lines.append(\"- 考虑限制 skill 的访问权限\")\n lines.append(\"\")\n elif summary['overall_risk'] == 'medium':\n lines.append(\"### 🟡 注意\")\n lines.append(\"\")\n lines.append(\"- 使用前请检查上述发现项\")\n lines.append(\"- 建议在测试环境先验证\")\n lines.append(\"\")\n else:\n lines.append(\"### ✅ 低风险\")\n lines.append(\"\")\n lines.append(\"- 未发现明显安全风险\")\n lines.append(\"- 建议定期进行安全检查\")\n lines.append(\"\")\n\n return '\\n'.join(lines)\n\n\ndef main():\n \"\"\"测试入口\"\"\"\n import sys\n\n if len(sys.argv) \u003c 2:\n print(\"用法: python -m scripts.security \u003c仓库路径>\")\n sys.exit(1)\n\n repo_path = sys.argv[1]\n analyzer = SecurityAnalyzer(repo_path)\n print(analyzer.generate_report())\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":46613,"content_sha256":"1def2db70242c6c4769608c460244c5cd2008ec6f65f24d2000be01a3ca8a471"},{"filename":"scripts/target.sh","content":"#!/bin/bash\n\n# Shared target resolution for Skill Manager scripts.\n# Returns the agent config directory that owns skills/ and commands/.\n\nis_agent_config_dir_name() {\n case \"$1\" in\n .codex|.claude|.openclaw|.agents|.agent)\n return 0\n ;;\n *)\n return 1\n ;;\n esac\n}\n\ncanonical_dir() {\n local dir=\"$1\"\n if [ -d \"$dir\" ]; then\n (cd \"$dir\" 2>/dev/null && pwd) || printf '%s\\n' \"$dir\"\n else\n printf '%s\\n' \"$dir\"\n fi\n}\n\nfind_agent_config_dir() {\n local start_dir=\"${1:-$PWD}\"\n local fallback_dir=\"${2:-$PWD/.claude}\"\n local current\n local home_dir=\"${HOME:-/Users/${USER}}\"\n local max_iterations=20\n local iteration=0\n\n if [ -n \"${SKILL_MANAGER_TARGET_DIR:-}\" ]; then\n printf '%s\\n' \"$SKILL_MANAGER_TARGET_DIR\"\n return 0\n fi\n\n current=\"$(canonical_dir \"$start_dir\")\"\n\n # Global config roots: support calls from ~/.codex, ~/.claude and ~/.openclaw.\n for config_name in .codex .claude .openclaw .agents .agent; do\n case \"$current\" in\n \"$home_dir/$config_name\"|\"$home_dir/$config_name\"/*)\n printf '%s\\n' \"$home_dir/$config_name\"\n return 0\n ;;\n esac\n done\n\n while [ \"$iteration\" -lt \"$max_iterations\" ]; do\n local current_name\n current_name=\"$(basename \"$current\")\"\n\n if is_agent_config_dir_name \"$current_name\"; then\n printf '%s\\n' \"$current\"\n return 0\n fi\n\n # Project-local config directories. Prefer Codex when multiple configs coexist.\n for config_name in .codex .claude .openclaw .agents .agent; do\n if [ -d \"$current/$config_name\" ]; then\n printf '%s\\n' \"$current/$config_name\"\n return 0\n fi\n done\n\n local parent\n parent=\"$(dirname \"$current\")\"\n\n if [ \"$parent\" = \"$current\" ]; then\n break\n fi\n\n local parent_name\n parent_name=\"$(basename \"$parent\")\"\n\n if [ \"$parent_name\" = \"skills\" ] || [ \"$parent_name\" = \"commands\" ]; then\n local grandparent\n grandparent=\"$(dirname \"$parent\")\"\n local grandparent_name\n grandparent_name=\"$(basename \"$grandparent\")\"\n if is_agent_config_dir_name \"$grandparent_name\"; then\n printf '%s\\n' \"$grandparent\"\n return 0\n fi\n fi\n\n current=\"$parent\"\n iteration=$((iteration + 1))\n done\n\n printf '%s\\n' \"$fallback_dir\"\n}\n\n# Find ALL agent config directories in the project.\n# Returns newline-separated paths (one per line).\n# - SKILL_MANAGER_TARGET_DIR set → returns that single dir\n# - Called from global config root (~/codex etc.) → returns that single dir\n# - Project directory → walks up to project root, returns all .codex/.claude/.openclaw found\n# - Not found → returns fallback_dir\nfind_all_agent_config_dirs() {\n local start_dir=\"${1:-$PWD}\"\n local fallback_dir=\"${2:-$PWD/.claude}\"\n local current\n local home_dir=\"${HOME:-/Users/${USER}}\"\n local max_iterations=20\n local iteration=0\n\n # Explicit override → single target\n if [ -n \"${SKILL_MANAGER_TARGET_DIR:-}\" ]; then\n printf '%s\\n' \"$SKILL_MANAGER_TARGET_DIR\"\n return 0\n fi\n\n current=\"$(canonical_dir \"$start_dir\")\"\n\n # Global config roots → single target (no multi-dir in global scope)\n for config_name in .codex .claude .openclaw .agents .agent; do\n case \"$current\" in\n \"$home_dir/$config_name\"|\"$home_dir/$config_name\"/*)\n printf '%s\\n' \"$home_dir/$config_name\"\n return 0\n ;;\n esac\n done\n\n # Walk up to find project root\n while [ \"$iteration\" -lt \"$max_iterations\" ]; do\n local current_name\n current_name=\"$(basename \"$current\")\"\n\n # If inside an agent config dir, step out to project root\n if is_agent_config_dir_name \"$current_name\"; then\n current=\"$(dirname \"$current\")\"\n elif [ \"$current_name\" = \"skills\" ] || [ \"$current_name\" = \"commands\" ]; then\n local parent\n parent=\"$(dirname \"$current\")\"\n local parent_name\n parent_name=\"$(basename \"$parent\")\"\n if is_agent_config_dir_name \"$parent_name\"; then\n current=\"$(dirname \"$parent\")\"\n fi\n fi\n\n # Check for agent config dirs at this level\n local found=0\n for config_name in .codex .claude .openclaw .agents .agent; do\n if [ -d \"$current/$config_name\" ]; then\n printf '%s\\n' \"$current/$config_name\"\n found=1\n fi\n done\n\n if [ \"$found\" -eq 1 ]; then\n return 0\n fi\n\n local parent\n parent=\"$(dirname \"$current\")\"\n\n if [ \"$parent\" = \"$current\" ]; then\n break\n fi\n\n current=\"$parent\"\n iteration=$((iteration + 1))\n done\n\n printf '%s\\n' \"$fallback_dir\"\n}\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":5042,"content_sha256":"2813630712db7da8e5fc45f470774890867f91c9d5400944de1a5116ddb13ea3"},{"filename":"scripts/update.sh","content":"#!/bin/bash\n\n# Skill & Command Manager - Update Script\n# 更新已安装的 git 克隆的 skills\n# 注意:符号链接的 skills/commands 会自动与源同步,无需更新\n\nITEM_NAME=\"$1\"\nORIGINAL_PWD=\"$PWD\"\n# 获取脚本所在目录\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nMANAGER_DIR=\"$(dirname \"$SCRIPT_DIR\")\"\nTARGET_HELPER=\"$SCRIPT_DIR/target.sh\"\n\n# 更新记录函数\nrecord_update() {\n local skill_name=\"$1\"\n local old_version=\"$2\"\n local new_version=\"$3\"\n \n if command -v python3 &> /dev/null; then\n RECORD_SCRIPT=\"$SCRIPT_DIR/record.py\"\n if [ -f \"$RECORD_SCRIPT\" ]; then\n python3 \"$RECORD_SCRIPT\" update \"$skill_name\" --from \"$old_version\" --to \"$new_version\" 2>/dev/null || true\n fi\n fi\n}\n\nif [ -f \"$TARGET_HELPER\" ]; then\n # shellcheck source=target.sh\n source \"$TARGET_HELPER\"\nelse\n echo \"❌ 错误: 找不到目标目录识别模块: $TARGET_HELPER\"\n exit 1\nfi\n\nSCRIPT_AGENT_DIR=\"$(find_agent_config_dir \"$MANAGER_DIR\" \"$PWD/.claude\")\"\nAGENT_DIR=\"$(find_agent_config_dir \"$ORIGINAL_PWD\" \"$SCRIPT_AGENT_DIR\")\"\nSKILLS_DIR=\"$AGENT_DIR/skills\"\n\nif [ ! -d \"$SKILLS_DIR\" ]; then\n echo \"❌ 错误: $SKILLS_DIR 目录不存在\"\n exit 1\nfi\n\nupdate_skill() {\n local skill_path=\"$1\"\n local skill_name=$(basename \"$skill_path\")\n\n # 只更新 git 克隆的 skills\n if [ -d \"$skill_path/.git\" ]; then\n echo \"▶ 更新: $skill_name\"\n\n cd \"$skill_path\"\n \n # 记录更新前的版本\n local old_version=$(grep -oP 'version:\\s*[\"\\']?\\K[\\d.]+' SKILL.md 2>/dev/null || echo \"\")\n \n git fetch -q origin 2>/dev/null || {\n echo \" ❌ 无法获取更新\"\n cd - > /dev/null\n echo \"\"\n return\n }\n local local_rev=$(git rev-parse HEAD)\n local remote_rev=$(git rev-parse @{u} 2>/dev/null)\n\n if [ \"$local_rev\" != \"$remote_rev\" ] && [ -n \"$remote_rev\" ]; then\n git pull -q\n \n # 获取更新后的版本\n local new_version=$(grep -oP 'version:\\s*[\"\\']?\\K[\\d.]+' SKILL.md 2>/dev/null || echo \"\")\n \n echo \" ✓ 已更新 ($old_version → $new_version)\"\n \n # 记录更新\n record_update \"$skill_name\" \"$old_version\" \"$new_version\"\n else\n echo \" ○ 已是最新\"\n fi\n\n cd - > /dev/null\n echo \"\"\n fi\n}\n\nif [ -z \"$ITEM_NAME\" ]; then\n # 更新所有 git 克隆的 skills\n echo \"🔄 更新所有 Git 克隆的 skills...\"\n echo \"\"\n echo \"注意: 符号链接的 skills/commands 会自动与源同步,无需手动更新\"\n echo \"\"\n\n count=0\n for item in \"$SKILLS_DIR\"/*; do\n if [ -d \"$item/.git\" ]; then\n update_skill \"$item\"\n ((count++))\n fi\n done\n\n if [ \"$count\" -eq 0 ]; then\n echo \"没有需要更新的 skills\"\n else\n echo \"✓ 更新完成,共检查 $count 个 skills\"\n fi\nelse\n # 更新指定 skill\n TARGET_PATH=\"$SKILLS_DIR/$ITEM_NAME\"\n\n if [ ! -e \"$TARGET_PATH\" ]; then\n echo \"❌ 错误: Skill '$ITEM_NAME' 不存在\"\n exit 1\n fi\n\n if [ -L \"$TARGET_PATH\" ]; then\n echo \"ℹ '$ITEM_NAME' 是符号链接,会自动与源同步,无需手动更新\"\n echo \" 指向: $(readlink \"$TARGET_PATH\")\"\n exit 0\n fi\n\n if [ ! -d \"$TARGET_PATH/.git\" ]; then\n echo \"❌ 错误: '$ITEM_NAME' 不是 Git 克隆的 skill,无法更新\"\n exit 1\n fi\n\n update_skill \"$TARGET_PATH\"\n echo \"✓ 更新完成\"\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3612,"content_sha256":"5b72008c70c6c8340acdb0c58d130fb0473be59fa9046a76fe93a88060e19089"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Skill Manager","type":"text"}]},{"type":"paragraph","content":[{"text":"管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"前置条件","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Git 已安装(用于 GitHub 克隆)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"有写入目标 Agent 配置目录的权限,例如 ","type":"text"},{"text":".codex/skills/","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":".claude/skills/","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":".openclaw/skills/","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"安装行为","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"本地路径 (Skill)","type":"text","marks":[{"type":"strong"}]},{"text":" → 符号链接(保持与源同步)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"本地路径 (Command)","type":"text","marks":[{"type":"strong"}]},{"text":" → 符号链接(保持与源同步)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"本地集合目录","type":"text","marks":[{"type":"strong"}]},{"text":" → 批量符号链接","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GitHub 仓库/子目录","type":"text","marks":[{"type":"strong"}]},{"text":" → 克隆后删除 .git(静态复制)+ 自动安全检查","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"目标目录识别","type":"text"}]},{"type":"paragraph","content":[{"text":"执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"在 ","type":"text"},{"text":"/Users/maoking/.codex","type":"text","marks":[{"type":"code_inline"}]},{"text":" 或其子目录调用时,目标为 ","type":"text"},{"text":"/Users/maoking/.codex/skills/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"在项目根目录包含 ","type":"text"},{"text":".codex/","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":".claude/","type":"text","marks":[{"type":"code_inline"}]},{"text":" 或 ","type":"text"},{"text":".openclaw/","type":"text","marks":[{"type":"code_inline"}]},{"text":" 时,目标为对应配置目录下的 ","type":"text"},{"text":"skills/","type":"text","marks":[{"type":"code_inline"}]},{"text":" 或 ","type":"text"},{"text":"commands/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"在 ","type":"text"},{"text":".codex/skills/","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":".claude/skills/","type":"text","marks":[{"type":"code_inline"}]},{"text":"、","type":"text"},{"text":".openclaw/skills/","type":"text","marks":[{"type":"code_inline"}]},{"text":" 内调用时,目标为其上级配置目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"如需显式指定目标根目录,可使用 ","type":"text"},{"text":"--target","type":"text","marks":[{"type":"code_inline"}]},{"text":" 参数或设置 ","type":"text"},{"text":"SKILL_MANAGER_TARGET_DIR=/path/to/.codex","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"从全局配置目录(如 ","type":"text"},{"text":"~/.claude","type":"text","marks":[{"type":"code_inline"}]},{"text":")调用时,会尝试通过 git 自动发现项目本地目录,并打印告警","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"支持的来源类型","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"本地路径(符号链接)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 单个 skill 目录\nskill-manager install ~/skills/pdf-tool\n\n# 单个 command 文件\nskill-manager install ~/commands/deepresearch.md\n\n# 包含多个 skills 的目录(批量安装)\nskill-manager install ~/skills/external-skills/\n\n# 包含多个 commands 的目录(批量安装)\nskill-manager install ~/commands/","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"GitHub 仓库根目录(克隆,删除 .git)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"skill-manager install https://github.com/owner/skill-repo\nskill-manager install owner/skill-repo","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"GitHub 子目录(稀疏克隆,删除 .git)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 完整 URL 到子目录\nskill-manager install https://github.com/jgtolentino/insightpulse-odoo/tree/main/docs/claude-code-skills/community\n\n# 简写格式:owner/repo/branch/path/to/skills-directory\nskill-manager install jgtolentino/insightpulse-odoo/main/docs/claude-code-skills/community","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"工作流程","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"安装","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"检测来源类型","type":"text","marks":[{"type":"strong"}]},{"text":" - 自动识别本地路径、GitHub 仓库或子目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"检测 Item 类型","type":"text","marks":[{"type":"strong"}]},{"text":" - 自动识别是 Skill(目录)还是 Command(.md 文件)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"检测是否为集合目录","type":"text","marks":[{"type":"strong"}]},{"text":" - 检查目录是否包含多个 items","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"批量处理模式","type":"text","marks":[{"type":"strong"}]},{"text":" - 如果是集合目录,遍历所有 items 并分别安装","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"本地来源","type":"text","marks":[{"type":"strong"}]},{"text":" - 创建符号链接,保持与源同步更新","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GitHub 仓库根","type":"text","marks":[{"type":"strong"}]},{"text":" - 使用 ","type":"text"},{"text":"git clone --depth 1","type":"text","marks":[{"type":"code_inline"}]},{"text":" 浅克隆","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GitHub 子目录","type":"text","marks":[{"type":"strong"}]},{"text":" - 使用稀疏克隆(sparse checkout)仅获取指定目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"冲突处理","type":"text","marks":[{"type":"strong"}]},{"text":" - 已存在时先备份为 ","type":"text"},{"text":".backup","type":"text","marks":[{"type":"code_inline"}]},{"text":",然后安装新版本","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"安装命令","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 使用脚本安装\nscripts/install.sh [--target \u003cdir>] \u003csource>\n\n# 示例\nscripts/install.sh ~/dev/my-skills/pdf-tool\nscripts/install.sh ~/dev/my-commands/deepresearch.md\nscripts/install.sh ~/dev/my-skills/\nscripts/install.sh ~/dev/my-commands/\nscripts/install.sh https://github.com/anthropics/claude-code\nscripts/install.sh jgtolentino/insightpulse-odoo/main/docs/claude-code-skills/community\n\n# 显式指定目标(从非项目目录调用时使用)\nscripts/install.sh --target /path/to/project/.claude ~/dev/my-skills/pdf-tool","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"列出已安装 Items","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"scripts/list.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"显示当前识别到的 Agent 配置目录下所有已安装的 items 及其类型(符号链接或克隆)。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"卸载","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"scripts/remove.sh \u003cname>","type":"text"}]},{"type":"paragraph","content":[{"text":"删除指定的 skill 或 command(自动识别类型)。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"更新","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"scripts/update.sh [name]","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"不指定参数:更新所有通过 git 克隆的 skills","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"指定名称:更新指定的 skill","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"注意","type":"text","marks":[{"type":"strong"}]},{"text":":符号链接的 items 会自动与源同步,无需手动更新","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"检查更新","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"scripts/check.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"检查所有远程安装 Skills 的更新状态,检测策略:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"有版本号","type":"text","marks":[{"type":"strong"}]},{"text":" → 直接比较本地与远程版本号","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"无版本号","type":"text","marks":[{"type":"strong"}]},{"text":" → 检查远程仓库最近 Commits,与安装时间对比","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"子目录安装","type":"text","marks":[{"type":"strong"}]},{"text":" → 精确检查 Skill 所在子目录的 Commits","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"显示:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"📦 有可用更新的 Skills","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ 已是最新版本的 Skills","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"⚠️ 检查失败的 Skills(无来源信息等)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"每次安装和更新都会自动记录到 ","type":"text"},{"text":"assets/skill-registry.json","type":"text","marks":[{"type":"code_inline"}]},{"text":"。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"查看已安装记录","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 scripts/record.py list","type":"text"}]},{"type":"paragraph","content":[{"text":"显示所有已安装 Skills 的详细记录,包括:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"安装时间","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"来源 URL","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"当前版本","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"描述信息","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"识别规则","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Skill 目录规则","type":"text"}]},{"type":"paragraph","content":[{"text":"一个目录被视为有效的 skill 目录,如果它包含:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 文件(标准 skill)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"或 ","type":"text"},{"text":"skill.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 文件(变体)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"或 ","type":"text"},{"text":".codex","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":".claude","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":".openclaw","type":"text","marks":[{"type":"code_inline"}]},{"text":" 子目录","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Command 文件规则","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"文件扩展名为 ","type":"text"},{"text":".md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"集合目录规则","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Skills 集合","type":"text","marks":[{"type":"strong"}]},{"text":":包含多个 skill 子目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Commands 集合","type":"text","marks":[{"type":"strong"}]},{"text":":包含多个 ","type":"text"},{"text":".md","type":"text","marks":[{"type":"code_inline"}]},{"text":" 文件","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"使用示例","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# ========== 安装 ==========\n# 安装本地单个 skill\nskill-manager install ~/dev/my-skills/pdf-tool\n\n# 批量安装本地目录下的所有 skills\nskill-manager install ~/dev/my-skills/\nskill-manager install ../other-project/.claude/skills/\n\n# 在 Codex 全局目录中调用时,安装到 ~/.codex/skills/\ncd /Users/maoking/.codex\nskill-manager install ~/dev/my-skills/pdf-tool\n\n# 从全局目录调用但安装到指定项目(使用 --target 避免装错位置)\nskill-manager install --target /path/to/project/.claude ~/dev/my-skills/pdf-tool\n\n# 从 GitHub 仓库根目录安装\nskill-manager install https://github.com/anthropics/claude-code\nskill-manager install anthropics/claude-code\n\n# 从 GitHub 子目录安装\nskill-manager install https://github.com/jgtolentino/insightpulse-odoo/tree/main/docs/claude-code-skills/community\nskill-manager install jgtolentino/insightpulse-odoo/main/docs/claude-code-skills/community\n\n# ========== 查看与管理 ==========\n# 列出已安装的 skills\nskill-manager list\n\n# 卸载 skill\nskill-manager remove pdf-tool\n\n# ========== 更新与检查 ==========\n# 检查所有 skills 的更新\nskill-manager check\n\n# 更新所有 git 克隆的 skills\nskill-manager update\n\n# 更新指定 skill\nskill-manager update claude-code\n\n# 查看安装记录\npython3 scripts/record.py list","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"安全检查","type":"text"}]},{"type":"paragraph","content":[{"text":"从 GitHub 安装 skill 时,会自动进行安全检查(本地安装不检查)。","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"检测内容","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"类别","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"说明","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"危险代码模式","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"命令执行、敏感文件访问、数据外泄、代码混淆、权限提升等","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill 特有风险","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"安装钩子、MCP 服务器配置等","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"提示词安全","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"提示注入、数据收集指令、执行指令、欺骗性描述等","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"硬编码凭证","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"API Key、Token、密码等敏感信息","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"风险等级","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🔴 ","type":"text"},{"text":"CRITICAL","type":"text","marks":[{"type":"strong"}]},{"text":" - 极高风险,强烈建议不要使用","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🟠 ","type":"text"},{"text":"HIGH","type":"text","marks":[{"type":"strong"}]},{"text":" - 高风险,需审计后使用","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🟡 ","type":"text"},{"text":"MEDIUM","type":"text","marks":[{"type":"strong"}]},{"text":" - 中等风险,使用前请检查","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🟢 ","type":"text"},{"text":"LOW","type":"text","marks":[{"type":"strong"}]},{"text":" - 低风险,建议定期检查","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ ","type":"text"},{"text":"NONE","type":"text","marks":[{"type":"strong"}]},{"text":" - 未发现明显风险","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"注意事项","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"安全检查需要 Python 3 环境,无 Python 时静默跳过","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"检查发现问题不会阻止安装,仅输出警告报告","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"建议在安装外部 skill 后仔细阅读安全报告","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"注册表 Schema","type":"text"}]},{"type":"paragraph","content":[{"text":"每个已安装的 Skill 记录在 ","type":"text"},{"text":"assets/skill-registry.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" 中,包含以下字段:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"字段","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"说明","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"name","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill 目录名","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"source","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"原始安装来源(本地路径或 GitHub URL)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"install_type","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"local\"","type":"text","marks":[{"type":"code_inline"}]},{"text":"(符号链接)或 ","type":"text"},{"text":"\"remote\"","type":"text","marks":[{"type":"code_inline"}]},{"text":"(GitHub 克隆)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"installed_at","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"初始安装时间(ISO 8601)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"last_updated","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"最后版本更新时间","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"last_check_at","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"最后一次更新检查时间(仅远程)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"installed_version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"安装时的版本号","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"current_version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"当前已安装版本","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"latest_version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"远程最新版本","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"install_commit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"安装时的 Git commit hash(仅远程)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"install_branch","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"安装时使用的 Git branch(仅远程)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"remote_url","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"完整 GitHub URL,含子目录路径(仅远程)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"remote_subpath","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill 在仓库中的子路径(仅子目录安装)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"description","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill 描述","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"homepage","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"主页 URL","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"目录结构","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"skill-manager/\n├── SKILL.md # 本文件\n├── CHANGELOG.md # 变更日志\n├── CLAUDE.md # AI 开发助手说明\n├── LICENSE.txt # 许可证\n├── scripts/\n│ ├── install.sh # 安装脚本\n│ ├── list.sh # 列表脚本\n│ ├── remove.sh # 卸载脚本\n│ ├── update.sh # 更新脚本\n│ ├── check.sh # 更新检查脚本\n│ ├── auto-check.sh # 定期自动检查触发器\n│ ├── target.sh # Agent 配置目录识别模块\n│ ├── record.py # 记录管理模块\n│ └── security.py # 安全检查模块\n└── assets/ # 资源文件\n ├── skill-registry.json # Skill 安装记录(运行时生成)\n └── skill-registry.example.json # 注册表示例","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"skill-manager","author":"@skillopedia","source":{"stars":281,"repo_name":"legal-skills","origin_url":"https://github.com/cat-xierluo/legal-skills/blob/HEAD/skills/skill-manager/SKILL.md","repo_owner":"cat-xierluo","body_sha256":"98f550e0e58d3927d8c03836e792a6f3173ed31d95b2178666ec2444abcaf4cf","cluster_key":"f7e3c6422c0cfbcfeca366b7e5a4aba85d664e3597ec11fb4fc3b467c7929915","clean_bundle":{"format":"clean-skill-bundle-v1","source":"cat-xierluo/legal-skills/skills/skill-manager/SKILL.md","attachments":[{"id":"ff34f0cc-c315-5017-9e86-0c47f5f0dc46","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ff34f0cc-c315-5017-9e86-0c47f5f0dc46/attachment.md","path":"CHANGELOG.md","size":5823,"sha256":"43fc3fb1632deec88d9555c47d447f767d6fe91d8dfb03b24572d30a95045ecb","contentType":"text/markdown; charset=utf-8"},{"id":"74e8ca84-3ee3-5ebc-aa6a-0354dbedf796","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/74e8ca84-3ee3-5ebc-aa6a-0354dbedf796/attachment.json","path":"assets/skill-registry.example.json","size":1166,"sha256":"5a7edd3848dcc1960c49827dfb594fb6b7588e2194c77e36d0168864ec01ce47","contentType":"application/json; charset=utf-8"},{"id":"d0bd3faa-943e-5ecc-8f9e-3a3657d6c97d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d0bd3faa-943e-5ecc-8f9e-3a3657d6c97d/attachment.sh","path":"scripts/auto-check.sh","size":478,"sha256":"e48e87a316f03a326865c1c60f00c5d2e30e58f7d02f1a04e37833687f39a7f7","contentType":"application/x-sh; charset=utf-8"},{"id":"820c1915-1695-5012-b4cb-e627720bf14e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/820c1915-1695-5012-b4cb-e627720bf14e/attachment.sh","path":"scripts/check.sh","size":607,"sha256":"b53b47acd4c0cebfdb455a6696fe4b3a2186ddf4a65067c79352458bdfff6670","contentType":"application/x-sh; charset=utf-8"},{"id":"ef44fa77-42c7-561f-8a56-00ada5fde418","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ef44fa77-42c7-561f-8a56-00ada5fde418/attachment.sh","path":"scripts/install.sh","size":19943,"sha256":"db691855aa70036743cb5f17a0c801291320cc99226b54cafef53d9e31bd4482","contentType":"application/x-sh; charset=utf-8"},{"id":"8a108c84-a774-5a4a-a503-744e8c57daba","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8a108c84-a774-5a4a-a503-744e8c57daba/attachment.sh","path":"scripts/list.sh","size":3079,"sha256":"00e3d7d30d846c0aeeef8fc60b8c0799ce6ab1dcfc0690053a6834e155fd8ec5","contentType":"application/x-sh; charset=utf-8"},{"id":"3954afbe-5f19-5369-984d-4323d11c69bd","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3954afbe-5f19-5369-984d-4323d11c69bd/attachment.py","path":"scripts/record.py","size":31733,"sha256":"07c2b349f679c616c21e2f74cc2a3be05cfb061be24717651f4ab847eaf948e5","contentType":"text/x-python; charset=utf-8"},{"id":"7106c440-d23e-5105-b7c7-8b6c34ac047f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7106c440-d23e-5105-b7c7-8b6c34ac047f/attachment.sh","path":"scripts/remove.sh","size":2525,"sha256":"904ca59535397cde3feac44743bb18dbe3e165602b72945d48a208d25fb7ec0d","contentType":"application/x-sh; charset=utf-8"},{"id":"ac4dfdf0-a20e-5183-9a91-a964363033aa","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ac4dfdf0-a20e-5183-9a91-a964363033aa/attachment.py","path":"scripts/security.py","size":46613,"sha256":"1def2db70242c6c4769608c460244c5cd2008ec6f65f24d2000be01a3ca8a471","contentType":"text/x-python; charset=utf-8"},{"id":"1d680ece-6c6d-55f6-b93f-e7728851571f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1d680ece-6c6d-55f6-b93f-e7728851571f/attachment.sh","path":"scripts/target.sh","size":5042,"sha256":"2813630712db7da8e5fc45f470774890867f91c9d5400944de1a5116ddb13ea3","contentType":"application/x-sh; charset=utf-8"},{"id":"c4ba9f44-494e-57bb-8e0c-0b9adf2f38fa","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c4ba9f44-494e-57bb-8e0c-0b9adf2f38fa/attachment.sh","path":"scripts/update.sh","size":3612,"sha256":"5b72008c70c6c8340acdb0c58d130fb0473be59fa9046a76fe93a88060e19089","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"31dfea777ea2ecabf4cd2b163e351e579cfd8f9f2c8fdc1d9b939324d81ce8f4","attachment_count":11,"text_attachments":4,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":7,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/skill-manager/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"license":"Complete terms in LICENSE.txt","version":"v1","category":"integrations-apis","homepage":"https://github.com/cat-xierluo/legal-skills","import_tag":"clean-skills-v1","description":"管理 Claude Code、Codex 和 OpenClaw Skills 的安装、版本追踪和更新检查。支持从本地路径或 GitHub 仓库安装,自动识别 .codex/.claude/.openclaw 目标目录,记录每个 Skill 的安装时间、来源 URL 和版本号,并检查 GitHub 更新。"}},"renderedAt":1782982800821}

Skill Manager 管理 Claude Code、Codex 和 OpenClaw Skills/Commands 的安装、同步、卸载和列表查看。 前置条件 - Git 已安装(用于 GitHub 克隆) - 有写入目标 Agent 配置目录的权限,例如 、 、 安装行为 - 本地路径 (Skill) → 符号链接(保持与源同步) - 本地路径 (Command) → 符号链接(保持与源同步) - 本地集合目录 → 批量符号链接 - GitHub 仓库/子目录 → 克隆后删除 .git(静态复制)+ 自动安全检查 目标目录识别 执行安装、列表、卸载、更新时,脚本会从调用目录向上查找 Agent 配置目录: - 在 或其子目录调用时,目标为 - 在项目根目录包含 、 或 时,目标为对应配置目录下的 或 - 在 、 、 内调用时,目标为其上级配置目录 - 如需显式指定目标根目录,可使用 参数或设置 - 从全局配置目录(如 )调用时,会尝试通过 git 自动发现项目本地目录,并打印告警 支持的来源类型 本地路径(符号链接) GitHub 仓库根目录(克隆,删除 .git) GitHub 子目录(稀疏克隆,删除 .git) 工作流程 安装 1. 检测来源类型 - 自动识别本地路径、GitHub 仓库或子目录 2. 检测 Item 类型 - 自动识别是 Skill(目录)还是 Co…