chore: initialize DevHarness template

This commit is contained in:
QiuSW
2026-08-07 23:11:53 +08:00
commit d6706f7c69
13 changed files with 588 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
"""检查 DevHarness 必需文件和任务归档的基本结构。"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REQUIRED_FILES = (
"AGENTS.md",
"README.md",
"docs/00-project-profile.md",
"docs/01-workflow.md",
"docs/templates/task-archive.md",
".gitea/issue_template/epic.md",
".gitea/issue_template/mvp.md",
".gitea/issue_template/task.md",
)
ARCHIVE_HEADINGS = (
"## 背景与目标",
"## 最终方案",
"## 修改文件",
"## 验收结果",
"## 测试",
"## 相关提交",
)
def check_required_files(errors: list[str]) -> None:
for relative_path in REQUIRED_FILES:
if not (ROOT / relative_path).is_file():
errors.append(f"缺少必需文件:{relative_path}")
def check_project_profile(errors: list[str], warnings: list[str], strict: bool) -> None:
profile = ROOT / "docs" / "00-project-profile.md"
if not profile.is_file():
return
if "<填写" in profile.read_text(encoding="utf-8"):
message = "项目档案仍有未填写内容"
(errors if strict else warnings).append(message)
def check_archives(errors: list[str]) -> None:
task_dir = ROOT / "docs" / "task"
for path in task_dir.glob("*.md"):
if not re.match(r"^\d+-.+\.md$", path.name):
errors.append(f"归档文件名不符合 <编号>-<标题>.md:{path.name}")
content = path.read_text(encoding="utf-8")
for heading in ARCHIVE_HEADINGS:
if heading not in content:
errors.append(f"{path.name} 缺少章节:{heading}")
if "**未验证部分**:" not in content:
errors.append(f"{path.name} 没有记录未验证部分")
def main() -> int:
parser = argparse.ArgumentParser(description="检查 DevHarness 项目结构")
parser.add_argument(
"--strict",
action="store_true",
help="项目档案有占位内容时返回失败",
)
args = parser.parse_args()
errors: list[str] = []
warnings: list[str] = []
check_required_files(errors)
check_project_profile(errors, warnings, args.strict)
check_archives(errors)
for warning in warnings:
print(f"警告:{warning}")
for error in errors:
print(f"错误:{error}")
if errors:
print(f"检查失败:{len(errors)} 个问题")
return 1
print("DevHarness 检查通过")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+56
View File
@@ -0,0 +1,56 @@
"""根据模板创建任务归档草稿。"""
from __future__ import annotations
import argparse
import re
from datetime import date
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TEMPLATE = ROOT / "docs" / "templates" / "task-archive.md"
TASK_DIR = ROOT / "docs" / "task"
def safe_title(title: str) -> str:
"""把标题转换为适合文件名的短文本。"""
cleaned = re.sub(r'[<>:"/\\|?*]', "-", title.strip())
cleaned = re.sub(r"\s+", "-", cleaned)
return cleaned.strip(".-")
def create_archive(issue_number: str, title: str) -> Path:
"""创建归档草稿;目标文件存在时拒绝覆盖。"""
short_title = safe_title(title)
if not issue_number.isdigit():
raise ValueError("工单号必须是数字")
if not short_title:
raise ValueError("标题不能为空")
target = TASK_DIR / f"{issue_number}-{short_title}.md"
if target.exists():
raise FileExistsError(f"文件已存在:{target}")
content = TEMPLATE.read_text(encoding="utf-8")
content = content.replace("<工单号>", issue_number, 1)
content = content.replace("<标题>", title.strip(), 1)
content = content.replace("YYYY-MM-DD", date.today().isoformat(), 1)
target.write_text(content, encoding="utf-8")
return target
def main() -> None:
parser = argparse.ArgumentParser(description="创建 docs/task 任务归档草稿")
parser.add_argument("issue_number", help="Gitea 工单号,例如 123")
parser.add_argument("title", help="简短任务标题")
args = parser.parse_args()
target = create_archive(args.issue_number, args.title)
print(f"已创建:{target.relative_to(ROOT)}")
if __name__ == "__main__":
main()