Files
awoooi/scripts/aider_watch_client/aider_watch_client/parsers.py

89 lines
2.7 KiB
Python

# aider-watch-client parsers | 2026-04-20 @ Asia/Taipei
"""從 aider stdout / chat_history.md / git diff 抽事件資訊。"""
from __future__ import annotations
import re
import subprocess
from pathlib import Path
_BANNER_MODEL = re.compile(r"^\s*Model:\s*(\S+)", re.MULTILINE)
_BANNER_VER = re.compile(r"^\s*Aider v([\d\.]+)", re.MULTILINE)
_TOKENS_LINE = re.compile(
r"Tokens:\s+([\d\.]+)([km]?)\s+sent,\s+([\d\.]+)([km]?)\s+received",
re.IGNORECASE,
)
def _parse_num(n: str, suffix: str) -> int:
v = float(n)
if suffix.lower() == "k":
v *= 1000
elif suffix.lower() == "m":
v *= 1_000_000
return int(v)
def parse_stdout_banner(stdout: str) -> dict:
m_model = _BANNER_MODEL.search(stdout)
m_ver = _BANNER_VER.search(stdout)
return {
"model": m_model.group(1) if m_model else None,
"cli_version": m_ver.group(1) if m_ver else None,
}
def parse_chat_history(md: str) -> dict:
total_sent = 0
total_recv = 0
for m in _TOKENS_LINE.finditer(md):
total_sent += _parse_num(m.group(1), m.group(2))
total_recv += _parse_num(m.group(3), m.group(4))
return {"tokens_sent": total_sent, "tokens_received": total_recv,
"file_edits": []}
def parse_git_diff_stat(diff_stat: str) -> list[dict]:
out = []
for line in diff_stat.strip().splitlines():
parts = line.split("\t")
if len(parts) != 3:
continue
add, dele, path = parts
try:
a = int(add) if add != "-" else 0
d = int(dele) if dele != "-" else 0
except ValueError:
continue
out.append({"path": path, "lines_added": a, "lines_deleted": d,
"is_new_file": d == 0 and a > 0})
return out
def git_numstat(cwd: Path, pre_sha: str, post_sha: str = "HEAD") -> list[dict]:
try:
r = subprocess.run(
["git", "-C", str(cwd), "diff", "--numstat", pre_sha, post_sha],
capture_output=True, text=True, timeout=10)
if r.returncode != 0:
return []
return parse_git_diff_stat(r.stdout)
except Exception:
return []
def git_diff_head(cwd: Path, path: str, pre_sha: str, n: int = 3) -> list[str]:
try:
r = subprocess.run(
["git", "-C", str(cwd), "diff", "-U0", pre_sha, "--", path],
capture_output=True, text=True, timeout=5)
picks = []
for l in r.stdout.splitlines():
if l.startswith("+++") or l.startswith("---"):
continue
if l.startswith("@@") or l.startswith("+") or l.startswith("-"):
picks.append(l)
if len(picks) >= n:
break
return picks
except Exception:
return []