update_agents_structure.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """Compare AGENTS.md directory structure with actual files."""
  4. import os
  5. import re
  6. import sys
  7. REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  8. ISSUE_BODY_FILE = os.path.join(REPO_ROOT, ".github", "issue_body.md")
  9. def scan_files(directory, extensions):
  10. # type: (str, tuple) -> set
  11. """Scan directory for files with given extensions."""
  12. result = set()
  13. base = os.path.join(REPO_ROOT, directory)
  14. if not os.path.isdir(base):
  15. return result
  16. for root, dirs, files in os.walk(base):
  17. dirs[:] = [d for d in dirs if not d.startswith(".") and d != "__pycache__"]
  18. for f in files:
  19. if not f.startswith(".") and f.endswith(extensions):
  20. result.add(os.path.relpath(os.path.join(root, f), REPO_ROOT).replace(os.sep, "/"))
  21. return result
  22. def parse_agents_md():
  23. # type: () -> set
  24. """Extract file paths from AGENTS.md directory structure (Tab-indented)."""
  25. agents_file = os.path.join(REPO_ROOT, "AGENTS.md")
  26. if not os.path.exists(agents_file):
  27. print("Error: AGENTS.md not found")
  28. sys.exit(1)
  29. with open(agents_file, "r", encoding="utf-8") as f:
  30. content = f.read()
  31. match = re.search(r"### Directory Structure.*?```text\s*\n(.*?)```\s*", content, re.DOTALL)
  32. if not match:
  33. return set()
  34. files = set()
  35. stack = [] # type: list
  36. for line in match.group(1).split("\n"):
  37. if not line.strip():
  38. continue
  39. depth = len(line) - len(line.lstrip("\t"))
  40. name = line.lstrip("\t").split(":")[0].strip()
  41. if not name or "*" in name or name == "...":
  42. continue
  43. stack = stack[:depth]
  44. if name.endswith("/"):
  45. stack.append(name.rstrip("/"))
  46. else:
  47. path = "/".join(stack + [name])
  48. if path.startswith(("ddns/", "doc/", "schema/")) and not path.endswith((".png", ".svg", ".jpg", ".gif", ".ico")):
  49. files.add(path)
  50. return files
  51. def main():
  52. # type: () -> None
  53. actual = (
  54. scan_files("ddns", (".py")) | scan_files("doc", (".md",)) | scan_files("schema", (".json",))
  55. )
  56. documented = parse_agents_md()
  57. added, deleted = sorted(actual - documented), sorted(documented - actual)
  58. # Remove old issue body file if exists
  59. if os.path.exists(ISSUE_BODY_FILE):
  60. os.remove(ISSUE_BODY_FILE)
  61. if not added and not deleted:
  62. print("No changes detected")
  63. sys.exit(0)
  64. # Build and write issue body
  65. lines = ["AGENTS.md directory structure is out of sync.\n"]
  66. if added:
  67. lines.append("## New Files\n")
  68. lines.extend("- `%s`" % f for f in added)
  69. lines.append("")
  70. if deleted:
  71. lines.append("## Missing Files\n")
  72. lines.extend("- `%s`" % f for f in deleted)
  73. lines.append("")
  74. lines.append("## Required Updates\n1. Update directory structure\n2. Update version/date")
  75. lines.append("\n---\n*Auto-generated by update-agents workflow.*")
  76. with open(ISSUE_BODY_FILE, "w", encoding="utf-8") as f:
  77. f.write("\n".join(lines))
  78. print("Changes detected: %d new, %d missing" % (len(added), len(deleted)))
  79. if __name__ == "__main__":
  80. main()