rd13_copilot_setup/git-templates/hooks/pre-commit
Conrad Schulz 2b20a985a5 refactor(history): move agent history under docs/history + auto-migration
Konvention geaendert: history/ -> docs/history/ (prompts + summary/PROJECT_CONTEXT.md).
Harter Cutover im pre-commit Hook (Check 4 + Check 6 erwarten docs/history/).
Bestehende Repos werden beim naechsten 'git copilot-update' automatisch per git mv migriert (Fallback mv; bei Konflikt Warnung statt Abbruch).
Angepasst: pre-commit Hook, alle 6 Skripte + selftest, beide copilot-instructions.md, settings.json Session-Protokoll, history.prompt.md, README, USER/ADMIN/MAINTAINER (+ ADMIN Migrationsabschnitt). git-templates/history -> git-templates/docs/history (git mv). Validiert: shellcheck clean, fish -n clean, selftest PASS, Migrationstest PASS (sh+fish+both-present).
2026-06-10 12:06:35 +02:00

184 lines
9.5 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env sh
# pre-commit Agent Quality Gate
#
# Prüft bei jeder Code-Änderung ob:
# 1. Tests erstellt oder aktualisiert wurden
# 2. Mindestens eine Zielgruppen-Dokumentation aktualisiert wurde
# 3. Alle 3 Dokumentations-Zielgruppen vorhanden sind (USER/ADMIN/MAINTAINER)
# 4. docs/history/summary/PROJECT_CONTEXT.md aktualisiert wurde
# 5. docs/requirements/REQUIREMENTS.md keine unstaged Änderungen hat
# 6. *_session.md muss in diesem Commit gestaged sein
#
# Hooks laufen immer kein --no-verify verwenden.
STAGED=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null)
ERRORS=0
# Quellcode-Dateien (keine Tests, keine Konfiguration, keine Docs, keine Daten)
CODE_CHANGED=$(printf '%s\n' "$STAGED" \
| grep -E '\.(py|js|ts|mjs|cjs|go|java|rs|rb|php|c|cpp|h|hpp|cs|kt|swift|sh|bash)$' \
| grep -v -E '(test|spec|__tests__|_test)\.(py|js|ts|mjs|go|java|rs|rb|php|c|cpp|cs|kt|swift)$' \
| grep -v -E '^(docs/|\.github/|\.vscode/|data/)' \
| grep -v -E '_test\.go$')
# Keine Code-Änderungen → Hook überspringen
if [ -z "$CODE_CHANGED" ]; then
exit 0
fi
# ── Check 1: Tests ────────────────────────────────────────────────────────────
# Opt-out: Datei '.copilot-no-tests' im Repo-Root deaktiviert diesen Check
# (für Repos ohne Test-Framework, z.B. reine Script/Config-Repos)
TESTS_CHANGED=$(printf '%s\n' "$STAGED" \
| grep -E '(test|spec|__tests__|_test)\.(py|js|ts|mjs|go|java|rs|rb|php|c|cpp|cs|kt|swift)$|_test\.go$|test_.*\.py$')
if [ -z "$TESTS_CHANGED" ] && [ ! -f ".copilot-no-tests" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [1/4]: Keine Test-Dateien geändert"
echo " Code-Änderungen ohne Test-Updates:"
printf '%s\n' "$CODE_CHANGED" | sed 's/^/ /'
echo ""
echo " → Tests fehlen oder nicht als STAGED markiert"
echo " → Copilot Chat: /write-tests"
echo " → Kein Test-Framework vorhanden? Datei '.copilot-no-tests' anlegen um diesen Check zu deaktivieren"
ERRORS=$((ERRORS + 1))
fi
# ── Check 2: Dokumentation aktualisiert ───────────────────────────────────────
# Prüfen ob docs/ Verzeichnis überhaupt existiert
# Opt-out: Datei '.copilot-no-docs' im Repo-Root deaktiviert diesen Check
# (für reine Config/Script-Repos ohne nutzerseitige Dokumentationspflicht)
if [ -d "docs" ] && [ ! -f ".copilot-no-docs" ]; then
DOCS_CHANGED=$(printf '%s\n' "$STAGED" \
| grep -E '(^docs/USER\.md$|^docs/ADMIN\.md$|^docs/MAINTAINER\.md$|^README\.md$)')
if [ -z "$DOCS_CHANGED" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [2/4]: Keine Dokumentation aktualisiert"
echo " Bei Code-Änderungen muss mindestens eine dieser Dateien angepasst werden:"
echo " docs/USER.md Nutzer-Dokumentation"
echo " docs/ADMIN.md Administrator-Dokumentation"
echo " docs/MAINTAINER.md Entwickler/Maintainer-Dokumentation"
echo " README.md"
echo ""
echo " → Copilot Chat: Frage den Agent die relevante Doku zu aktualisieren"
ERRORS=$((ERRORS + 1))
fi
fi
# ── Check 3: Vollständigkeit der 3 Zielgruppen ────────────────────────────────
# Nur prüfen wenn docs/ neu angelegt wird (noch nicht vollständig) oder ein
# neues Feature ein neues docs/-File staged hat.
DOCS_DIR_STAGED=$(printf '%s\n' "$STAGED" | grep -E '^docs/' | head -1)
if [ -n "$DOCS_DIR_STAGED" ] && [ -d "docs" ]; then
MISSING=""
[ ! -f "docs/USER.md" ] && MISSING="${MISSING}\n docs/USER.md (fehlt für Endnutzer)"
[ ! -f "docs/ADMIN.md" ] && MISSING="${MISSING}\n docs/ADMIN.md (fehlt für Administratoren)"
[ ! -f "docs/MAINTAINER.md" ] && MISSING="${MISSING}\n docs/MAINTAINER.md (fehlt für Entwickler)"
if [ -n "$MISSING" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [3/4]: Fehlende Zielgruppen-Dokumentation"
printf " Nicht vorhanden:%b\n" "$MISSING"
echo ""
echo " Alle 3 Zielgruppen-Dokumente müssen existieren."
echo " Templates: docs/USER.md, docs/ADMIN.md, docs/MAINTAINER.md"
ERRORS=$((ERRORS + 1))
fi
fi
# ── Check 4: PROJECT_CONTEXT.md aktualisiert ──────────────────────────────────
if [ -f "docs/history/summary/PROJECT_CONTEXT.md" ]; then
CONTEXT_STAGED=$(printf '%s\n' "$STAGED" | grep -F 'docs/history/summary/PROJECT_CONTEXT.md')
if [ -z "$CONTEXT_STAGED" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [4/4]: PROJECT_CONTEXT.md nicht aktualisiert"
echo " Bei Code-Änderungen muss der Agent-Kontext aktuell gehalten werden."
echo " docs/history/summary/PROJECT_CONTEXT.md"
echo ""
echo " → Copilot Chat: /history (loggt Session + aktualisiert Kontext)"
ERRORS=$((ERRORS + 1))
fi
fi
# ── Check 5: Requirements-Konsistenz ─────────────────────────────────────────
# Wenn docs/requirements/REQUIREMENTS.md existiert und unstaged Änderungen hat,
# muss es vor dem Commit gestaged werden.
# Opt-out: Datei '.copilot-no-requirements' im Repo-Root deaktiviert diesen Check.
if [ -f "docs/requirements/REQUIREMENTS.md" ] && [ ! -f ".copilot-no-requirements" ]; then
REQ_UNSTAGED=$(git diff --name-only -- "docs/requirements/REQUIREMENTS.md" 2>/dev/null)
if [ -n "$REQ_UNSTAGED" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [5/5]: Requirements-Datei hat unstaged Änderungen"
echo " docs/requirements/REQUIREMENTS.md wurde geändert aber nicht gestaged."
echo ""
echo " → git add docs/requirements/REQUIREMENTS.md"
echo " → Copilot Chat: /requirements (Requirements aktualisieren + konsistenz prüfen)"
ERRORS=$((ERRORS + 1))
fi
fi
# ── Check 6: Session-Datei muss in diesem Commit gestaged sein ─────────────
# Jeder Commit muss eine *_session.md enthalten die laufende Konversation
# soll immer dokumentiert werden. Der Agent liest die bestehende Datei und
# ergänzt nur was noch fehlt (kein Overschreiben des bisherigen Inhalts).
# Opt-out: Datei '.copilot-no-session' für Repos ohne Chat-Kontext (z.B. CI).
if [ -d "docs/history/prompts" ] && [ ! -f ".copilot-no-session" ]; then
SESSION_STAGED=$(printf '%s\n' "$STAGED" | grep -E 'docs/history/prompts/.*_session\.md$' | head -1)
if [ -z "$SESSION_STAGED" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [6/6]: Keine Session-Datei in diesem Commit"
echo " Jeder Commit muss eine *_session.md enthalten."
echo ""
echo " → Copilot Chat: /history (ergänzt bestehende Session-Datei + stagt sie)"
echo " → Kein Chat-Kontext (z.B. CI/manuell)? Opt-out anlegen:"
echo " touch .copilot-no-session && git add .copilot-no-session"
ERRORS=$((ERRORS + 1))
else
# Dateiname muss heutiges Datum enthalten (YYYY-MM-DD)
TODAY=$(date +%Y-%m-%d)
SESSION_DATE=$(basename "$SESSION_STAGED" | grep -o '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}')
if [ "$SESSION_DATE" != "$TODAY" ]; then
echo ""
echo "✗ AGENT QUALITY GATE [6/6]: Session-Datei hat nicht das heutige Datum"
echo " Gestagt: $SESSION_STAGED"
echo " Erwartet: Dateiname beginnt mit $TODAY"
echo ""
echo " → Neue Session-Datei für heute anlegen: docs/history/prompts/${TODAY}_beschreibung_session.md"
ERRORS=$((ERRORS + 1))
else
# Datei muss mindestens einen ### Prompt-Eintrag enthalten
# (verhindert reine 'Nachtrag'-Blöcke ohne echte Prompt-Dokumentation)
SESSION_FILE_CONTENT=$(git show ":$SESSION_STAGED" 2>/dev/null)
PROMPT_COUNT=$(printf '%s\n' "$SESSION_FILE_CONTENT" | grep -c '^### Prompt')
if [ "$PROMPT_COUNT" -eq 0 ]; then
echo ""
echo "✗ AGENT QUALITY GATE [6/6]: Session-Datei enthält keine '### Prompt'-Einträge"
echo " Gestagt: $SESSION_STAGED"
echo ""
echo " Jede Session-Datei muss die Benutzer-Prompts im Format:"
echo " ### Prompt N"
echo " «wörtlicher Benutzertext»"
echo " ### Antwort N"
echo " «Zusammenfassung der Agent-Antwort»"
echo ""
echo " → Copilot Chat: /history (ergänzt Session-Datei korrekt)"
ERRORS=$((ERRORS + 1))
fi
fi
fi
fi
# ── Ergebnis ──────────────────────────────────────────────────────────────────
if [ "$ERRORS" -gt 0 ]; then
echo ""
echo "══════════════════════════════════════════════════════════"
echo " $ERRORS von 6 Quality Gate(s) nicht bestanden."
echo " Behebe die Probleme bevor du committst."
echo " ══════════════════════════════════════════════════════════"
echo ""
exit 1
fi
exit 0