#!/usr/bin/env python3 """audit_reminder — Agent 结束时检查是否有待审计改动,15 分钟限频提醒。 替代原 PowerShell 版本,避免 Windows PowerShell 5.1 解析器 bug。 """ import json import os import subprocess import sys from datetime import datetime, timezone, timedelta TZ_TAIPEI = timezone(timedelta(hours=8)) STATE_PATH = os.path.join(".kiro", "state", ".audit_state.json") MIN_INTERVAL = timedelta(minutes=15) def now_taipei(): return datetime.now(TZ_TAIPEI) def load_state(): if not os.path.isfile(STATE_PATH): return None try: with open(STATE_PATH, "r", encoding="utf-8") as f: return json.load(f) except Exception: return None def save_state(state): os.makedirs(os.path.join(".kiro", "state"), exist_ok=True) with open(STATE_PATH, "w", encoding="utf-8") as f: json.dump(state, f, indent=2, ensure_ascii=False) def get_real_changes(): """获取排除噪声后的变更文件""" try: r = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True, timeout=10) if r.returncode != 0: return [] except Exception: return [] files = [] for line in r.stdout.splitlines(): if len(line) < 4: continue path = line[3:].strip().strip('"').replace("\\", "/") if " -> " in path: path = path.split(" -> ")[-1] # 排除审计产物、.kiro 配置、临时文件 if path and not path.startswith("docs/audit/") and not path.startswith(".kiro/") and not path.startswith("tmp/") and not path.startswith(".hypothesis/"): files.append(path) return sorted(set(files)) def main(): state = load_state() if not state: sys.exit(0) if not state.get("audit_required"): sys.exit(0) # 工作树干净时清除审计状态 real_files = get_real_changes() if not real_files: state["audit_required"] = False state["reasons"] = [] state["changed_files"] = [] state["last_reminded_at"] = None save_state(state) sys.exit(0) now = now_taipei() # 15 分钟限频 last_str = state.get("last_reminded_at") if last_str: try: last = datetime.fromisoformat(last_str) if (now - last) < MIN_INTERVAL: sys.exit(0) except Exception: pass # 更新提醒时间 state["last_reminded_at"] = now.isoformat() save_state(state) reasons = state.get("reasons", []) reason_text = ", ".join(reasons) if reasons else "high-risk paths changed" sys.stderr.write( f"[AUDIT REMINDER] Pending audit detected ({reason_text}). " f"Run /audit (Manual: Run /audit hook) to sync docs & write audit artifacts. " f"(rate limit: 15min)\n" ) sys.exit(1) if __name__ == "__main__": try: main() except Exception: sys.exit(0)