96 lines
2.3 KiB
Python
96 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Require PLAN/PROGRESS handoff discipline for multi-agent work."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
TRACKED_PREFIXES = (
|
|
".agents/",
|
|
".codex/",
|
|
"AGENTS.md",
|
|
"PLAN.md",
|
|
"docs/",
|
|
"phases/",
|
|
"plugins/",
|
|
"requirements.txt",
|
|
"scripts/",
|
|
"src/",
|
|
"tests/",
|
|
)
|
|
|
|
|
|
def git_status_names(root: Path) -> list[str]:
|
|
result = subprocess.run(
|
|
["git", "status", "--porcelain"],
|
|
cwd=root,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=20,
|
|
)
|
|
if result.returncode != 0:
|
|
return []
|
|
|
|
names: list[str] = []
|
|
for line in result.stdout.splitlines():
|
|
if not line.strip():
|
|
continue
|
|
path = line[3:].replace("\\", "/")
|
|
if " -> " in path:
|
|
path = path.split(" -> ", 1)[1]
|
|
names.append(path)
|
|
return names
|
|
|
|
|
|
def is_coordination_relevant(path: str) -> bool:
|
|
return any(path == prefix or path.startswith(prefix) for prefix in TRACKED_PREFIXES)
|
|
|
|
|
|
def block(reason: str) -> int:
|
|
json.dump({"decision": "block", "reason": reason}, sys.stdout)
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
try:
|
|
payload = json.load(sys.stdin)
|
|
except json.JSONDecodeError:
|
|
return 0
|
|
|
|
if payload.get("stop_hook_active"):
|
|
return 0
|
|
|
|
root = Path(payload.get("cwd") or ".").resolve()
|
|
plan = root / "PLAN.md"
|
|
progress = root / "PROGRESS.md"
|
|
|
|
if not plan.exists() or not progress.exists():
|
|
return block(
|
|
"Multi-agent coordination requires PLAN.md and PROGRESS.md. "
|
|
"Create or restore both files before ending the turn."
|
|
)
|
|
|
|
changed = git_status_names(root)
|
|
if not changed:
|
|
return 0
|
|
|
|
relevant = [path for path in changed if is_coordination_relevant(path)]
|
|
progress_changed = "PROGRESS.md" in changed
|
|
|
|
if relevant and not progress_changed:
|
|
return block(
|
|
"Repository planning, docs, code, tests, requirements, or .codex files changed, "
|
|
"but PROGRESS.md was not updated. Add a concise handoff note so the next agent "
|
|
"can see what changed, what was verified, and what remains next."
|
|
)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|