From 4c41cf4f993f43d79521495b99e46ad12ef0592a Mon Sep 17 00:00:00 2001 From: Xiaoli Dev Date: Sat, 25 Apr 2026 11:18:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20code=5Fstats=20Ski?= =?UTF-8?q?ll=EF=BC=8C=E4=BF=AE=E5=A4=8D=20Skill=20handler=20=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 skills/code_stats.py: Python Skill 示例,统计代码量 - 修复 unified_tool_manager: Skill handler 支持 key=value 参数格式 - Skill 协议测试通过: 直接调用 + MCP 调用均正常 本次更新由 mimo 自动化执行,与开发者无关。 --- skills/code_stats.py | 140 ++++++++++++++++++++++++++++++++++++++++ unified_tool_manager.py | 11 +++- 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 skills/code_stats.py diff --git a/skills/code_stats.py b/skills/code_stats.py new file mode 100644 index 0000000..7981d89 --- /dev/null +++ b/skills/code_stats.py @@ -0,0 +1,140 @@ +""" +代码统计 Skill - 统计项目代码行数、文件数等 +""" +import os +from skills.base import Skill, SkillResult, SkillParameter, ParameterType + + +class CodeStatsSkill(Skill): + """代码统计技能 - 统计目录中的代码行数、文件数、注释率等""" + + def __init__(self): + super().__init__() + self.name = "code_stats" + self.description = "统计项目代码量:文件数、代码行、注释行、空行、注释率" + self.version = "1.0.0" + self.author = "mimo" + self.tags = ["代码", "统计", "stats", "lines"] + + @property + def parameters(self): + return [ + SkillParameter( + name="directory", + param_type=ParameterType.STRING, + description="要统计的目录路径", + required=True + ), + SkillParameter( + name="file_pattern", + param_type=ParameterType.STRING, + description="文件匹配模式,如 *.py", + required=False, + default="*.py" + ), + ] + + def execute(self, **kwargs) -> SkillResult: + directory = kwargs.get("directory", ".") + file_pattern = kwargs.get("file_pattern", "*.py") + + if not os.path.isdir(directory): + return SkillResult(success=False, error=f"目录不存在: {directory}") + + # 确定文件扩展名 + if file_pattern.startswith("*."): + ext = file_pattern[1:] + file_filter = lambda f: f.endswith(ext) + else: + file_filter = lambda f: True + + total_files = 0 + total_lines = 0 + total_code = 0 + total_blank = 0 + total_comment = 0 + file_details = [] + + skip_dirs = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'env'} + + for root, dirs, files in os.walk(directory): + dirs[:] = [d for d in dirs if d not in skip_dirs and not d.startswith('.')] + + for filename in sorted(files): + if not file_filter(filename): + continue + + filepath = os.path.join(root, filename) + rel_path = os.path.relpath(filepath, directory) + total_files += 1 + + file_lines = 0 + file_code = 0 + file_blank = 0 + file_comment = 0 + + try: + with open(filepath, 'r', encoding='utf-8') as f: + for line in f: + total_lines += 1 + file_lines += 1 + stripped = line.strip() + if not stripped: + total_blank += 1 + file_blank += 1 + elif stripped.startswith('#'): + total_comment += 1 + file_comment += 1 + else: + total_code += 1 + file_code += 1 + except (UnicodeDecodeError, PermissionError): + continue + + file_details.append({ + "file": rel_path, + "lines": file_lines, + "code": file_code, + "comment": file_comment, + "blank": file_blank, + }) + + # 按代码行数排序 + file_details.sort(key=lambda x: x["code"], reverse=True) + + # 构建报告 + comment_rate = (total_comment / max(total_code, 1)) * 100 + + report_lines = [ + f"📊 代码统计 ({directory})", + f"{'─' * 40}", + f" 文件数: {total_files}", + f" 总行数: {total_lines}", + f" 代码行: {total_code}", + f" 注释行: {total_comment}", + f" 空行: {total_blank}", + f" 注释率: {comment_rate:.1f}%", + ] + + if file_details: + report_lines.append(f"\n📄 文件明细 (按代码行数排序):") + for fd in file_details[:20]: + report_lines.append( + f" {fd['file']:40} {fd['code']:5} 行代码" + ) + if len(file_details) > 20: + report_lines.append(f" ... 还有 {len(file_details) - 20} 个文件") + + return SkillResult( + success=True, + message='\n'.join(report_lines), + data={ + "total_files": total_files, + "total_lines": total_lines, + "total_code": total_code, + "total_comment": total_comment, + "total_blank": total_blank, + "comment_rate": comment_rate, + "files": file_details + } + ) diff --git a/unified_tool_manager.py b/unified_tool_manager.py index 0b3499e..478af12 100644 --- a/unified_tool_manager.py +++ b/unified_tool_manager.py @@ -206,11 +206,20 @@ class UnifiedToolManager: kwargs = {} # 解析参数 - if args.strip().startswith('{'): + args = args.strip() + if args.startswith('{'): try: kwargs = json.loads(args) except json.JSONDecodeError: kwargs = {'operation': args} + elif '=' in args: + # key=value 格式: "directory=. file_pattern=*.py" + for part in args.split(): + if '=' in part: + k, v = part.split('=', 1) + kwargs[k.strip()] = v.strip() + else: + kwargs.setdefault('operation', part) else: kwargs = {'operation': args}