feat: 新增 code_stats Skill,修复 Skill handler 参数解析

- 新增 skills/code_stats.py: Python Skill 示例,统计代码量
- 修复 unified_tool_manager: Skill handler 支持 key=value 参数格式
- Skill 协议测试通过: 直接调用 + MCP 调用均正常

本次更新由 mimo 自动化执行,与开发者无关。
This commit is contained in:
Xiaoli Dev committed 2026-04-25 11:18:20 +08:00
1 parent 2af7004247
commit 4c41cf4f99
2 files changed
+150 -1

No files matched your search

+140
View File
@@ -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
}
)