feat: export core Hermes skills
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit 待审查 and 任务交付 directories against tracker.
|
||||
|
||||
Usage:
|
||||
python3 ~/.hermes/skills/legal/contract-pass-workflow/references/cleanup-audit.py
|
||||
|
||||
Prints a matrix showing which files are:
|
||||
- In tracker (and their status/age/cleaned flag)
|
||||
- NOT in tracker (orphans that will never be auto-cleaned)
|
||||
- Should be cleaned (>24h + completed + cleaned=false)
|
||||
|
||||
Does NOT delete anything. Pure diagnostic.
|
||||
"""
|
||||
import json, os, subprocess
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
BJT = timezone(timedelta(hours=8))
|
||||
now = datetime.now(BJT)
|
||||
|
||||
TRACKER = os.path.expanduser('~/.hermes/data/contract-tracker.json')
|
||||
BASE = os.path.expanduser('~/nextcloud/data/data/doro/files/Doro合同审查任务')
|
||||
待审查 = os.path.join(BASE, '待审查')
|
||||
任务交付 = os.path.join(BASE, '任务交付')
|
||||
|
||||
def nc_ls(path):
|
||||
"""List files in a Nextcloud-managed directory (needs sudo)."""
|
||||
r = subprocess.run(['sudo', 'ls', path], capture_output=True, text=True)
|
||||
return [f for f in r.stdout.strip().split('\n') if f] if r.stdout.strip() else []
|
||||
|
||||
def file_age_hours(path):
|
||||
"""Get file age in hours from mtime."""
|
||||
r = subprocess.run(['sudo', 'stat', '-c', '%Y', path], capture_output=True, text=True)
|
||||
if r.stdout.strip():
|
||||
mtime = int(r.stdout.strip())
|
||||
return (now - datetime.fromtimestamp(mtime, tz=BJT)).total_seconds() / 3600
|
||||
return -1
|
||||
|
||||
def main():
|
||||
with open(TRACKER) as f:
|
||||
tracker = json.load(f)
|
||||
|
||||
# Build lookup: filename -> list of tracker records
|
||||
lookup = {}
|
||||
for c in tracker['contracts']:
|
||||
for key in ['delivered_filename', 'original_filename', 'converted_filename']:
|
||||
fn = c.get(key, '')
|
||||
if fn:
|
||||
lookup.setdefault(fn, []).append(c)
|
||||
|
||||
orphans = []
|
||||
|
||||
for label, directory in [('待审查', 待审查), ('任务交付', 任务交付)]:
|
||||
print(f"\n{'='*70}")
|
||||
print(f" {label} ({directory})")
|
||||
print(f"{'='*70}")
|
||||
files = nc_ls(directory)
|
||||
if not files:
|
||||
print(" (empty)")
|
||||
continue
|
||||
|
||||
for fn in sorted(files):
|
||||
records = lookup.get(fn, [])
|
||||
age = file_age_hours(os.path.join(directory, fn))
|
||||
is_companion = any(k in fn for k in ['审查意见', '合同流程单'])
|
||||
|
||||
if records:
|
||||
for r in records:
|
||||
ts = r.get('xlsx_updated_at', '')
|
||||
h = (now - datetime.fromisoformat(ts)).total_seconds() / 3600 if ts else -1
|
||||
should = r['status'] == 'completed' and h > 24
|
||||
flag = '🔴 SHOULD_CLEAN' if should else '⏳ waiting'
|
||||
print(f" {fn}")
|
||||
print(f" seq={r['seq']} | {h:.0f}h | cleaned={r.get('cleaned')} | {flag}")
|
||||
else:
|
||||
tag = '📋 COMPANION_ORPHAN' if is_companion else '⚠️ NOT_IN_TRACKER'
|
||||
print(f" {fn}")
|
||||
print(f" {tag} | age={age:.0f}h")
|
||||
orphans.append((label, fn, age))
|
||||
|
||||
if orphans:
|
||||
print(f"\n{'='*70}")
|
||||
print(f" ORPHANS SUMMARY: {len(orphans)} files not tracked")
|
||||
print(f"{'='*70}")
|
||||
for label, fn, age in orphans:
|
||||
print(f" [{label}] {fn} ({age:.0f}h old)")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user