Files
vibe-coding-cn/scripts/check-directory-docs.py
tradecatlabs f1f0b9fe75 refactor: move research domain to repository root (#58)
Move the complete research domain from docs/research to root-level research, update indexes and path-aware tooling, and align raw snapshot governance and CODEOWNERS.\n\nVerified with make test and GitHub CI.
2026-07-11 22:18:37 +08:00

129 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Check repository-owned directories have README.md and AGENTS.md."""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REQUIRED_DIRS = [
Path(".github"),
Path(".github/ISSUE_TEMPLATE"),
Path(".github/workflows"),
Path("assets"),
Path("assets/ai-citation"),
Path("assets/datasets"),
Path("assets/images"),
Path("assets/templates"),
Path("docs"),
Path("docs/concepts"),
Path("docs/getting-started"),
Path("docs/philosophy"),
Path("docs/references"),
Path("research"),
Path("docs/workflow"),
Path("metadata"),
Path("prompts"),
Path("scripts"),
Path("scripts/lib"),
Path("skills"),
Path("skills/auto-skill"),
Path("skills/auto-skill/assets"),
Path("skills/auto-skill/references"),
Path("skills/auto-skill/scripts"),
Path("tools"),
Path("tools/chat-vault"),
Path("tools/config"),
Path("tools/config/.codex"),
Path("tools/external"),
Path("tools/prompts-library"),
Path("tools/prompts-library/docs"),
Path("tools/prompts-library/scripts"),
]
README_OPTIONAL_DIRS = {
# GitHub repository pages can surface `.github/README.md` as an overview file.
# Keep the root project README authoritative and use `.github/AGENTS.md` only.
Path(".github"),
}
GENERATED_OR_VENDOR_DIRS = [
Path("build"),
Path("node_modules"),
]
SKIP_PARTS = {".git", ".history", "build", "node_modules", "__pycache__"}
SKIP_PREFIXES = [
Path(".github/wiki"),
]
VENDOR_SUBTREES = [
Path("tools/external"),
Path("tools/chat-vault"),
]
def should_skip(directory: Path) -> bool:
rel = directory.relative_to(ROOT)
if any(part in SKIP_PARTS for part in rel.parts):
return True
if is_research_repository_snapshot(rel):
return True
if directory.is_symlink():
return True
if any(rel == prefix or prefix in rel.parents for prefix in SKIP_PREFIXES):
return True
return any(vendor in rel.parents for vendor in VENDOR_SUBTREES)
def is_research_repository_snapshot(rel: Path) -> bool:
parts = rel.parts
return (
len(parts) >= 4
and parts[0] == "research"
and parts[2] == "raw"
and parts[3] == "repository"
)
def repository_owned_dirs() -> list[Path]:
dirs = set(REQUIRED_DIRS)
for directory in ROOT.rglob("*"):
if not directory.is_dir():
continue
if should_skip(directory):
continue
dirs.add(directory.relative_to(ROOT))
return sorted(dirs)
def main() -> int:
errors: list[str] = []
for rel_dir in repository_owned_dirs():
directory = ROOT / rel_dir
if not directory.is_dir():
errors.append(f"{rel_dir}: required directory is missing")
continue
required_files = ["AGENTS.md"] if rel_dir in README_OPTIONAL_DIRS else ["README.md", "AGENTS.md"]
for filename in required_files:
if not (directory / filename).is_file():
errors.append(f"{rel_dir}/{filename}: missing required directory document")
for rel_dir in GENERATED_OR_VENDOR_DIRS:
directory = ROOT / rel_dir
if directory.exists() and not (ROOT / ".gitignore").read_text(encoding="utf-8").count(str(rel_dir)):
errors.append(f"{rel_dir}: generated directory exists but is not ignored")
if errors:
print("DIRECTORY_DOC_ERRORS")
for error in errors:
print(error)
print(f"TOTAL={len(errors)}")
return 1
print(f"OK directory README/AGENTS pairs checked: {len(repository_owned_dirs())} directories")
return 0
if __name__ == "__main__":
sys.exit(main())