Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:24:37 +08:00
commit 8cd5c7679d
61 changed files with 6788 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
"""
External Tool Detector - 检测系统已安装的外部CLI工具
"""
import yaml
import shutil
from pathlib import Path
from typing import List, Optional, Dict, Any
from .base import ToolDetector
from ..models import ExternalTool, ToolMetadata
class ExternalToolDetector(ToolDetector):
"""外部工具检测器
从external/目录扫描.meta.yml文件来发现外部工具配置
"""
def __init__(self, root_path: Path):
super().__init__(root_path)
self._external_dir = root_path / "external"
def detect(self) -> List[ExternalTool]:
"""
扫描external/目录检测外部工具
Returns:
List[ExternalTool]: 检测到的外部工具列表
"""
self._tools = []
# 扫描external目录下的所有.meta.yml文件
if self._external_dir.exists():
for meta_file in self._external_dir.rglob("*.meta.yml"):
tool = self._parse_meta_file(meta_file)
if tool:
self._tools.append(tool)
return self._tools
def _parse_meta_file(self, meta_file: Path) -> Optional[ExternalTool]:
"""解析外部工具的meta.yml文件"""
try:
content = yaml.safe_load(meta_file.read_text(encoding='utf-8'))
if not content:
return None
# 获取基本信息
basic_info = content.get("基本信息", {})
tool_type = basic_info.get("类型", "external")
# 只处理external类型的工具
if tool_type != "external":
return None
command = basic_info.get("命令", "")
if not command:
return None
# 检测是否已安装
command_name = command.split()[0]
is_installed = shutil.which(command_name) is not None
tool_path = shutil.which(command_name)
# 创建metadata
metadata = ToolMetadata(
tool_id=content.get("tool_id", "unknown"),
tool_name=content.get("tool_name", "未命名工具"),
description=content.get("功能描述", {}).get("简介", "")
)
# 获取功能描述
func_desc = content.get("功能描述", {})
# 获取快速开始信息
quick_start = content.get("快速开始", {})
return ExternalTool(
metadata=metadata,
command=command,
category=basic_info.get("类别", "unknown"),
use_cases=content.get("使用场景", []),
install_guide=quick_start.get("安装", ""),
installed=is_installed,
path=tool_path
)
except Exception as e:
# 静默失败单个工具的检测
return None
def refresh(self):
"""刷新工具列表"""
# 重新扫描external目录
super().refresh()
def find_tool(self, name_or_id: str) -> Optional[ExternalTool]:
"""
查找外部工具
Args:
name_or_id: 工具名称或ID
Returns:
ExternalTool: 找到的工具如果未找到返回None
"""
# 先尝试精确匹配
for tool in self._tools:
if tool.tool_id == name_or_id or tool.tool_name == name_or_id:
return tool
# 尝试模糊匹配(名称包含)
matches = [t for t in self._tools if name_or_id.lower() in t.tool_name.lower()]
if len(matches) == 1:
return matches[0]
elif len(matches) > 1:
# 不打印,由调用者处理
return None
return None
def get_uninstalled_tools(self) -> List[ExternalTool]:
"""获取未安装的工具列表"""
return [t for t in self._tools if not t.installed]
def get_installed_tools(self) -> List[ExternalTool]:
"""获取已安装的工具列表"""
return [t for t in self._tools if t.installed]