Initial commit
This commit is contained in:
36
skills/toolkit/discover/__init__.py
Normal file
36
skills/toolkit/discover/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Toolkit Discovery Package
|
||||
|
||||
A modular toolkit discovery and management system for ai-runtime.
|
||||
"""
|
||||
|
||||
__version__ = "2.0.0"
|
||||
__author__ = "AI-Runtime Team"
|
||||
|
||||
from .discovery import ToolkitDiscovery
|
||||
from .models import Tool, InternalTool, ExternalTool, ToolMetadata
|
||||
from .detectors import ToolDetector, InternalToolDetector, ExternalToolDetector
|
||||
from .formatters import ToolFormatter, TableFormatter, JsonFormatter
|
||||
from .cli import ToolkitCLI
|
||||
|
||||
__all__ = [
|
||||
# Main classes
|
||||
'ToolkitDiscovery',
|
||||
'ToolkitCLI',
|
||||
|
||||
# Models
|
||||
'Tool',
|
||||
'InternalTool',
|
||||
'ExternalTool',
|
||||
'ToolMetadata',
|
||||
|
||||
# Detectors
|
||||
'ToolDetector',
|
||||
'InternalToolDetector',
|
||||
'ExternalToolDetector',
|
||||
|
||||
# Formatters
|
||||
'ToolFormatter',
|
||||
'TableFormatter',
|
||||
'JsonFormatter',
|
||||
]
|
||||
8
skills/toolkit/discover/__main__.py
Normal file
8
skills/toolkit/discover/__main__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
Entry point for python -m discover
|
||||
"""
|
||||
|
||||
from .cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
230
skills/toolkit/discover/cli.py
Normal file
230
skills/toolkit/discover/cli.py
Normal file
@@ -0,0 +1,230 @@
|
||||
"""
|
||||
CLI Interface for Toolkit Discovery
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from .discovery import ToolkitDiscovery
|
||||
|
||||
|
||||
class ToolkitCLI:
|
||||
"""Toolkit Discovery CLI Interface"""
|
||||
|
||||
def __init__(self, toolkit_root: Path):
|
||||
self.toolkit_root = toolkit_root
|
||||
self.discovery = ToolkitDiscovery(toolkit_root)
|
||||
|
||||
def run(self, args=None):
|
||||
"""运行CLI"""
|
||||
parser = self._create_parser()
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
if not parsed_args.command:
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
try:
|
||||
return self._execute_command(parsed_args)
|
||||
except Exception as e:
|
||||
print(f"❌ 错误: {e}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
def _create_parser(self) -> argparse.ArgumentParser:
|
||||
"""创建参数解析器"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="工具包发现和管理工具",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
示例:
|
||||
python -m discover list # 列出所有工具
|
||||
python -m discover list --lang python # 列出Python工具
|
||||
python -m discover list --external # 仅显示外部工具
|
||||
python -m discover show SERVICE-CHECK-001 # 查看工具详情
|
||||
python -m discover recommend '分析日志' # 推荐工具
|
||||
python -m discover search json # 搜索工具
|
||||
"""
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
||||
|
||||
# list 命令
|
||||
self._add_list_parser(subparsers)
|
||||
|
||||
# show 命令
|
||||
self._add_show_parser(subparsers)
|
||||
|
||||
# run 命令
|
||||
self._add_run_parser(subparsers)
|
||||
|
||||
# recommend 命令
|
||||
self._add_recommend_parser(subparsers)
|
||||
|
||||
# search 命令
|
||||
self._add_search_parser(subparsers)
|
||||
|
||||
return parser
|
||||
|
||||
def _add_list_parser(self, subparsers):
|
||||
"""添加list命令"""
|
||||
list_parser = subparsers.add_parser("list", help="列出所有工具")
|
||||
list_parser.add_argument("--lang", help="按语言过滤 (bash/python/node)")
|
||||
list_parser.add_argument("--purpose", help="按用途过滤 (DATA/CODE/TEST/BUILD/MONITOR/DOC)")
|
||||
list_parser.add_argument("--query", help="按名称或描述搜索")
|
||||
list_parser.add_argument("--json", action="store_true", help="JSON格式输出")
|
||||
list_parser.add_argument("--external", action="store_true", help="仅显示外部工具")
|
||||
list_parser.add_argument("--include-external", action="store_true", help="包含外部工具")
|
||||
|
||||
def _add_show_parser(self, subparsers):
|
||||
"""添加show命令"""
|
||||
show_parser = subparsers.add_parser("show", help="显示工具详情")
|
||||
show_parser.add_argument("tool", help="工具ID或名称")
|
||||
|
||||
def _add_run_parser(self, subparsers):
|
||||
"""添加run命令"""
|
||||
run_parser = subparsers.add_parser("run", help="运行工具")
|
||||
run_parser.add_argument("tool", help="工具ID或名称")
|
||||
run_parser.add_argument("args", nargs=argparse.REMAINDER, help="工具参数")
|
||||
|
||||
def _add_recommend_parser(self, subparsers):
|
||||
"""添加recommend命令"""
|
||||
recommend_parser = subparsers.add_parser("recommend", help="推荐工具")
|
||||
recommend_parser.add_argument("task", help="任务描述")
|
||||
|
||||
def _add_search_parser(self, subparsers):
|
||||
"""添加search命令"""
|
||||
search_parser = subparsers.add_parser("search", help="搜索工具")
|
||||
search_parser.add_argument("keyword", help="搜索关键词")
|
||||
|
||||
def _execute_command(self, args) -> int:
|
||||
"""执行命令"""
|
||||
if args.command == "list":
|
||||
return self._cmd_list(args)
|
||||
elif args.command == "show":
|
||||
return self._cmd_show(args)
|
||||
elif args.command == "run":
|
||||
return self._cmd_run(args)
|
||||
elif args.command == "recommend":
|
||||
return self._cmd_recommend(args)
|
||||
elif args.command == "search":
|
||||
return self._cmd_search(args)
|
||||
|
||||
return 0
|
||||
|
||||
def _cmd_list(self, args) -> int:
|
||||
"""执行list命令"""
|
||||
# 判断输出格式
|
||||
format_type = "json" if args.json else "table"
|
||||
|
||||
# 获取工具列表
|
||||
if args.external:
|
||||
tools = self.discovery.external_tools
|
||||
elif args.include_external:
|
||||
tools = self.discovery.all_tools
|
||||
else:
|
||||
tools = self.discovery.internal_tools
|
||||
|
||||
# 过滤内部工具
|
||||
if not args.external:
|
||||
tools = self.discovery.filter_tools(
|
||||
lang=args.lang,
|
||||
purpose=args.purpose,
|
||||
query=args.query
|
||||
)
|
||||
|
||||
# 输出
|
||||
output = self.discovery.format_tools(tools, format_type=format_type)
|
||||
print(output)
|
||||
return 0
|
||||
|
||||
def _cmd_show(self, args) -> int:
|
||||
"""执行show命令"""
|
||||
tool = self.discovery.find_tool(args.tool)
|
||||
if not tool:
|
||||
print(f"❌ 未找到工具: {args.tool}")
|
||||
return 1
|
||||
|
||||
print(self.discovery.format_tool(tool))
|
||||
return 0
|
||||
|
||||
def _cmd_run(self, args) -> int:
|
||||
"""执行run命令"""
|
||||
# 注意: 这里简化处理,实际应该直接从文件执行
|
||||
# 为了保持向后兼容,这里直接调用工具文件
|
||||
import subprocess
|
||||
|
||||
tool = self.discovery.find_tool(args.tool)
|
||||
if not tool:
|
||||
print(f"❌ 未找到工具: {args.tool}")
|
||||
return 1
|
||||
|
||||
# 检查是否有tool_file(仅内部工具有)
|
||||
if not hasattr(tool, 'tool_file') or not tool.tool_file:
|
||||
print(f"❌ 外部工具无法直接运行: {args.tool}")
|
||||
return 1
|
||||
|
||||
tool_path = self.toolkit_root / tool.tool_file
|
||||
if not tool_path.exists():
|
||||
print(f"❌ 工具文件不存在: {tool_path}")
|
||||
return 1
|
||||
|
||||
print(f"🚀 运行工具: {tool.tool_name}")
|
||||
print(f"📁 文件: {tool.tool_file}")
|
||||
print(f"⏳ 正在执行...")
|
||||
print("=" * 70)
|
||||
|
||||
try:
|
||||
cmd = [str(tool_path)] + args.args
|
||||
result = subprocess.run(cmd, capture_output=False)
|
||||
print("=" * 70)
|
||||
print(f"✅ 执行完成 (退出码: {result.returncode})")
|
||||
return result.returncode
|
||||
except Exception as e:
|
||||
print(f"❌ 执行失败: {e}")
|
||||
return 1
|
||||
|
||||
def _cmd_recommend(self, args) -> int:
|
||||
"""执行recommend命令"""
|
||||
tools = self.discovery.recommend_tools(args.task)
|
||||
|
||||
if not tools:
|
||||
print(f"💡 未找到匹配的工具,尝试使用更通用的关键词搜索")
|
||||
return 0
|
||||
|
||||
print(f"\n🔍 为任务 '{args.task}' 推荐工具:")
|
||||
print("=" * 70)
|
||||
|
||||
for i, tool in enumerate(tools[:5], 1):
|
||||
print(f"\n{i}. {tool.tool_name}")
|
||||
print(f" ID: {tool.tool_id}")
|
||||
print(f" 语言: {tool.language}")
|
||||
print(f" 描述: {tool.description[:60]}...")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("💡 使用: show <tool-id> 查看详情")
|
||||
return 0
|
||||
|
||||
def _cmd_search(self, args) -> int:
|
||||
"""执行search命令"""
|
||||
tools = self.discovery.search_tools(args.keyword)
|
||||
|
||||
if not tools:
|
||||
print(f"⚠️ 未找到包含 '{args.keyword}' 的工具")
|
||||
return 0
|
||||
|
||||
print(f"\n🔍 搜索 '{args.keyword}' 找到 {len(tools)} 个结果:")
|
||||
for tool in tools:
|
||||
print(f" • {tool.tool_name} ({tool.tool_id}) - {tool.description[:50]}...")
|
||||
print()
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
toolkit_root = Path(__file__).parent.parent
|
||||
cli = ToolkitCLI(toolkit_root)
|
||||
sys.exit(cli.run())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
13
skills/toolkit/discover/config/__init__.py
Normal file
13
skills/toolkit/discover/config/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Configuration management for toolkit discovery
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG_ROOT = Path(__file__).parent
|
||||
EXTERNAL_TOOLS_CONFIG = CONFIG_ROOT / "external_tools.yaml"
|
||||
|
||||
__all__ = [
|
||||
'CONFIG_ROOT',
|
||||
'EXTERNAL_TOOLS_CONFIG'
|
||||
]
|
||||
113
skills/toolkit/discover/config/external_tools.yaml
Normal file
113
skills/toolkit/discover/config/external_tools.yaml
Normal file
@@ -0,0 +1,113 @@
|
||||
# External Tool Configurations
|
||||
# 外部工具配置信息,与代码分离便于维护
|
||||
|
||||
tools:
|
||||
- tool_id: "EXT-FZF-001"
|
||||
tool_name: "fzf (Fuzzy Finder)"
|
||||
command: "fzf"
|
||||
description: "命令行模糊查找器,用于交互式选择"
|
||||
category: "搜索/交互"
|
||||
use_cases:
|
||||
- "文件名查找"
|
||||
- "历史命令搜索"
|
||||
- "Git分支切换"
|
||||
install_guide: "brew install fzf (macOS) / apt-get install fzf (Ubuntu)"
|
||||
|
||||
- tool_id: "EXT-EZA-001"
|
||||
tool_name: "eza (Modern ls)"
|
||||
command: "eza"
|
||||
description: "现代化的ls替代品,带彩色输出和图标"
|
||||
category: "文件列表"
|
||||
use_cases:
|
||||
- "查看文件列表"
|
||||
- "树形结构显示"
|
||||
- "Git状态查看"
|
||||
install_guide: "brew install eza"
|
||||
|
||||
- tool_id: "EXT-ZOXIDE-001"
|
||||
tool_name: "zoxide (Smart cd)"
|
||||
command: "zoxide"
|
||||
description: "智能目录跳转工具,学习访问习惯"
|
||||
category: "目录导航"
|
||||
use_cases:
|
||||
- "快速跳转目录"
|
||||
- "访问频率学习"
|
||||
install_guide: "curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh"
|
||||
|
||||
- tool_id: "EXT-FD-001"
|
||||
tool_name: "fd (Simple find)"
|
||||
command: "fd"
|
||||
description: "简单友好的find替代品"
|
||||
category: "文件搜索"
|
||||
use_cases:
|
||||
- "查找文件"
|
||||
- "忽略.gitignore搜索"
|
||||
- "执行操作"
|
||||
install_guide: "brew install fd"
|
||||
|
||||
- tool_id: "EXT-RG-001"
|
||||
tool_name: "ripgrep (rg)"
|
||||
command: "rg"
|
||||
description: "极速代码搜索工具"
|
||||
category: "代码搜索"
|
||||
use_cases:
|
||||
- "搜索代码"
|
||||
- "显示上下文"
|
||||
- "统计匹配数"
|
||||
install_guide: "brew install ripgrep"
|
||||
|
||||
- tool_id: "EXT-BAT-001"
|
||||
tool_name: "bat (cat with syntax)"
|
||||
command: "bat"
|
||||
description: "带语法高亮的cat替代品"
|
||||
category: "文件查看"
|
||||
use_cases:
|
||||
- "查看代码文件"
|
||||
- "分页查看"
|
||||
- "Git修改查看"
|
||||
install_guide: "brew install bat"
|
||||
|
||||
- tool_id: "EXT-JQ-001"
|
||||
tool_name: "jq (JSON processor)"
|
||||
command: "jq"
|
||||
description: "JSON数据的命令行处理器"
|
||||
category: "数据处理"
|
||||
use_cases:
|
||||
- "JSON美化"
|
||||
- "字段提取"
|
||||
- "数据过滤"
|
||||
- "格式转换"
|
||||
install_guide: "brew install jq"
|
||||
|
||||
- tool_id: "EXT-XH-001"
|
||||
tool_name: "xh (HTTP client)"
|
||||
command: "xh"
|
||||
description: "友好的HTTP客户端,替代curl"
|
||||
category: "API测试"
|
||||
use_cases:
|
||||
- "发送HTTP请求"
|
||||
- "API测试"
|
||||
- "文件下载"
|
||||
install_guide: "brew install xh"
|
||||
|
||||
- tool_id: "EXT-DELTA-001"
|
||||
tool_name: "delta (Git diff美化)"
|
||||
command: "delta"
|
||||
description: "Git diff的美化工具"
|
||||
category: "Git工具"
|
||||
use_cases:
|
||||
- "查看Git diff"
|
||||
- "语法高亮"
|
||||
- "行号显示"
|
||||
install_guide: "brew install git-delta"
|
||||
|
||||
- tool_id: "EXT-STEAMSHIP-001"
|
||||
tool_name: "starship (Shell提示符)"
|
||||
command: "starship"
|
||||
description: "快速、可定制、智能的Shell提示符"
|
||||
category: "Shell增强"
|
||||
use_cases:
|
||||
- "显示Git状态"
|
||||
- "显示Python版本"
|
||||
- "显示目录"
|
||||
install_guide: "curl -sS https://starship.rs/install.sh | sh"
|
||||
13
skills/toolkit/discover/detectors/__init__.py
Normal file
13
skills/toolkit/discover/detectors/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Tool Detectors Module
|
||||
"""
|
||||
|
||||
from .base import ToolDetector
|
||||
from .internal import InternalToolDetector
|
||||
from .external import ExternalToolDetector
|
||||
|
||||
__all__ = [
|
||||
'ToolDetector',
|
||||
'InternalToolDetector',
|
||||
'ExternalToolDetector'
|
||||
]
|
||||
51
skills/toolkit/discover/detectors/base.py
Normal file
51
skills/toolkit/discover/detectors/base.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
Base detector interface for toolkit discovery
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import List, Any
|
||||
from ..models import Tool
|
||||
|
||||
|
||||
class ToolDetector(ABC):
|
||||
"""抽象基类:工具检测器"""
|
||||
|
||||
def __init__(self, root_path: Path):
|
||||
self.root = root_path
|
||||
self._tools = []
|
||||
|
||||
@property
|
||||
def tools(self) -> List[Tool]:
|
||||
"""获取检测到的工具列表"""
|
||||
return self._tools
|
||||
|
||||
@abstractmethod
|
||||
def detect(self) -> List[Tool]:
|
||||
"""
|
||||
检测工具
|
||||
|
||||
Returns:
|
||||
List[Tool]: 检测到的工具列表
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_tool(self, name_or_id: str) -> Tool:
|
||||
"""
|
||||
查找工具
|
||||
|
||||
Args:
|
||||
name_or_id: 工具名称或ID
|
||||
|
||||
Returns:
|
||||
Tool: 找到的工具
|
||||
|
||||
Raises:
|
||||
ToolDetectorError: 如果工具未找到
|
||||
"""
|
||||
pass
|
||||
|
||||
def refresh(self):
|
||||
"""刷新工具列表"""
|
||||
self._tools = self.detect()
|
||||
129
skills/toolkit/discover/detectors/external.py
Normal file
129
skills/toolkit/discover/detectors/external.py
Normal 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]
|
||||
137
skills/toolkit/discover/detectors/internal.py
Normal file
137
skills/toolkit/discover/detectors/internal.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Internal Tool Detector - 检测AI-runtime内部创建的工具
|
||||
"""
|
||||
|
||||
import sys
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Optional
|
||||
from .base import ToolDetector
|
||||
from ..models import InternalTool, ToolMetadata
|
||||
|
||||
|
||||
class InternalToolDetector(ToolDetector):
|
||||
"""内部工具检测器"""
|
||||
|
||||
def detect(self) -> List[InternalTool]:
|
||||
"""
|
||||
扫描工具包目录检测内部工具
|
||||
|
||||
Returns:
|
||||
List[InternalTool]: 检测到的内部工具列表
|
||||
"""
|
||||
self._tools = []
|
||||
|
||||
# 加载registry.md(暂时跳过详细解析)
|
||||
registry_file = self.root / "registry.md"
|
||||
if registry_file.exists():
|
||||
# 这里可以扩展registry解析逻辑
|
||||
pass
|
||||
|
||||
# 扫描所有语言目录
|
||||
for lang_dir in self.root.iterdir():
|
||||
if lang_dir.is_dir() and not lang_dir.name.startswith('.') and lang_dir.name != 'discover':
|
||||
self._scan_language_directory(lang_dir)
|
||||
|
||||
return self._tools
|
||||
|
||||
def _scan_language_directory(self, lang_dir: Path):
|
||||
"""扫描语言目录下的工具"""
|
||||
for meta_file in lang_dir.rglob("*.meta.yml"):
|
||||
try:
|
||||
tool = self._parse_meta_file(meta_file)
|
||||
if tool:
|
||||
self._tools.append(tool)
|
||||
except Exception as e:
|
||||
print(f"⚠️ 解析失败 {meta_file}: {e}", file=sys.stderr)
|
||||
|
||||
def _parse_meta_file(self, meta_file: Path) -> Optional[InternalTool]:
|
||||
"""解析元数据文件"""
|
||||
try:
|
||||
content = yaml.safe_load(meta_file.read_text(encoding='utf-8'))
|
||||
if not content:
|
||||
return None
|
||||
|
||||
# 获取工具文件
|
||||
tool_file = self._find_tool_file(meta_file)
|
||||
|
||||
# 解析基本信息
|
||||
basic_info = content.get("基本信息", {})
|
||||
|
||||
# 创建metadata
|
||||
metadata = ToolMetadata(
|
||||
tool_id=content.get("tool_id", "unknown"),
|
||||
tool_name=content.get("tool_name", "未命名工具"),
|
||||
description=content.get("功能描述", {}).get("简介", ""),
|
||||
purpose=content.get("用途分类", [])
|
||||
)
|
||||
|
||||
# 解析上次使用信息
|
||||
last_use = content.get("上次使用", {})
|
||||
if last_use:
|
||||
metadata.satisfaction = last_use.get("满意度", 0.0)
|
||||
|
||||
return InternalTool(
|
||||
metadata=metadata,
|
||||
meta_file=str(meta_file.relative_to(self.root)),
|
||||
tool_file=str(tool_file.relative_to(self.root)) if tool_file else None,
|
||||
language=basic_info.get("语言", "unknown"),
|
||||
file=basic_info.get("文件", "unknown"),
|
||||
complexity=basic_info.get("复杂度", "unknown"),
|
||||
usage=content.get("使用方法", {}),
|
||||
full_meta=content
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 警告: 解析元数据文件失败 {meta_file}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def _find_tool_file(self, meta_file: Path) -> Optional[Path]:
|
||||
"""查找与meta文件对应的工具文件"""
|
||||
possible_extensions = ['.sh', '.py', '.js', '.ts', '.java', '.go', '.rs']
|
||||
|
||||
for ext in possible_extensions:
|
||||
possible_file = meta_file.with_suffix(ext)
|
||||
if possible_file.exists():
|
||||
return possible_file
|
||||
|
||||
# 如果没找到,尝试与meta文件同名(去掉.meta部分)
|
||||
name_without_meta = meta_file.stem.replace('.meta', '')
|
||||
for ext in possible_extensions:
|
||||
possible_file = meta_file.parent / f"{name_without_meta}{ext}"
|
||||
if possible_file.exists():
|
||||
return possible_file
|
||||
|
||||
return None
|
||||
|
||||
def find_tool(self, name_or_id: str) -> Optional[InternalTool]:
|
||||
"""
|
||||
查找内部工具
|
||||
|
||||
Args:
|
||||
name_or_id: 工具名称或ID
|
||||
|
||||
Returns:
|
||||
InternalTool: 找到的工具,如果未找到返回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:
|
||||
print(f"⚠️ 找到多个匹配工具:")
|
||||
for i, tool in enumerate(matches[:5], 1):
|
||||
print(f" {i}. {tool.tool_name} ({tool.tool_id})")
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
def refresh(self):
|
||||
"""刷新工具列表"""
|
||||
super().refresh()
|
||||
222
skills/toolkit/discover/discovery.py
Normal file
222
skills/toolkit/discover/discovery.py
Normal file
@@ -0,0 +1,222 @@
|
||||
"""
|
||||
Main Discovery Orchestrator - 协调内部和外部工具检测
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from .detectors import InternalToolDetector, ExternalToolDetector
|
||||
from .models import Tool, InternalTool, ExternalTool
|
||||
from .formatters import ToolFormatter, TableFormatter, JsonFormatter
|
||||
|
||||
|
||||
class ToolkitDiscovery:
|
||||
"""工具包发现主类 - 协调所有检测器"""
|
||||
|
||||
def __init__(self, toolkit_root: Path):
|
||||
self.root = toolkit_root
|
||||
self.internal_detector = InternalToolDetector(toolkit_root)
|
||||
self.external_detector = ExternalToolDetector(toolkit_root)
|
||||
|
||||
# 格式化器
|
||||
self.table_formatter = TableFormatter()
|
||||
self.json_formatter = JsonFormatter()
|
||||
|
||||
# 初始化时加载所有工具
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
"""刷新所有工具列表"""
|
||||
self.internal_detector.refresh()
|
||||
self.external_detector.refresh()
|
||||
|
||||
@property
|
||||
def internal_tools(self) -> List[InternalTool]:
|
||||
"""获取内部工具列表"""
|
||||
return self.internal_detector.tools
|
||||
|
||||
@property
|
||||
def external_tools(self) -> List[ExternalTool]:
|
||||
"""获取外部工具列表"""
|
||||
return self.external_detector.tools
|
||||
|
||||
@property
|
||||
def all_tools(self) -> List[Tool]:
|
||||
"""获取所有工具列表"""
|
||||
return self.internal_tools + self.external_tools
|
||||
|
||||
def list_tools(
|
||||
self,
|
||||
internal_only: bool = False,
|
||||
external_only: bool = False
|
||||
) -> List[Tool]:
|
||||
"""
|
||||
列出工具
|
||||
|
||||
Args:
|
||||
internal_only: 仅返回内部工具
|
||||
external_only: 仅返回外部工具
|
||||
|
||||
Returns:
|
||||
List[Tool]: 工具列表
|
||||
"""
|
||||
if internal_only:
|
||||
return self.internal_tools
|
||||
elif external_only:
|
||||
return self.external_tools
|
||||
else:
|
||||
return self.all_tools
|
||||
|
||||
def find_tool(self, name_or_id: str) -> Optional[Tool]:
|
||||
"""
|
||||
查找工具(同时搜索内部和外部)
|
||||
|
||||
Args:
|
||||
name_or_id: 工具名称或ID
|
||||
|
||||
Returns:
|
||||
Tool: 找到的工具,如果未找到返回None
|
||||
"""
|
||||
# 先搜索内部工具
|
||||
tool = self.internal_detector.find_tool(name_or_id)
|
||||
if tool:
|
||||
return tool
|
||||
|
||||
# 再搜索外部工具
|
||||
tool = self.external_detector.find_tool(name_or_id)
|
||||
if tool:
|
||||
return tool
|
||||
|
||||
return None
|
||||
|
||||
def filter_tools(
|
||||
self,
|
||||
lang: Optional[str] = None,
|
||||
purpose: Optional[str] = None,
|
||||
query: Optional[str] = None
|
||||
) -> List[InternalTool]:
|
||||
"""
|
||||
过滤内部工具
|
||||
|
||||
Args:
|
||||
lang: 按语言过滤
|
||||
purpose: 按用途过滤
|
||||
query: 按名称或描述搜索
|
||||
|
||||
Returns:
|
||||
List[InternalTool]: 过滤后的内部工具列表
|
||||
"""
|
||||
tools = self.internal_tools
|
||||
|
||||
if lang:
|
||||
tools = [t for t in tools if t.language == lang]
|
||||
|
||||
if purpose:
|
||||
tools = [t for t in tools if purpose in t.metadata.purpose]
|
||||
|
||||
if query:
|
||||
query_lower = query.lower()
|
||||
tools = [
|
||||
t for t in tools
|
||||
if query_lower in t.tool_name.lower() or query_lower in t.description.lower()
|
||||
]
|
||||
|
||||
return tools
|
||||
|
||||
def search_tools(self, keyword: str) -> List[Tool]:
|
||||
"""
|
||||
搜索工具(内部和外部)
|
||||
|
||||
Args:
|
||||
keyword: 搜索关键词
|
||||
|
||||
Returns:
|
||||
List[Tool]: 匹配的工具列表
|
||||
"""
|
||||
keyword_lower = keyword.lower()
|
||||
|
||||
# 搜索内部工具
|
||||
internal_matches = [
|
||||
t for t in self.internal_tools
|
||||
if (keyword_lower in t.tool_name.lower() or
|
||||
keyword_lower in t.description.lower())
|
||||
]
|
||||
|
||||
# 搜索外部工具
|
||||
external_matches = [
|
||||
t for t in self.external_tools
|
||||
if (keyword_lower in t.tool_name.lower() or
|
||||
keyword_lower in t.description.lower() or
|
||||
keyword_lower in t.category.lower())
|
||||
]
|
||||
|
||||
return internal_matches + external_matches
|
||||
|
||||
def recommend_tools(self, task_description: str) -> List[InternalTool]:
|
||||
"""
|
||||
根据任务描述推荐工具
|
||||
|
||||
Args:
|
||||
task_description: 任务描述
|
||||
|
||||
Returns:
|
||||
List[InternalTool]: 推荐的工具列表(按匹配度排序)
|
||||
"""
|
||||
keywords = task_description.lower().split()
|
||||
|
||||
# 简单的推荐算法:匹配关键词数量
|
||||
scores = {}
|
||||
for tool in self.internal_tools:
|
||||
score = 0
|
||||
tool_text = (
|
||||
tool.tool_name + ' ' +
|
||||
tool.description + ' ' +
|
||||
' '.join(tool.metadata.purpose)
|
||||
).lower()
|
||||
|
||||
for keyword in keywords:
|
||||
if keyword in tool_text:
|
||||
score += 1
|
||||
|
||||
if score > 0:
|
||||
scores[tool] = score
|
||||
|
||||
# 按分数排序
|
||||
sorted_tools = sorted(scores.items(), key=lambda x: x[1], reverse=True)
|
||||
|
||||
return [tool for tool, _ in sorted_tools[:5]] # 返回前5个
|
||||
|
||||
def format_tools(
|
||||
self,
|
||||
tools: List[Tool],
|
||||
format_type: str = 'table'
|
||||
) -> str:
|
||||
"""
|
||||
格式化工具列表
|
||||
|
||||
Args:
|
||||
tools: 工具列表
|
||||
format_type: 格式类型 ('table' 或 'json')
|
||||
|
||||
Returns:
|
||||
str: 格式化后的字符串
|
||||
"""
|
||||
if format_type == 'json':
|
||||
return self.json_formatter.format(tools)
|
||||
else:
|
||||
return self.table_formatter.format(tools)
|
||||
|
||||
def format_tool(self, tool: Tool, format_type: str = 'table') -> str:
|
||||
"""
|
||||
格式化单个工具
|
||||
|
||||
Args:
|
||||
tool: 工具对象
|
||||
format_type: 格式类型 ('table' 或 'json')
|
||||
|
||||
Returns:
|
||||
str: 格式化后的字符串
|
||||
"""
|
||||
if format_type == 'json':
|
||||
return self.json_formatter.format_single(tool)
|
||||
else:
|
||||
return self.table_formatter.format_single(tool)
|
||||
13
skills/toolkit/discover/formatters/__init__.py
Normal file
13
skills/toolkit/discover/formatters/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Output Formatters Module
|
||||
"""
|
||||
|
||||
from .base import ToolFormatter
|
||||
from .table import TableFormatter
|
||||
from .json import JsonFormatter
|
||||
|
||||
__all__ = [
|
||||
'ToolFormatter',
|
||||
'TableFormatter',
|
||||
'JsonFormatter'
|
||||
]
|
||||
37
skills/toolkit/discover/formatters/base.py
Normal file
37
skills/toolkit/discover/formatters/base.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Abstract base class for tool formatters
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
from ..models import Tool
|
||||
|
||||
|
||||
class ToolFormatter(ABC):
|
||||
"""抽象基类:工具输出格式化器"""
|
||||
|
||||
@abstractmethod
|
||||
def format(self, tools: List[Tool]) -> str:
|
||||
"""
|
||||
格式化工具列表
|
||||
|
||||
Args:
|
||||
tools: 工具列表
|
||||
|
||||
Returns:
|
||||
str: 格式化后的字符串
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def format_single(self, tool: Tool) -> str:
|
||||
"""
|
||||
格式化单个工具
|
||||
|
||||
Args:
|
||||
tool: 工具对象
|
||||
|
||||
Returns:
|
||||
str: 格式化后的字符串
|
||||
"""
|
||||
pass
|
||||
67
skills/toolkit/discover/formatters/json.py
Normal file
67
skills/toolkit/discover/formatters/json.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
JSON Formatter - JSON格式输出
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import List
|
||||
from .base import ToolFormatter
|
||||
from ..models import Tool, InternalTool, ExternalTool
|
||||
|
||||
|
||||
class JsonFormatter(ToolFormatter):
|
||||
"""JSON格式化器"""
|
||||
|
||||
def format(self, tools: List[Tool]) -> str:
|
||||
"""格式化工具列表为JSON"""
|
||||
result = []
|
||||
for tool in tools:
|
||||
result.append(self._tool_to_dict(tool))
|
||||
return json.dumps(result, indent=2, ensure_ascii=False)
|
||||
|
||||
def format_single(self, tool: Tool) -> str:
|
||||
"""格式化单个工具为JSON"""
|
||||
return json.dumps(self._tool_to_dict(tool), indent=2, ensure_ascii=False)
|
||||
|
||||
def _tool_to_dict(self, tool: Tool) -> dict:
|
||||
"""将工具对象转换为字典"""
|
||||
# 基础信息
|
||||
data = {
|
||||
"tool_id": tool.tool_id,
|
||||
"tool_name": tool.tool_name,
|
||||
"description": tool.description
|
||||
}
|
||||
|
||||
# 内部工具特有信息
|
||||
if isinstance(tool, InternalTool):
|
||||
data.update({
|
||||
"type": "internal",
|
||||
"language": tool.language,
|
||||
"file": tool.file,
|
||||
"complexity": tool.complexity,
|
||||
"meta_file": tool.meta_file,
|
||||
"tool_file": tool.tool_file,
|
||||
"purpose": tool.metadata.purpose,
|
||||
"usage": tool.usage
|
||||
})
|
||||
|
||||
if tool.metadata.satisfaction > 0:
|
||||
data["satisfaction"] = tool.metadata.satisfaction
|
||||
|
||||
# 外部工具特有信息
|
||||
elif isinstance(tool, ExternalTool):
|
||||
data.update({
|
||||
"type": "external",
|
||||
"command": tool.command,
|
||||
"category": tool.category,
|
||||
"use_cases": tool.use_cases,
|
||||
"install_guide": tool.install_guide,
|
||||
"installed": tool.installed
|
||||
})
|
||||
|
||||
if tool.path:
|
||||
data["path"] = tool.path
|
||||
|
||||
if tool.metadata.version != "1.0.0":
|
||||
data["version"] = tool.metadata.version
|
||||
|
||||
return data
|
||||
178
skills/toolkit/discover/formatters/table.py
Normal file
178
skills/toolkit/discover/formatters/table.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
Table Formatter - 表格格式输出
|
||||
"""
|
||||
|
||||
from typing import List, Any
|
||||
from .base import ToolFormatter
|
||||
from ..models import Tool, InternalTool, ExternalTool
|
||||
|
||||
|
||||
class TableFormatter(ToolFormatter):
|
||||
"""表格格式化器"""
|
||||
|
||||
def format(self, tools: List[Tool]) -> str:
|
||||
"""格式化工具列表为表格"""
|
||||
if not tools:
|
||||
return "⚠️ 未找到匹配的工具\n"
|
||||
|
||||
# 分离内部工具和外部工具
|
||||
internal = [t for t in tools if isinstance(t, InternalTool)]
|
||||
external = [t for t in tools if isinstance(t, ExternalTool)]
|
||||
|
||||
output = []
|
||||
|
||||
# 内部工具
|
||||
if internal:
|
||||
output.append(self._format_internal_tools(internal))
|
||||
|
||||
# 外部工具
|
||||
if external:
|
||||
if internal:
|
||||
output.append("")
|
||||
output.append(self._format_external_tools(external))
|
||||
|
||||
return "\n".join(output) + "\n"
|
||||
|
||||
def _format_internal_tools(self, tools: List[InternalTool]) -> str:
|
||||
"""格式化内部工具"""
|
||||
lines = [
|
||||
f"📦 找到 {len(tools)} 个内部工具:",
|
||||
"=" * 110,
|
||||
f"{'名称':<25} {'ID':<25} {'语言':<8} {'用途':<15} {'描述':<30}",
|
||||
"-" * 110
|
||||
]
|
||||
|
||||
for tool in tools:
|
||||
purposes = ",".join(tool.metadata.purpose)[:13]
|
||||
desc = tool.description[:28]
|
||||
lines.append(
|
||||
f"{tool.tool_name:<25} {tool.tool_id:<25} {tool.language:<8} {purposes:<15} {desc:<30}"
|
||||
)
|
||||
|
||||
lines.append("=" * 110)
|
||||
return "\n".join(lines)
|
||||
|
||||
def _format_external_tools(self, tools: List[ExternalTool]) -> str:
|
||||
"""格式化外部工具"""
|
||||
lines = [
|
||||
f"🌟 找到 {len(tools)} 个外部工具:",
|
||||
"=" * 100,
|
||||
f"{'名称':<25} {'ID':<20} {'分类':<12} {'安装状态':<10} {'描述':<30}",
|
||||
"-" * 100
|
||||
]
|
||||
|
||||
for tool in tools:
|
||||
status = tool.status
|
||||
desc = tool.description[:30]
|
||||
lines.append(
|
||||
f"{tool.tool_name:<25} {tool.tool_id:<20} {tool.category:<12} {status:<10} {desc:<30}"
|
||||
)
|
||||
|
||||
lines.append("=" * 100)
|
||||
lines.extend([
|
||||
"",
|
||||
"💡 提示: 使用 --external 仅显示外部工具",
|
||||
"💡 提示: 外部工具是系统级的CLI工具,需单独安装"
|
||||
])
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def format_single(self, tool: Tool) -> str:
|
||||
"""格式化单个工具详情"""
|
||||
if isinstance(tool, InternalTool):
|
||||
return self._format_internal_tool(tool)
|
||||
elif isinstance(tool, ExternalTool):
|
||||
return self._format_external_tool(tool)
|
||||
else:
|
||||
return self._format_generic_tool(tool)
|
||||
|
||||
def _format_internal_tool(self, tool: InternalTool) -> str:
|
||||
"""格式化内部工具详情"""
|
||||
lines = [
|
||||
"",
|
||||
"=" * 70,
|
||||
f"📦 {tool.tool_name}",
|
||||
"=" * 70,
|
||||
f"ID: {tool.tool_id}",
|
||||
f"语言: {tool.language}",
|
||||
f"文件: {tool.file}",
|
||||
f"复杂度: {tool.complexity}",
|
||||
f"用途: {', '.join(tool.metadata.purpose)}",
|
||||
"",
|
||||
"📋 描述:",
|
||||
f" {tool.description}",
|
||||
""
|
||||
]
|
||||
|
||||
if tool.usage:
|
||||
lines.append("🚀 使用方法:")
|
||||
if '命令' in tool.usage:
|
||||
lines.append(f" 命令: {tool.usage['命令']}")
|
||||
if '参数' in tool.usage:
|
||||
lines.append(" 参数:")
|
||||
for param, desc in tool.usage['参数'].items():
|
||||
lines.append(f" - {param}: {desc}")
|
||||
if '示例' in tool.usage:
|
||||
lines.append(" 示例:")
|
||||
for example in tool.usage.get('示例', [])[:3]:
|
||||
lines.append(f" • {example}")
|
||||
lines.append("")
|
||||
|
||||
if tool.metadata.satisfaction > 0:
|
||||
lines.extend([
|
||||
"📊 使用统计:",
|
||||
f" 满意度: {tool.metadata.satisfaction}/1.0",
|
||||
""
|
||||
])
|
||||
|
||||
lines.extend([
|
||||
"📂 文件位置:",
|
||||
f" 元数据: {tool.meta_file}",
|
||||
f"{'=' * 70}",
|
||||
""
|
||||
])
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def _format_external_tool(self, tool: ExternalTool) -> str:
|
||||
"""格式化外部工具详情"""
|
||||
lines = [
|
||||
"",
|
||||
"=" * 70,
|
||||
f"🌟 {tool.tool_name}",
|
||||
"=" * 70,
|
||||
f"ID: {tool.tool_id}",
|
||||
f"分类: {tool.category}",
|
||||
f"命令: {tool.command}",
|
||||
f"状态: {tool.status}",
|
||||
"",
|
||||
"📋 描述:",
|
||||
f" {tool.description}",
|
||||
"",
|
||||
"💡 使用场景:",
|
||||
]
|
||||
|
||||
for use_case in tool.use_cases:
|
||||
lines.append(f" • {use_case}")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"📥 安装指南:",
|
||||
f" {tool.install_guide}",
|
||||
""
|
||||
])
|
||||
|
||||
if tool.path:
|
||||
lines.extend([
|
||||
"📂 安装路径:",
|
||||
f" {tool.path}",
|
||||
""
|
||||
])
|
||||
|
||||
lines.append(f"{'=' * 70}\n")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def _format_generic_tool(self, tool: Tool) -> str:
|
||||
"""格式化通用工具详情"""
|
||||
return f"\n{'=' * 70}\n📦 {tool.tool_name} ({tool.tool_id})\n{'=' * 70}\n 描述: {tool.description}\n{'=' * 70}\n\n"
|
||||
13
skills/toolkit/discover/models/__init__.py
Normal file
13
skills/toolkit/discover/models/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Data models for toolkit discovery system
|
||||
"""
|
||||
|
||||
from .tool import Tool, InternalTool, ExternalTool, ToolUsage, ToolMetadata
|
||||
|
||||
__all__ = [
|
||||
'Tool',
|
||||
'InternalTool',
|
||||
'ExternalTool',
|
||||
'ToolUsage',
|
||||
'ToolMetadata'
|
||||
]
|
||||
107
skills/toolkit/discover/models/tool.py
Normal file
107
skills/toolkit/discover/models/tool.py
Normal file
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
数据模型:工具和元数据定义
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Any, Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolUsage:
|
||||
"""工具使用信息"""
|
||||
command: str
|
||||
task: str
|
||||
trigger: str
|
||||
expected: str
|
||||
|
||||
execution_status: str = "unknown"
|
||||
output_files: List[str] = field(default_factory=list)
|
||||
key_findings: Dict[str, Any] = field(default_factory=dict)
|
||||
satisfaction: float = 0.0
|
||||
duration: float = 0.0
|
||||
|
||||
followup_actions: List[str] = field(default_factory=list)
|
||||
lessons_learned: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolMetadata:
|
||||
"""工具元数据基类"""
|
||||
tool_id: str
|
||||
tool_name: str
|
||||
description: str
|
||||
purpose: List[str] = field(default_factory=list)
|
||||
last_used: Optional[datetime] = None
|
||||
satisfaction: float = 0.0
|
||||
|
||||
related_tools: Dict[str, List[str]] = field(default_factory=dict)
|
||||
maintenance_notes: Dict[str, Any] = field(default_factory=dict)
|
||||
version: str = "1.0.0"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Tool:
|
||||
"""工具基类"""
|
||||
metadata: ToolMetadata
|
||||
|
||||
@property
|
||||
def tool_id(self) -> str:
|
||||
return self.metadata.tool_id
|
||||
|
||||
@property
|
||||
def tool_name(self) -> str:
|
||||
return self.metadata.tool_name
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return self.metadata.description
|
||||
|
||||
|
||||
@dataclass
|
||||
class InternalTool(Tool):
|
||||
"""内部工具(AI-runtime创建的工具)"""
|
||||
meta_file: str
|
||||
tool_file: Optional[str]
|
||||
language: str
|
||||
file: str
|
||||
complexity: str
|
||||
|
||||
usage: Dict[str, Any] = field(default_factory=dict)
|
||||
full_meta: Optional[Dict[str, Any]] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if not isinstance(self.metadata, ToolMetadata):
|
||||
self.metadata = ToolMetadata(
|
||||
tool_id=self.tool_id,
|
||||
tool_name=self.tool_name,
|
||||
description=self.description,
|
||||
purpose=self.metadata.get("purpose", []) if isinstance(self.metadata, dict) else self.metadata.purpose
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExternalTool(Tool):
|
||||
"""外部工具(第三方CLI工具)"""
|
||||
command: str
|
||||
category: str
|
||||
use_cases: List[str]
|
||||
install_guide: str
|
||||
|
||||
installed: bool = False
|
||||
path: Optional[str] = None
|
||||
|
||||
@property
|
||||
def status(self) -> str:
|
||||
"""获取安装状态"""
|
||||
return "✅ 已安装" if self.installed else "❌ 未安装"
|
||||
|
||||
|
||||
class ToolDetectorError(Exception):
|
||||
"""工具检测异常"""
|
||||
pass
|
||||
|
||||
|
||||
class ToolFormatError(Exception):
|
||||
"""工具格式化异常"""
|
||||
pass
|
||||
Reference in New Issue
Block a user