116 lines
4.3 KiB
Python
116 lines
4.3 KiB
Python
from __future__ import annotations
|
||
|
||
from .shared import *
|
||
|
||
class TaskBriefSeedHistoryMixin:
|
||
@staticmethod
|
||
def _extract_markdown_section_lines(content: str, *, heading: str) -> list[str]:
|
||
target = f"## {heading}".lower()
|
||
lines = content.splitlines()
|
||
in_section = False
|
||
collected: list[str] = []
|
||
for raw in lines:
|
||
line = raw.strip()
|
||
if line.lower().startswith("## "):
|
||
if line.lower() == target:
|
||
in_section = True
|
||
continue
|
||
if in_section:
|
||
break
|
||
if in_section and line:
|
||
collected.append(line)
|
||
return collected
|
||
|
||
@staticmethod
|
||
def _extract_markdown_meta_value(content: str, *, key: str) -> str | None:
|
||
prefix = f"- {key}:"
|
||
for raw in content.splitlines():
|
||
line = raw.strip()
|
||
if line.startswith(prefix):
|
||
return line.split(":", 1)[1].strip() or None
|
||
return None
|
||
|
||
@classmethod
|
||
def _normalize_generated_at(cls, raw_generated: str | None, fallback: datetime) -> str:
|
||
if not raw_generated:
|
||
return fallback.isoformat()
|
||
for fmt in ("%Y-%m-%d %H:%M:%S %z",):
|
||
try:
|
||
return datetime.strptime(raw_generated, fmt).isoformat()
|
||
except Exception:
|
||
pass
|
||
try:
|
||
return datetime.fromisoformat(raw_generated).isoformat()
|
||
except Exception:
|
||
return raw_generated
|
||
|
||
@classmethod
|
||
def _load_seed_history(cls) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||
docs_dir = cls._project_root() / "docs/task_briefs"
|
||
if not docs_dir.exists():
|
||
return [], []
|
||
|
||
briefs: list[dict[str, Any]] = []
|
||
runs: list[dict[str, Any]] = []
|
||
for file_path in sorted(docs_dir.glob("task_brief_*.md")):
|
||
try:
|
||
content = file_path.read_text(encoding="utf-8")
|
||
except Exception:
|
||
continue
|
||
if not content.strip():
|
||
continue
|
||
|
||
stem = "".join(ch for ch in file_path.stem if ch.isalnum() or ch in {"_", "-"})
|
||
brief_id = f"brief_seed_{stem}"[:64]
|
||
run_id = f"run_seed_{stem}"[:64]
|
||
fallback_time = datetime.fromtimestamp(file_path.stat().st_mtime)
|
||
generated_at = cls._normalize_generated_at(
|
||
cls._extract_markdown_meta_value(content, key="Generated At"),
|
||
fallback=fallback_time,
|
||
)
|
||
|
||
title = "导入任务包"
|
||
for line in content.splitlines():
|
||
if line.strip().startswith("# "):
|
||
title = line.strip()[2:].strip() or title
|
||
break
|
||
|
||
changed_section = cls._extract_markdown_section_lines(content, heading="Changed Files")
|
||
changed_files = [line[2:].strip() for line in changed_section if line.startswith("- ") and line[2:].strip()]
|
||
|
||
scope_section = cls._extract_markdown_section_lines(content, heading="Scope")
|
||
scope_items = [line[2:].strip() if line.startswith("- ") else line for line in scope_section]
|
||
scope_summary = ";".join([item for item in scope_items if item][:2]).strip()
|
||
if not scope_summary:
|
||
scope_summary = "从 docs/task_briefs 自动导入"
|
||
|
||
run = {
|
||
"id": run_id,
|
||
"brief_id": brief_id,
|
||
"title": title,
|
||
"template_id": DEFAULT_TEMPLATE_ID,
|
||
"template_name": DEFAULT_TEMPLATE_NAME,
|
||
"generated_at": generated_at,
|
||
"generated_by": 0,
|
||
"status": "imported",
|
||
"changed_files": changed_files,
|
||
"scope_summary": scope_summary,
|
||
}
|
||
brief = {
|
||
"id": brief_id,
|
||
"title": title,
|
||
"status": "ready",
|
||
"created_at": generated_at,
|
||
"updated_at": generated_at,
|
||
"latest_run_id": run_id,
|
||
"template_id": DEFAULT_TEMPLATE_ID,
|
||
"template_name": DEFAULT_TEMPLATE_NAME,
|
||
"summary": scope_summary,
|
||
"content": content.strip(),
|
||
"changed_files": changed_files,
|
||
}
|
||
runs.append(run)
|
||
briefs.append(brief)
|
||
return briefs, runs
|
||
|