utils/todo_hooks/pr_parse.py
Krzysztof kuhy Rudnicki aec96eefd3 Add todo_hooks closer, drift sweep, PR import
Python 3.14 stdlib package wiring git to the todo MCP server:
- closer (post-commit): runs the done_check of each Todo: trailer
  task and close_task's it only on exit 0. It FAILS OPEN: the commit
  already happened, so a down server, bad token or no gh prints one
  warning and exits 0 instead of failing the hook.
- sweep: nightly drift report (systemd units shipped, not enabled).
- import-pr: a merged PR's '## Todo:' items become tasks, deduped.
- A done_check runs only if its sha256 is in
  ~/.config/todo-hooks/trusted-checks; 'Check: manual' notes are
  never auto-closed.
- Hooks exposed in .pre-commit-hooks.yaml (post-commit, post-merge,
  post-rewrite).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 12:24:29 +01:00

44 lines
1.2 KiB
Python

"""Extract ``- title | done_check: cmd`` items from a PR's Todo section."""
from __future__ import annotations
from dataclasses import dataclass
import re
_HEADING = re.compile(r"^#{1,6}\s*todo:?\s*$", re.IGNORECASE)
_BULLET = re.compile(r"^\s*[-*]\s+(.*\S)\s*$")
_CHECK = re.compile(r"^(.*?)\s*\|\s*done_check:\s*(.*)$", re.IGNORECASE)
@dataclass(frozen=True)
class TodoItem:
"""One imported task: title and optional done_check."""
title: str
done_check: str | None
def parse_todo_items(body: str) -> list[TodoItem]:
"""Bullets under a ``## Todo:`` heading, up to the first non-bullet text."""
items: list[TodoItem] = []
inside = False
for row in body.splitlines():
if not inside:
inside = bool(_HEADING.match(row))
continue
bullet = _BULLET.match(row)
if bullet:
item = _item(bullet.group(1))
if item:
items.append(item)
elif row.strip():
break
return items
def _item(text: str) -> TodoItem | None:
found = _CHECK.match(text)
title, check = (found.group(1), found.group(2)) if found else (text, "")
title = title.strip()
return TodoItem(title, check.strip() or None) if title else None