reconstruct_admin.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Reconstruct admin.html from JSONL conversation transcript.
  5. Only applies operations that succeeded (matched tool_result with no error).
  6. """
  7. import json
  8. import sys
  9. JSONL_PATH = r'C:/Users/Administrator/.claude/projects/h--lab-safety-monitor-exam-main-2026/369b78fb-8697-4ea6-b83e-eeb87cee0bba.jsonl'
  10. OUTPUT_PATH = r'H:/lab-safety-monitor/exam_main_2026/admin.html'
  11. # --- Pass 1: collect tool_use items for admin.html, keyed by tool_use_id ---
  12. tool_uses = {} # tool_use_id -> {'ts', 'name', 'input', 'order'}
  13. order = 0
  14. tool_results = {} # tool_use_id -> {'ts', 'is_error', 'content'}
  15. with open(JSONL_PATH, 'r', encoding='utf-8') as f:
  16. for raw_line in f:
  17. raw_line = raw_line.strip()
  18. if not raw_line:
  19. continue
  20. try:
  21. obj = json.loads(raw_line)
  22. except Exception:
  23. continue
  24. ts = obj.get('timestamp', '')
  25. msg = obj.get('message', {})
  26. if not isinstance(msg, dict):
  27. continue
  28. content = msg.get('content', [])
  29. if not isinstance(content, list):
  30. continue
  31. for item in content:
  32. if not isinstance(item, dict):
  33. continue
  34. if item.get('type') == 'tool_use':
  35. name = item.get('name', '')
  36. inp = item.get('input', {})
  37. fp = inp.get('file_path', '')
  38. if 'admin.html' in fp and name in ('Write', 'Edit'):
  39. tid = item.get('id', '')
  40. tool_uses[tid] = {
  41. 'ts': ts,
  42. 'name': name,
  43. 'input': inp,
  44. 'order': order,
  45. }
  46. order += 1
  47. elif item.get('type') == 'tool_result':
  48. tid = item.get('tool_use_id', '')
  49. is_error = item.get('is_error', False)
  50. tc = item.get('content', '')
  51. # Also check for error strings in content
  52. if isinstance(tc, list):
  53. tc_text = ' '.join(x.get('text', '') if isinstance(x, dict) else str(x) for x in tc)
  54. else:
  55. tc_text = str(tc)
  56. if '<tool_use_error>' in tc_text:
  57. is_error = True
  58. tool_results[tid] = {'ts': ts, 'is_error': is_error, 'content': tc_text}
  59. # Sort operations by original order
  60. ops = sorted(tool_uses.values(), key=lambda x: x['order'])
  61. print(f"Total admin.html operations: {len(ops)}")
  62. # Find the initial Write
  63. write_ops = [op for op in ops if op['name'] == 'Write']
  64. edit_ops = [op for op in ops if op['name'] == 'Edit']
  65. if not write_ops:
  66. print("ERROR: No Write operation found!")
  67. sys.exit(1)
  68. # Get the write that actually succeeded
  69. # Find tool_use_id for the write
  70. write_tid = None
  71. for tid, v in tool_uses.items():
  72. if v['name'] == 'Write' and 'admin.html' in v['input'].get('file_path', ''):
  73. r = tool_results.get(tid, {})
  74. if not r.get('is_error', True):
  75. write_tid = tid
  76. break
  77. if write_tid is None:
  78. print("WARNING: Write had no matching result, using it anyway")
  79. write_tid = [tid for tid, v in tool_uses.items() if v['name'] == 'Write'][0]
  80. file_content = tool_uses[write_tid]['input'].get('content', '')
  81. print(f"Initial Write content length: {len(file_content)}")
  82. # Apply edits in order, skipping failed ones
  83. skipped = 0
  84. applied = 0
  85. for tid, v in sorted([(tid, v) for tid, v in tool_uses.items() if v['name'] == 'Edit'],
  86. key=lambda x: x[1]['order']):
  87. result = tool_results.get(tid, {})
  88. if result.get('is_error', False):
  89. print(f" SKIP (original error): {v['ts']}")
  90. skipped += 1
  91. continue
  92. inp = v['input']
  93. old = inp.get('old_string', '')
  94. new = inp.get('new_string', '')
  95. replace_all = inp.get('replace_all', False)
  96. if replace_all:
  97. count = file_content.count(old)
  98. file_content = file_content.replace(old, new)
  99. print(f" replace_all {v['ts']}: {count} replacements, len={len(file_content)}")
  100. applied += 1
  101. else:
  102. count = file_content.count(old)
  103. if count == 0:
  104. print(f" WARNING not found: {v['ts']} old={repr(old[:80])}")
  105. # Still count as applied since original session said success
  106. elif count == 1:
  107. file_content = file_content.replace(old, new, 1)
  108. print(f" OK {v['ts']}: len={len(file_content)}")
  109. applied += 1
  110. else:
  111. # Multiple matches - replace first
  112. file_content = file_content.replace(old, new, 1)
  113. print(f" MULTI({count}) {v['ts']}: replaced first, len={len(file_content)}")
  114. applied += 1
  115. print(f"\nApplied: {applied}, Skipped: {skipped}")
  116. print(f"Final content length: {len(file_content)}")
  117. print(f"Final line count: {file_content.count(chr(10)) + 1}")
  118. with open(OUTPUT_PATH, 'w', encoding='utf-8') as f:
  119. f.write(file_content)
  120. print(f"\nWritten to: {OUTPUT_PATH}")