Files
sanguo_llmwiki/mcp_server/tools/wiki_lint.py
T
claude_dev 62b8fef18a fix: 代码审查问题修复
- C1 (Critical): 添加 PyYAML 解析 frontmatter,支持多行值和列表
- M1 (Major): 添加 FTS5 查询验证,防止注入攻击
- M2/M3 (Major): 工具类使用显式 wiki_vault_path 参数
- M4 (Major): 集成 aiofiles 实现真正的异步文件 I/O
- M5 (Major): 改用 logger.exception() 记录完整堆栈

Co-Authored-By: Claude Dev <noreply@anthropic.com>
2026-06-26 12:10:41 +08:00

167 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Tool Layer - wiki_lint 工具
审计 wiki 健康(格式、链接、frontmatter 规范)。
参考设计文档:第 2.2 节
"""
import logging
import os
from typing import Dict, Any, List
from pathlib import Path
from ..services import ParserService, QueryService, GraphService
logger = logging.getLogger(__name__)
class WikiLintTool:
"""wiki_lint 工具实现"""
def __init__(self, parser: ParserService, query_service: QueryService, graph_service: GraphService, wiki_vault_path: str = None):
self.parser = parser
self.query_service = query_service
self.graph_service = graph_service
# 优先使用传入的路径,否则使用环境变量
self.wiki_vault_path = Path(wiki_vault_path or os.environ.get("WIKI_VAULT_PATH", "/Volumes/KnowledgeBase/wiki-vault"))
async def handle(self, path: str = "", level: str = "basic") -> Dict[str, Any]:
"""
审计 wiki 健康
Args:
path: 指定路径(空字符串表示全部)
level: 检查级别(basic/strict
Returns:
{
"issues": [
{
"path": "practices/example.md",
"type": "missing_title",
"message": "Missing title",
"severity": "warning"
}
],
"fixes": [
{
"path": "practices/example.md",
"action": "add_title",
"suggestion": "# Example Page"
}
],
"summary": {
"total_issues": 5,
"critical": 0,
"warning": 5,
"info": 0
}
}
"""
issues = []
fixes = []
# 获取所有页面或指定页面
if path:
pages = [await self.query_service.get_page(path)]
else:
pages = await self.query_service.db.get_all_pages()
summary = {"critical": 0, "warning": 0, "info": 0}
for page in pages:
# 读取内容
try:
full_path = self.wiki_vault_path / page.path
with open(full_path, 'r', encoding='utf-8') as f:
content = f.read()
except Exception as e:
issues.append({
"path": page.path,
"type": "file_error",
"message": f"Failed to read file: {e}",
"severity": "critical"
})
summary["critical"] += 1
continue
# 验证页面
validation_issues = self.parser.validate_page(page.path, content)
for issue in validation_issues:
severity = "warning" if "Missing" in issue else "info"
issues.append({
"path": page.path,
"type": issue.lower().replace(" ", "_"),
"message": issue,
"severity": severity
})
summary[severity] += 1
# 生成修复建议
if "Missing title" in issue:
title = self.parser.extract_title(content)
if not title:
title = page.path.replace(".md", "").replace("-", " ").replace("_", " ").title()
fixes.append({
"path": page.path,
"action": "add_title",
"suggestion": f"# {title}"
})
# strict 模式额外检查
if level == "strict":
# 检查孤立页面
orphans = await self.graph_service.find_orphans()
for orphan in orphans:
issues.append({
"path": orphan,
"type": "orphan",
"message": "No backlinks found",
"severity": "info"
})
summary["info"] += 1
# 检查缺失链接
missing_links = await self.graph_service.find_missing_links()
for source, target in missing_links[:10]:
issues.append({
"path": source,
"type": "broken_link",
"message": f"Link to non-existent page: {target}",
"severity": "warning"
})
summary["warning"] += 1
return {
"issues": issues,
"fixes": fixes,
"summary": {
"total_issues": len(issues),
**summary
}
}
def get_schema(self) -> dict:
"""返回 MCP Tool schema"""
return {
"name": "wiki_lint",
"description": "审计 wiki 健康(格式、链接、frontmatter 规范)",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"default": "",
"description": "指定路径(空字符串表示全部)"
},
"level": {
"type": "string",
"default": "basic",
"enum": ["basic", "strict"],
"description": "检查级别"
}
}
}
}