Paweł Łukasiewicz: programista blogger
Paweł Łukasiewicz
2026-03-03
Paweł Łukasiewicz: programista blogger
Paweł Łukasiewicz
2026-03-03
Udostępnij Udostępnij Kontakt
Wprowadzenie

Przez ostatnie wpisy budowaliśmy coraz bardziej autonomicznego Copilota – instrukcje, skills, agenci, MCP serwery. Ale jest jedno fundamentalne ograniczenie wszystkich tych narzędzi: polegają na tym że AI coś zrobi, bo tak powiedziałeś. Copilot może zapomnieć uruchomić lintera. Agent może pominąć testy. Może – bo opiera się na rozumowaniu probabilistycznym.

Hooks rozwiązują ten problem nie przez lepsze instrukcje, ale przez deterministyczne wymuszenie. Hook nie prosi agenta żeby sformatował kod – hook po prostu uruchamia formatter i koniec, niezależnie od tego co agent myśli. Hook nie instruuje agenta żeby nie wykonywał niebezpiecznych komend – hook blokuje je na poziomie systemu zanim agent zdąży je wykonać.

To jest zmiana jakościowa w podejściu do automatyzacji: od "proszę rób X" do "X zawsze się wykona".

Oryginalna dokumentacja, na której bazuje ten wpis: Automating with Hooks – Awesome GitHub Copilot Learning Hub. Dokumentacja GitHub: About hooks – GitHub Docs.

Czym są Hooks i jak się różnią od reszty?

Hook to plik JSON w katalogu .github/hooks/ który mapuje zdarzenia sesji agenta na komendy shellowe. Gdy zdarzenie nastąpi – komenda się wykona. Synchronicznie, deterministycznie, niezależnie od tego co AI sobie myśli.

⚠️ Instrukcja / Skill / Agent
  • Opiera się na rozumowaniu AI
  • Probabilistyczny – może zadziałać, może nie
  • AI może "zapomnieć" lub pominąć krok
  • Nie może zablokować akcji agenta
  • Idealne dla złożonej logiki i kontekstu
✅ Hook
  • Wykonuje się poza modelem AI
  • Deterministyczny – odpala się zawsze
  • Nie zależy od "pamięci" agenta
  • Może blokować akcje agenta (preToolUse)
  • Idealne dla quality gates i guardrails
