_recover_from_transcript.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/env python3
  2. """Replay Write/StrReplace tool calls from agent transcript onto repo."""
  3. from __future__ import annotations
  4. import json
  5. import re
  6. import sys
  7. from pathlib import Path
  8. REPO = Path(__file__).resolve().parents[1]
  9. TRANSCRIPT = Path(
  10. r"C:\Users\skygu\.cursor\projects\d-Projects-Ai-DOP-SourceCode-ZZYDOP"
  11. r"\agent-transcripts\09f6a7e3-4c30-4108-b1d7-f3d16f6412f6"
  12. r"\09f6a7e3-4c30-4108-b1d7-f3d16f6412f6.jsonl"
  13. )
  14. PREFIXES = ("Web", "server", "doc")
  15. def to_rel(path: str) -> str | None:
  16. p = path.replace("/", "\\")
  17. m = re.search(r"ZZYDOP[\\/](.+)$", p, re.I)
  18. if not m:
  19. return None
  20. rel = m.group(1)
  21. if rel.startswith(PREFIXES):
  22. return rel
  23. return None
  24. def iter_ops():
  25. text = TRANSCRIPT.read_text(encoding="utf-8", errors="replace")
  26. for line in text.splitlines():
  27. try:
  28. obj = json.loads(line)
  29. except json.JSONDecodeError:
  30. continue
  31. msg = obj.get("message", {})
  32. content = msg.get("content", [])
  33. if not isinstance(content, list):
  34. continue
  35. for part in content:
  36. if not isinstance(part, dict) or part.get("type") != "tool_use":
  37. continue
  38. name = part.get("name")
  39. inp = part.get("input", {})
  40. if not isinstance(inp, dict):
  41. continue
  42. rel = to_rel(inp.get("path", ""))
  43. if not rel:
  44. continue
  45. if name == "Write":
  46. yield ("write", rel, inp.get("contents", ""))
  47. elif name == "StrReplace":
  48. yield (
  49. "replace",
  50. rel,
  51. inp.get("old_string", ""),
  52. inp.get("new_string", ""),
  53. bool(inp.get("replace_all")),
  54. )
  55. def main() -> int:
  56. writes = 0
  57. replaces_ok = 0
  58. replaces_fail: list[str] = []
  59. for op in iter_ops():
  60. kind = op[0]
  61. rel = op[1]
  62. target = REPO / rel
  63. if kind == "write":
  64. contents = op[2]
  65. target.parent.mkdir(parents=True, exist_ok=True)
  66. target.write_text(contents, encoding="utf-8", newline="\n")
  67. writes += 1
  68. print(f"WRITE {rel}")
  69. continue
  70. old, new, replace_all = op[2], op[3], op[4]
  71. if not target.is_file():
  72. replaces_fail.append(f"{rel}: file missing")
  73. continue
  74. try:
  75. text = target.read_text(encoding="utf-8")
  76. except UnicodeDecodeError:
  77. replaces_fail.append(f"{rel}: not utf-8 text")
  78. continue
  79. if old not in text:
  80. replaces_fail.append(f"{rel}: old_string not found")
  81. continue
  82. if replace_all:
  83. text = text.replace(old, new)
  84. else:
  85. text = text.replace(old, new, 1)
  86. target.write_text(text, encoding="utf-8", newline="\n")
  87. replaces_ok += 1
  88. print(f"REPLACE {rel}")
  89. print(f"\nDone: writes={writes}, replaces_ok={replaces_ok}, replaces_fail={len(replaces_fail)}")
  90. for item in replaces_fail[:40]:
  91. print(f" FAIL {item}")
  92. if len(replaces_fail) > 40:
  93. print(f" ... and {len(replaces_fail) - 40} more")
  94. return 0 if not replaces_fail else 1
  95. if __name__ == "__main__":
  96. sys.exit(main())