import csv, hashlib, os, re
from pathlib import Path
root=Path('/opt/struktur/hermes-werkstandard/data/skills'); carlo=Path('/opt/struktur/hermes-carlo/data/skills')
def sha(p): return hashlib.sha256(p.read_bytes()).hexdigest()
def dirs(base):
return {p.relative_to(base).as_posix():p for p in base.rglob('SKILL.md')}
w,c=dirs(root),dirs(carlo)
rows=[]
for rel in sorted(set(w)|set(c)):
if rel not in w or rel not in c: kind='einzigartig' if rel in w else 'nur Carlo'
else:
if sha(w[rel])==sha(c[rel]): continue
kind='abweichend'
if kind=='nur Carlo': continue
p=w.get(rel) or c.get(rel); text=p.read_text(errors='ignore') if p else ''
low=(rel+' '+text).lower()
files=sum(1 for _ in p.parent.rglob('*'))
deps=[]
for pat,label in [(r'python|pip|venv','Python'),(r'node|npm|pnpm','Node'),(r'docker','Docker'),(r'mcp','MCP'),(r'api key|token|oauth|credential','Credentials'),(r'http://|https://','External HTTP'),(r'write|create|update|delete|remove|restart|install','Write/action')]:
if re.search(pat,low): deps.append(label)
risk=[]
for pat,label in [(r'api key|token|password|secret|credential|oauth','secrets/auth'),(r'docker socket|/var/run/docker','Docker socket'),(r'rm -rf|delete|remove|destroy','destructive commands'),(r'ssh|sudo|root','privileged/remote'),(r'upload|send|webhook|external','external transfer')]:
if re.search(pat,low): risk.append(label)
if any(x in low for x in ['vps','docker','nginx','backup','security','diagnos','debug','database','git','test','quality']): pri='1'
elif any(x in low for x in ['air','humid','pdf','image','email','obsidian','graphiti','website','wordpress','crm','whatsapp','ocr']): pri='2'
elif any(x in low for x in ['agent','mcp','workflow','research','documentation','software','project']): pri='3'
else: pri='4'
if kind=='abweichend': decision='ungeklärt'; reason='Abweichender gemeinsamer Skill: Carlo- und Werkstandard-Version müssen inhaltlich/versionsbezogen gegeneinander geprüft werden; keine automatische Zusammenführung.'
elif 'destructive commands' in risk or 'Docker socket' in risk or 'secrets/auth' in risk and 'external transfer' in risk: decision='Sicherheitsrisiko'; reason='Statische Indikatoren für privilegierte, destruktive, geheime oder externe Aktionen; erst nach Sicherheitsreview.'
else: decision='archivieren'; reason='Potenziell eigener Skillbestand; aktuell keine Ausführung oder Kompatibilitätsfreigabe, daher Original erhalten und später isoliert prüfen.'
function=(next((x.strip() for x in text.splitlines() if x.strip() and not x.startswith('#')), 'Dokumentierter Skill; Kurzfunktion aus vollständigem SKILL.md separat zu prüfen.'))[:240]
rows.append({'Skill':rel,'Kategorie':kind,'Werkstandard-Pfad':str(w[rel]) if rel in w else '','Carlo-Pfad':str(c[rel]) if rel in c else '','einzigartig oder abweichend':kind,'Kurzfunktion':function,'Nutzen Mallorca AirServices':'potenziell; domänenabhängig','Nutzen Agent Solutions':'potenziell; domänenabhängig','Infrastrukturwert':'hoch' if pri=='1' else 'mittel' if pri in ('2','3') else 'unklar','technischer Reifegrad':'3/5 statisch','Kompatibilität':'ungeklärt' if kind=='abweichend' else 'nicht ausgeführt','Sicherheit':'prüfen: '+(', '.join(risk) if risk else 'keine offensichtlichen Indikatoren'),'Wartbarkeit':'prüfen','Abhängigkeiten':', '.join(deps) or 'nicht erkennbar','notwendige Anpassungen':'Einzeltest, Pfad-/Abhängigkeitsprüfung' if kind=='einzigartig' else 'Versionsvergleich und gezielter Diff','Entscheidung':decision,'Begründung':reason,'Priorität':pri,'Risiko':'hoch' if risk else 'mittel'})
out=Path('/tmp/SKILL-EINZELPRUEFUNG.csv'); fields=list(rows[0]) if rows else []
with out.open('w',newline='',encoding='utf-8') as f:
wr=csv.DictWriter(f,fieldnames=fields); wr.writeheader(); wr.writerows(rows)
from collections import Counter
print('rows',len(rows)); print('kind',Counter(r['Kategorie'] for r in rows)); print('decision',Counter(r['Entscheidung'] for r in rows)); print('priority',Counter(r['Priorität'] for r in rows))