🔍 Gdzie działają Hooks – dwa środowiska
Środowisko Jak czyta konfigurację Uwagi
Copilot Coding Agent (GitHub.com) Z pliku .github/hooks/*.json na domyślnej gałęzi Zmiana hooka = commit na main/master. Wszystkie zmiany przechodzą przez code review.
Copilot CLI (lokalnie w VS Code) Z .github/hooks/*.json w bieżącym katalogu roboczym Jeden plik konfiguracyjny działa w obu środowiskach – brak duplikacji.

Konfiguracja repozytorium jako granica polityki – tak jak GitHub Actions. Hooks wchodzą do repozytorium razem z kodem, są reviewowane i rollbackowane przez git revert. To filozofia "infrastructure as code" zastosowana do polityki agenta.

Osiem zdarzeń – kiedy co się odpala

Hooks reagują na osiem zdarzeń z cyklu życia sesji agenta. Zrozumienie kiedy każde z nich odpala się jest kluczem do wyboru właściwego hooka dla danego zadania.

sessionStart MEDIUM

Sesja agenta się zaczyna lub wznawia. Idealne do: inicjalizacji środowiska, logowania startu sesji, walidacji stanu projektu (czy są wymagane narzędzia?), ustawiania zmiennych środowiskowych.

userPromptSubmitted MEDIUM

Użytkownik wysyła prompt do agenta. Treść promptu dostępna przez JSON na stdin. Idealne do: audytu promptów pod kątem compliance, skanowania w poszukiwaniu sekretów lub PII w zapytaniach, logowania dla enterprise governance.

preToolUse 🛡 BLOKUJE

Najsilniejszy hook. Odpala się przed każdym wywołaniem narzędzia przez agenta (bash, edit, terminal...). Jeśli hook zwróci kod niezerowy – narzędzie nie zostanie wykonane. Idealne do: blokowania niebezpiecznych komend, ochrony krytycznych plików, wymuszania approval dla operacji destruktywnych (DROP TABLE, rm -rf).

postToolUse HIGH

Po każdym wykonaniu narzędzia przez agenta. Dostępny wynik operacji. Idealne do: automatycznego formatowania kodu po edycji pliku, logowania każdej operacji narzędziowej, alertowania o nieoczekiwanych zmianach.

agentStop HIGH

Główny agent kończy odpowiedź na prompt. Idealne do: uruchomienia pełnego zestawu linterów i formattersów, walidacji kompletności zmian, uruchomienia testów po zakończeniu implementacji.

subagentStop LOW

Subagent kończy pracę i zwraca wyniki do głównego agenta. Idealne do: audytu outputów subagentów, logowania aktywności w złożonych workflows z orkiestracją agentów.

errorOccurred MEDIUM

Podczas wykonania agenta wystąpił błąd. Idealne do: logowania błędów dla debugowania, wysyłania alertów do Slack/Teams, zbierania metryk o awariach agenta.

sessionEnd MEDIUM

Sesja agenta kończy się lub jest przerywana. Idealne do: czyszczenia plików tymczasowych, generowania raportów aktywności, wysyłania powiadomień o zakończeniu sesji, archiwizacji logów.

Anatomia pliku hooks.json

Każdy hook to plik JSON umieszczony w katalogu .github/hooks/. Możesz mieć wiele plików – wszystkie są ładowane automatycznie. Nazwa pliku nie ma znaczenia, liczy się zawartość.

.github/
└── hooks/
    ├── dotnet-lint.json       ← Twój główny hook jakości kodu
    ├── security-gate.json       ← preToolUse blokujący niebezpieczne operacje
    ├── audit-log.json       ← logowanie sesji dla compliance
    └── scripts/
        ├── dotnet-lint.sh           ← skrypt bashowy (Linux/macOS)
        ├── dotnet-lint.ps1           ← skrypt PowerShell (Windows)
        └── security-check.sh
🔍 Pola konfiguracji każdego wpisu hooka
Pole Wymagane Opis
type Tak Zawsze "command" – jedyny obsługiwany typ.
bash Bash lub PS Komenda lub ścieżka do skryptu na Unix/Linux/macOS.
powershell Bash lub PS Komenda lub ścieżka do skryptu na Windows. Podaj oba żeby hook działał cross-platform.
cwd Nie Katalog roboczy względem katalogu głównego repo. Domyślnie root repo.
timeoutSec Nie Maks. czas wykonania w sekundach (domyślnie 30). Hook jest ubijany po przekroczeniu – agent kontynuuje.
env Nie Dodatkowe zmienne środowiskowe dla tego hooka. Mergowane z istniejącym środowiskiem.

Zasada blokowania: hook wychodzi z kodem niezerowym → akcja zablokowana (tylko dla preToolUse). Dla pozostałych eventów niezerowy exit kod jest logowany, ale agent kontynuuje.

Minimalna, działająca struktura pliku:

{
  "version": 1,
  "hooks": {
    "agentStop": [
      {
        "type": "command",
        "bash": "echo 'Sesja zakończona' >> /tmp/copilot-log.txt",
        "powershell": "Add-Content -Path $env:TEMP\\copilot-log.txt -Value 'Sesja zakończona'",
        "cwd": ".",
        "timeoutSec": 5
      }
    ]
  }
}
⚠️ Hooks działają synchronicznie – agent czeka

Agent jest zawieszony podczas wykonania hooka. Zbyt wolny hook = irytująco długie oczekiwanie przy każdej operacji. Dobra zasada: postToolUse i preToolUse max 15 sekund, agentStop max 60 sekund. Wszystko co dłuższe – rozważ uruchomienie asynchronicznie w tle lub przeniesienie do sessionEnd.

.NET Gotowy hook dla .NET developera – lintowanie przy każdym commicie

To jest mój główny dodatek do tego wpisu. Zamiast omawiać abstrakcyjne przykłady z Prettier i ESLint (które dla .NET developera są równie użyteczne co instrukcja obsługi kosiarki) – daję Ci gotowy, kompletny hook dla projektu .NET.

Hook uruchamia się po każdym zakończeniu odpowiedzi agenta (agentStop). Sprawdza formatowanie przez dotnet format, uruchamia analizę przez Roslyn Analyzers i – opcjonalnie – szybki build weryfikacyjny. Jeśli cokolwiek nie przejdzie, agent jest blokowany od dalszej pracy do naprawy.

Struktura plików:

.github/
└── hooks/
    ├── dotnet-quality-gate.json
    └── scripts/
        ├── dotnet-quality-gate.sh        ← Linux / macOS / WSL
        └── dotnet-quality-gate.ps1        ← Windows (native PowerShell)
📄 .github/hooks/dotnet-quality-gate.json agentStop
{
  "version": 1,
  "hooks": {
    "agentStop": [
      {
        "type": "command",
        "bash": ".github/hooks/scripts/dotnet-quality-gate.sh",
        "powershell": ".github/hooks/scripts/dotnet-quality-gate.ps1",
        "cwd": ".",
        "timeoutSec": 90,
        "env": {
          "DOTNET_SOLUTION": "MyApp.sln",
          "RUN_BUILD_CHECK": "true",
          "FAIL_ON_WARNINGS": "true"
        }
      }
    ]
  }
}

Dostosuj DOTNET_SOLUTION do nazwy Twojego pliku .sln. Ustaw RUN_BUILD_CHECK na false jeśli build trwa długo i chcesz szybszego hooka.

🐧 .github/hooks/scripts/dotnet-quality-gate.sh Linux / macOS / WSL
#!/usr/bin/env bash
# .github/hooks/scripts/dotnet-quality-gate.sh
# Hook jakości dla projektów .NET – uruchamiany po każdej odpowiedzi agenta.
# Sprawdza: formatowanie (dotnet format), analizę statyczną (Roslyn),
# opcjonalnie build weryfikacyjny.
#
# Zmienne środowiskowe (konfiguracja w hooks.json):
#   DOTNET_SOLUTION  – nazwa pliku .sln (domyślnie: *.sln w root)
#   RUN_BUILD_CHECK  – true/false (domyślnie: true)
#   FAIL_ON_WARNINGS – true/false (domyślnie: true)

set -euo pipefail

# ──────────────────────────────────────────────
# Konfiguracja
# ──────────────────────────────────────────────
SOLUTION="${DOTNET_SOLUTION:-}"
RUN_BUILD="${RUN_BUILD_CHECK:-true}"
FAIL_WARNINGS="${FAIL_ON_WARNINGS:-true}"
ERRORS=0

# Jeśli nie podano .sln – znajdź automatycznie
if [[ -z "$SOLUTION" ]]; then
    SOLUTION=$(find . -maxdepth 2 -name "*.sln" | head -1)
fi

if [[ -z "$SOLUTION" ]]; then
    echo "⚠️  Nie znaleziono pliku .sln – pomijam quality gate."
    exit 0
fi

echo ""
echo "╔══════════════════════════════════════════════════╗"
echo "║    🔬 .NET Quality Gate – Copilot Hook           ║"
echo "╚══════════════════════════════════════════════════╝"
echo "  Rozwiązanie: $SOLUTION"
echo ""

# ──────────────────────────────────────────────
# KROK 1: Formatowanie (dotnet format)
# ──────────────────────────────────────────────
echo "▶ [1/3] Sprawdzanie formatowania (dotnet format)..."

if dotnet format "$SOLUTION" --verify-no-changes --no-restore 2>&1; then
    echo "  ✅ Formatowanie OK"
else
    echo ""
    echo "  ❌ BŁĄD FORMATOWANIA – kod nie jest sformatowany zgodnie z .editorconfig"
    echo "  💡 Uruchom lokalnie: dotnet format $SOLUTION"
    echo ""
    ERRORS=$((ERRORS + 1))
fi

# ──────────────────────────────────────────────
# KROK 2: Analiza statyczna (Roslyn Analyzers)
# ──────────────────────────────────────────────
echo "▶ [2/3] Analiza statyczna (Roslyn Analyzers)..."

ANALYZER_OUTPUT=$(dotnet build "$SOLUTION" \
    --no-restore \
    --no-incremental \
    -warnaserror:false \
    -p:EnforceCodeStyleInBuild=true \
    -p:RunAnalyzersDuringBuild=true \
    2>&1)

ANALYZER_ERRORS=$(echo "$ANALYZER_OUTPUT" | grep -c " error " || true)
ANALYZER_WARNINGS=$(echo "$ANALYZER_OUTPUT" | grep -c " warning " || true)

if [[ "$ANALYZER_ERRORS" -gt 0 ]]; then
    echo ""
    echo "  ❌ BŁĘDY ANALIZY ($ANALYZER_ERRORS błędów):"
    echo "$ANALYZER_OUTPUT" | grep " error " | head -10
    echo ""
    ERRORS=$((ERRORS + 1))
elif [[ "$FAIL_WARNINGS" == "true" && "$ANALYZER_WARNINGS" -gt 0 ]]; then
    echo ""
    echo "  ⚠️  OSTRZEŻENIA ANALIZY ($ANALYZER_WARNINGS ostrzeżeń):"
    echo "$ANALYZER_OUTPUT" | grep " warning " | head -5
    echo "  (FAIL_ON_WARNINGS=true – traktuję jako błąd)"
    echo ""
    ERRORS=$((ERRORS + 1))
else
    echo "  ✅ Analiza OK (ostrzeżeń: $ANALYZER_WARNINGS)"
fi

# ──────────────────────────────────────────────
# KROK 3: Weryfikacja buildu (opcjonalnie)
# ──────────────────────────────────────────────
if [[ "$RUN_BUILD" == "true" ]]; then
    echo "▶ [3/3] Weryfikacja buildu..."

    if dotnet build "$SOLUTION" \
        --no-restore \
        --no-incremental \
        -warnaserror \
        -p:TreatWarningsAsErrors=true \
        --verbosity quiet 2>&1; then
        echo "  ✅ Build OK"
    else
        echo ""
        echo "  ❌ BUILD NIEUDANY – napraw błędy kompilacji przed kontynuacją"
        echo ""
        ERRORS=$((ERRORS + 1))
    fi
else
    echo "▶ [3/3] Weryfikacja buildu pominięta (RUN_BUILD_CHECK=false)"
fi

# ──────────────────────────────────────────────
# Wynik końcowy
# ──────────────────────────────────────────────
echo ""
echo "══════════════════════════════════════════════════"
if [[ "$ERRORS" -gt 0 ]]; then
    echo "  🚫 Quality gate NIEUDANY ($ERRORS problem(y))"
    echo "  Agent zablokowany do naprawy błędów."
    echo "══════════════════════════════════════════════════"
    echo ""
    exit 1
else
    echo "  ✅ Quality gate ZALICZONY – kod gotowy"
    echo "══════════════════════════════════════════════════"
    echo ""
    exit 0
fi
🪟 .github/hooks/scripts/dotnet-quality-gate.ps1 Windows PowerShell
# .github/hooks/scripts/dotnet-quality-gate.ps1
# Hook jakości dla projektów .NET – wersja Windows PowerShell.
# Odpowiednik dotnet-quality-gate.sh dla developerów na Windows.

$ErrorActionPreference = "Continue"

# ──────────────────────────────────────────────
# Konfiguracja
# ──────────────────────────────────────────────
$Solution     = $env:DOTNET_SOLUTION
$RunBuild     = $env:RUN_BUILD_CHECK     -ne "false"
$FailWarnings = $env:FAIL_ON_WARNINGS    -ne "false"
$Errors       = 0

# Jeśli nie podano .sln – znajdź automatycznie
if (-not $Solution) {
    $Solution = Get-ChildItem -Path . -Filter "*.sln" -Depth 2 |
                Select-Object -First 1 -ExpandProperty FullName
}

if (-not $Solution) {
    Write-Host "⚠️  Nie znaleziono pliku .sln – pomijam quality gate."
    exit 0
}

Write-Host ""
Write-Host "╔══════════════════════════════════════════════════╗"
Write-Host "║    🔬 .NET Quality Gate – Copilot Hook           ║"
Write-Host "╚══════════════════════════════════════════════════╝"
Write-Host "  Rozwiązanie: $Solution"
Write-Host ""

# ──────────────────────────────────────────────
# KROK 1: Formatowanie
# ──────────────────────────────────────────────
Write-Host "▶ [1/3] Sprawdzanie formatowania (dotnet format)..."
$formatOutput = dotnet format $Solution --verify-no-changes --no-restore 2>&1
if ($LASTEXITCODE -eq 0) {
    Write-Host "  ✅ Formatowanie OK"
} else {
    Write-Host ""
    Write-Host "  ❌ BŁĄD FORMATOWANIA – uruchom: dotnet format $Solution"
    Write-Host ""
    $Errors++
}

# ──────────────────────────────────────────────
# KROK 2: Analiza statyczna
# ──────────────────────────────────────────────
Write-Host "▶ [2/3] Analiza statyczna (Roslyn Analyzers)..."
$analyzeOutput = dotnet build $Solution `
    --no-restore `
    --no-incremental `
    -p:EnforceCodeStyleInBuild=true `
    -p:RunAnalyzersDuringBuild=true `
    2>&1

$analyzeErrors   = ($analyzeOutput | Select-String " error "  ).Count
$analyzeWarnings = ($analyzeOutput | Select-String " warning ").Count

if ($analyzeErrors -gt 0) {
    Write-Host "  ❌ BŁĘDY ANALIZY ($analyzeErrors błędów)"
    $analyzeOutput | Select-String " error " | Select-Object -First 10 | ForEach-Object { Write-Host "  $_" }
    $Errors++
} elseif ($FailWarnings -and $analyzeWarnings -gt 0) {
    Write-Host "  ⚠️  OSTRZEŻENIA ($analyzeWarnings) – traktuję jako błędy (FAIL_ON_WARNINGS=true)"
    $Errors++
} else {
    Write-Host "  ✅ Analiza OK (ostrzeżeń: $analyzeWarnings)"
}

# ──────────────────────────────────────────────
# KROK 3: Build weryfikacyjny
# ──────────────────────────────────────────────
if ($RunBuild) {
    Write-Host "▶ [3/3] Weryfikacja buildu..."
    dotnet build $Solution --no-restore --no-incremental `
        -warnaserror -p:TreatWarningsAsErrors=true --verbosity quiet 2>&1 | Out-Null
    if ($LASTEXITCODE -eq 0) {
        Write-Host "  ✅ Build OK"
    } else {
        Write-Host "  ❌ BUILD NIEUDANY"
        $Errors++
    }
} else {
    Write-Host "▶ [3/3] Build pominięty (RUN_BUILD_CHECK=false)"
}

# ──────────────────────────────────────────────
# Wynik
# ──────────────────────────────────────────────
Write-Host ""
Write-Host "══════════════════════════════════════════════════"
if ($Errors -gt 0) {
    Write-Host "  🚫 Quality gate NIEUDANY ($Errors problem(y))"
    Write-Host "  Agent zablokowany do naprawy błędów."
    Write-Host "══════════════════════════════════════════════════"
    exit 1
} else {
    Write-Host "  ✅ Quality gate ZALICZONY – kod gotowy"
    Write-Host "══════════════════════════════════════════════════"
    exit 0
}
⚠️ Pamiętaj o uprawnieniach do skryptu .sh

Na Linux/macOS skrypt musi być wykonywalny. Po stworzeniu pliku uruchom:

chmod +x .github/hooks/scripts/dotnet-quality-gate.sh
git add .github/hooks/scripts/dotnet-quality-gate.sh
git commit -m "Add dotnet quality gate hook"

Na Windows ten krok nie jest potrzebny, ale upewnij się że PowerShell execution policy pozwala na uruchomienie skryptów: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

Bonus – hook blokujący niebezpieczne operacje

Drugi gotowy hook – tym razem preToolUse który blokuje agenta przed wykonaniem destruktywnych komend. Szczególnie przydatny gdy agent ma dostęp do terminala i może uruchamiać dowolne polecenia.

🛡️ .github/hooks/security-gate.json preToolUse – BLOKUJE
{
  "version": 1,
  "hooks": {
    "preToolUse": [
      {
        "type": "command",
        "bash": ".github/hooks/scripts/security-gate.sh",
        "powershell": ".github/hooks/scripts/security-gate.ps1",
        "cwd": ".",
        "timeoutSec": 5
      }
    ]
  }
}
🐧 .github/hooks/scripts/security-gate.sh
#!/usr/bin/env bash
# Blokuje niebezpieczne komendy. Czyta JSON z stdin (opis narzędzia).
set -euo pipefail

INPUT=$(cat)

# Wyciągnij nazwę narzędzia i parametry z JSON
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName // empty' 2>/dev/null || echo "")
TOOL_INPUT=$(echo "$INPUT" | jq -r '.toolInput // empty' 2>/dev/null || echo "")

# Działamy tylko gdy narzędzie to terminal/bash/shell
if [[ "$TOOL_NAME" != "terminal" && "$TOOL_NAME" != "bash" && "$TOOL_NAME" != "run_in_terminal" ]]; then
    exit 0
fi

# Lista zabronionych wzorców komend
BLOCKED_PATTERNS=(
    "DROP TABLE"
    "DROP DATABASE"
    "TRUNCATE"
    "rm -rf /"
    "rm -rf \*"
    "del /f /s /q C:\\\\"
    "format c:"
    "git push --force"
    "git push -f"
    "> /dev/sd"
    "sudo rm"
)

for PATTERN in "${BLOCKED_PATTERNS[@]}"; do
    if echo "$TOOL_INPUT" | grep -qi "$PATTERN"; then
        echo ""
        echo "🚫 SECURITY GATE ZABLOKOWAŁ operację:"
        echo "   Wzorzec: $PATTERN"
        echo "   Komenda: $TOOL_INPUT"
        echo ""
        echo "   Jeśli chcesz wykonać tę operację – zrób to ręcznie,"
        echo "   poza sesją agenta."
        echo ""
        exit 1
    fi
done

# Chronione pliki – agent nie może ich edytować
PROTECTED_FILES=(
    "*.sln"
    "global.json"
    "NuGet.Config"
    ".github/workflows/*.yml"
)

for PATTERN in "${PROTECTED_FILES[@]}"; do
    if echo "$TOOL_INPUT" | grep -qi "$PATTERN"; then
        echo ""
        echo "🛡️  SECURITY GATE: Plik chroniony – $PATTERN"
        echo "   Modyfikuj go ręcznie jeśli to naprawdę potrzebne."
        echo ""
        exit 1
    fi
done

exit 0
🔍 Jak hook preToolUse otrzymuje kontekst?

Zanim agent wykona narzędzie, Copilot przekazuje do skryptu hooka JSON przez stdin. Struktura tego JSONa zawiera m.in.:

{
  "toolName": "terminal",
  "toolInput": "dotnet ef database drop --force",
  "sessionId": "abc-123",
  "timestamp": "2026-03-11T10:30:00Z"
}

Twój skrypt czyta ten JSON przez cat, parsuje przez jq i podejmuje decyzję: exit 0 = zezwól, exit 1 = zablokuj. Prosto, niezawodnie, bez AI w pętli decyzyjnej.

Najlepsze praktyki

1. Warstwuj hooki – nie rób monolitu

Każdy plik JSON to osobna odpowiedzialność. Linting osobno, security osobno, audit logging osobno. Ułatwia to debugowanie (wiesz który hook się wywalił), code review i selektywne wyłączanie.

2. Używaj set -euo pipefail w skryptach bash

Bez tego skrypt może "przejść" mimo błędu w jednym kroku. -e zatrzymuje przy pierwszym błędzie, -u traktuje niezdefiniowane zmienne jako błąd, -o pipefail propaguje błąd przez pipe.

3. Testuj lokalnie zanim wrzucisz do repo

Uruchom skrypt ręcznie z terminala zanim go scommitujesz. Hook który się crashuje przy każdej operacji agenta to szybka droga do frustracji całego teamu.

4. Ustaw realistyczne timeouty

Domyślne 30 sekund to za mało dla dotnet build na większym projekcie. Za dużo dla prostego sprawdzenia formatowania. Dopasuj do rzeczywistego czasu wykonania + 20% margines.

5. Dostarczaj czytelne komunikaty błędów

Agent widzi output hooka i może na jego podstawie zaproponować naprawę. "❌ Formatowanie niezgodne z .editorconfig – uruchom: dotnet format" jest lepsze niż "Error code 1". Im więcej kontekstu w błędzie tym lepiej agent rozumie co naprawić.

💪 Zadanie dla Ciebie – wdróż quality gate
  1. Stwórz katalogi .github/hooks/ i .github/hooks/scripts/
  2. Wklej trzy pliki: dotnet-quality-gate.json, dotnet-quality-gate.sh, dotnet-quality-gate.ps1
  3. Dostosuj DOTNET_SOLUTION w pliku JSON do nazwy swojego .sln
  4. Na Linux/macOS: chmod +x .github/hooks/scripts/dotnet-quality-gate.sh
  5. Przetestuj skrypt ręcznie z terminala – upewnij się że dotnet format i dotnet build są dostępne
  6. Scommituj i poproś agenta o celowe "pobrudnienie" formatowania – obserwuj czy hook go złapie i zablokuje
Podsumowanie

Hooks to ostatni element który odróżnia "Copilota z ładną konfiguracją" od prawdziwego, enterprise-grade środowiska agentic development – gdzie standardy są wymuszane kodem, a nie apelami do AI.

  • Hooks są deterministyczne – wykonują się zawsze, niezależnie od rozumowania agenta. Nie proszą, wymuszają.
  • Osiem zdarzeń – od sessionStart do sessionEnd. Najsilniejszy: preToolUse który może blokować wykonanie narzędzia przez agenta.
  • Jeden plik JSON – dwa środowiska: Copilot Coding Agent (GitHub.com) i Copilot CLI (lokalnie w VS Code). Commituj do .github/hooks/ i działa wszędzie.
  • Cross-platform przez bash + powershell – jeden hook działa na Linux/macOS i Windows.
  • Gotowy .NET Quality Gate: trzy kroki – formatowanie (dotnet format), analiza statyczna (Roslyn Analyzers), build weryfikacyjny. Bash + PowerShell, gotowy do wklejenia.
  • Gotowy Security Gate: preToolUse który blokuje destruktywne komendy i chroni pliki konfiguracyjne przed modyfikacją przez agenta.

W kolejnym wpisie wychodzimy poza VS Code i patrzymy na szerszy obraz: Agentic Workflows – jak Copilot Coding Agent działa autonomicznie na GitHub.com, jak kompilować naturalne języki do GitHub Actions i jak zautomatyzować cały pipeline od issue do PR.