dfd8421dc6
**Storage Layer:** - Database 类(aiosqlite + WAL + 并发保护) - WikiPage 数据模型 - fix_dirty_states 恢复机制 - FTS5 全文搜索支持 - 完整的 SQLite 表结构 **Service Layer:** - CacheService(LRU 缓存 + TTL + 大小限制) - QueryService(查询服务) - ParserService(Markdown 解析) - IndexerService(索引服务) - GraphService(链接图服务) **Tool Layer (8 个 MCP Tools):** - wiki_query - FTS5 全文搜索 + 标签过滤 - memory_bridge - 按工具来源浏览 - wiki_status - 索引状态 - wiki_lint - 健康审计 - cross_linker - 缺失链接发现 - tag_taxonomy - 标签一致性 - wiki_synthesize - 跨概念综合分析 - daily_update - 日常维护 + hot.md 生成 **MCP Protocol Layer:** - MCP 协议解析和封装 - 工具注册和路由 - 错误处理和日志 - stdio 模式支持 **配置和部署:** - requirements.txt - config.example.yaml - ecosystem.config.cjs (PM2) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
"""
|
||
Tool Layer - wiki_lint 工具
|
||
|
||
审计 wiki 健康(格式、链接、frontmatter 规范)。
|
||
|
||
参考设计文档:第 2.2 节
|
||
"""
|
||
|
||
import logging
|
||
from typing import Dict, Any, List
|
||
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):
|
||
self.parser = parser
|
||
self.query_service = query_service
|
||
self.graph_service = graph_service
|
||
|
||
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:
|
||
from pathlib import Path
|
||
vault_path = Path(self.query_service.db.path).parent.parent / "wiki-vault"
|
||
full_path = 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": "检查级别"
|
||
}
|
||
}
|
||
}
|
||
}
|