155 lines
4.4 KiB
Python
155 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
需求追踪器
|
|
从对话历史和计划文件中提取需求,生成检查清单
|
|
"""
|
|
import json
|
|
import sys
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
def load_input():
|
|
"""加载 Hook 输入数据"""
|
|
try:
|
|
return json.load(sys.stdin)
|
|
except json.JSONDecodeError:
|
|
return {}
|
|
|
|
|
|
def extract_requirements_from_conversation(conversation_text):
|
|
"""从对话中提取需求"""
|
|
requirements = []
|
|
|
|
# 需求关键词模式
|
|
patterns = {
|
|
'功能需求': r'(需要|实现|添加|创建|开发)\s*([^,。;\n]{5,50})',
|
|
'技术栈': r'(使用|基于|框架)\s*([^,。;\n]{2,30})',
|
|
'约束条件': r'(必须|应该|不能|禁止)\s*([^,。;\n]{5,50})',
|
|
}
|
|
|
|
for category, pattern in patterns.items():
|
|
matches = re.findall(pattern, conversation_text)
|
|
for match in matches:
|
|
if isinstance(match, tuple):
|
|
requirements.append({
|
|
'category': category,
|
|
'description': match[-1].strip(),
|
|
'verified': False
|
|
})
|
|
|
|
return requirements
|
|
|
|
|
|
def read_plan_files(project_root):
|
|
"""读取项目计划文件"""
|
|
plan_files = []
|
|
docs_path = Path(project_root) / 'docs' / 'plans'
|
|
|
|
if docs_path.exists():
|
|
for plan_file in docs_path.glob('*.md'):
|
|
try:
|
|
with open(plan_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
plan_files.append({
|
|
'path': str(plan_file),
|
|
'content': content
|
|
})
|
|
except Exception as e:
|
|
pass
|
|
|
|
return plan_files
|
|
|
|
|
|
def parse_plan_to_checklist(plan_content):
|
|
"""解析计划文件为检查清单"""
|
|
checklist = []
|
|
|
|
# 提取任务/功能项
|
|
lines = plan_content.split('\n')
|
|
for line in lines:
|
|
line = line.strip()
|
|
# 匹配 markdown 列表项
|
|
if line.startswith('- ') or line.startswith('* '):
|
|
item = line[2:].strip()
|
|
# 移除 [ ] 或 [x] 标记
|
|
item = re.sub(r'^\[[ x]\]\s*', '', item)
|
|
if item:
|
|
checklist.append({
|
|
'description': item,
|
|
'verified': False,
|
|
'source': 'plan_file'
|
|
})
|
|
|
|
return checklist
|
|
|
|
|
|
def generate_requirement_checklist(project_root, conversation_context=''):
|
|
"""生成需求检查清单"""
|
|
checklist = {
|
|
'requirements': [],
|
|
'source': 'unknown'
|
|
}
|
|
|
|
# 1. 尝试从计划文件读取
|
|
plan_files = read_plan_files(project_root)
|
|
if plan_files:
|
|
for plan in plan_files:
|
|
items = parse_plan_to_checklist(plan['content'])
|
|
checklist['requirements'].extend(items)
|
|
checklist['source'] = 'plan_files'
|
|
checklist['plan_files'] = [p['path'] for p in plan_files]
|
|
|
|
# 2. 如果没有计划文件,从对话提取
|
|
if not checklist['requirements'] and conversation_context:
|
|
items = extract_requirements_from_conversation(conversation_context)
|
|
checklist['requirements'] = items
|
|
checklist['source'] = 'conversation'
|
|
|
|
return checklist
|
|
|
|
|
|
def save_checklist(checklist, task_id, project_root):
|
|
"""保存检查清单到临时文件"""
|
|
cache_dir = Path(project_root) / '.claude' / 'cache'
|
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
checklist_file = cache_dir / f'requirements_{task_id}.json'
|
|
with open(checklist_file, 'w', encoding='utf-8') as f:
|
|
json.dump(checklist, f, ensure_ascii=False, indent=2)
|
|
|
|
return str(checklist_file)
|
|
|
|
|
|
def main():
|
|
"""主函数"""
|
|
input_data = load_input()
|
|
|
|
# 获取项目根目录
|
|
project_root = input_data.get('projectRoot', os.getcwd())
|
|
|
|
# 获取对话上下文(如果有)
|
|
conversation_context = input_data.get('conversationContext', '')
|
|
|
|
# 生成检查清单
|
|
checklist = generate_requirement_checklist(project_root, conversation_context)
|
|
|
|
# 保存到缓存
|
|
task_id = input_data.get('taskId', 'default')
|
|
checklist_file = save_checklist(checklist, task_id, project_root)
|
|
|
|
# 输出结果
|
|
output = {
|
|
'checklist_generated': True,
|
|
'requirements_count': len(checklist['requirements']),
|
|
'source': checklist['source'],
|
|
'checklist_file': checklist_file
|
|
}
|
|
|
|
print(json.dumps(output, ensure_ascii=False))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|