Skrypty z Cursor CLI: git hooki, review w CI i zadania cykliczne
ENCursor CLI uruchamia agenta Cursora bez edytora: agent -p "<prompt>" odpowiada jeden raz i kończy działanie, więc mogą go wywoływać git hooki, joby GitHub Actions i zadania cykliczne. Bezpieczny wzorzec trzyma krok agenta wąsko, nie daje mu tokenu z prawem zapisu, a o tym, czy wynik trafi dalej, decydują testy, strażnik ścieżek i merge wykonany przez człowieka.
Ta strona jest dla programistów i tech leadów, którzy używają Cursora w edytorze i chcą, żeby ten sam agent pracował w infrastrukturze repozytorium. Pierwsza próba zwykle wygląda tak: job „AI review” napisał „Wygląda dobrze!” pod PR, który zepsuł checkout, a cotygodniowy bot od zależności po cichu zmienił plik źródłowy, o który nikt go nie prosił. Obie wpadki mają tę samą naprawę i nie jest nią lepszy model.
Co daje Cursor uruchamiany ze skryptów
Dział zatytułowany „Co daje Cursor uruchamiany ze skryptów”- Hook pre-commit, który wysyła do agenta zmiany dodane do indeksu (
git add) i blokuje commit przy werdykcieFAIL, z przełącznikiem trybu ostrzegawczego. - Review PR w GitHub Actions, które działa na zaufanym kodzie, traktuje diff jako dane i publikuje komentarz z osobnego joba.
- Cotygodniowe zadanie od zależności, którego wynik sprawdzają strażnik zmian wersji i testy w jobie bez żadnych sekretów, a nie lektura diffu.
- Tabelę decyzyjną: kiedy skrypt na własnym runnerze wygrywa z Cursor Automations, Cursor SDK albo Bugbotem.
- Typowe awarie przebiegów bez nadzoru i sposób wyjścia z każdej z nich.
Gdzie uruchamiać zadanie Cursora ze skryptu?
Dział zatytułowany „Gdzie uruchamiać zadanie Cursora ze skryptu?”Tabela pokazuje pięć sposobów na uruchomienie agenta Cursora bez ciebie przy klawiaturze. Ta strona omawia dwa pierwsze wiersze (CLI w git hooku i w CI); pozostałe trzy mają własne strony.
| Potrzebujesz | Użyj | Dlaczego |
|---|---|---|
| Kontroli w chwili commitu, na maszynie programisty | agent -p w git hooku | Szybka informacja zwrotna, zanim kod opuści laptopa; jedno wywołanie na commit |
| Joba w istniejącym CI, na twoim runnerze, obok twoich bramek | agent -p w GitHub Actions | Polityka sekretów, testy i wymagane checki zostają po twojej stronie |
| Przebiegów cyklicznych lub wyzwalanych zdarzeniem na maszynach Cursora | Cloud agents i Automations | Brak runnera do utrzymania; wyzwalacze z GitHuba, Slacka, Lineara, Sentry i innych |
| Przebiegów agenta startowanych i śledzonych z własnego kodu | Cursor SDK | Strumień zdarzeń, anulowanie i artefakty przez API |
| Zarządzanego review PR bez pliku workflow | Bugbot | Recenzenta utrzymuje Cursor; ty stroisz reguły zamiast promptów |
Cursor ma też własne Hooks, które działają wewnątrz pętli agenta i wymieniają JSON przez stdio. To inna funkcja niż opisane niżej git hooki.
Zainstaluj Cursor CLI i uwierzytelnij skrypt
Dział zatytułowany „Zainstaluj Cursor CLI i uwierzytelnij skrypt”-
Zainstaluj CLI na maszynie lub runnerze. Dokumentacja instalacji Cursora (sprawdzona 2026-10-12) podaje
curl https://cursor.com/install -fsS | bashdla macOS, Linuksa i WSL orazirm 'https://cursor.com/install?win32=true' | iexdla Windowsa. Tej linii używa Dockerfile w cookbooku Cursora, a z niej korzystają workflowy poniżej:Okno terminala curl -fsSL https://cursor.com/install | bashexport PATH="$HOME/.cursor/bin:$PATH" # w GitHub Actions: echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"Instalację sprawdzisz przez
agent --version, a później zaktualizujesz przezagent update. -
Utwórz klucz API w panelu Cursora, w sekcji Integrations, i zapisz go jako sekret o nazwie
CURSOR_API_KEY. CLI czyta klucz z tej zmiennej środowiskowej, a przykłady SDK Cursora używają tej samej nazwy, więc skrypt nigdy nie potrzebuje interaktywnego logowania. -
Uruchom smoke test tylko do odczytu z katalogu głównego repozytorium:
Okno terminala agent -p "List the top-level directories of this repository and what each one holds." --output-format text -
Dla każdego joba zdecyduj, czy agent może zmieniać pliki.
--forcedodawaj tylko tam, gdzie agent musi zapisywać, i uruchamiaj takie joby wyłącznie na jednorazowym runnerze albo w kontenerze z ograniczonym dostępem do sieci, nigdy na stacji roboczej:--forcezatwierdza każde wywołanie narzędzia, także polecenia powłoki; joby review działają z--mode ask, czyli w trybie tylko do odczytu, którego używają publiczne workflowy z review, i przechwytują standardowe wyjście. Pomiń--model, żeby użyć domyślnego modelu konta, a modele wybieraj w hubie modeli, zamiast wpisywać jeden na sztywno w dziesięć workflowów.
Blokuj ryzykowne commity przeglądem pre-commit
Dział zatytułowany „Blokuj ryzykowne commity przeglądem pre-commit”Hook wkleja do promptu zmiany dodane do indeksu (git add), więc agent nie potrzebuje narzędzi ani prawa zapisu. Gdy agent jest nieosiągalny, hook przepuszcza commit, bo lokalny hook to wygoda, a prawdziwą bramką jest CI.
Najpierw zacommituj prompt recenzenta jako plik scripts/ai/pre-commit-review.txt, żeby cały zespół sprawdzał kod według tego samego tekstu:
Następnie zapisz hook jako .githooks/pre-commit, nadaj mu prawo wykonywania (chmod +x .githooks/pre-commit) i raz na klon wskaż gitowi katalog poleceniem git config core.hooksPath .githooks:
#!/usr/bin/env bash# .githooks/pre-commit - Cursor CLI review of the staged diffset -euo pipefail
DIFF=$(git diff --cached --diff-filter=ACMR -U3)[ -z "$DIFF" ] && exit 0
# One command-line argument is capped at ~128 KB on Linux; CI skips such diffs too.if [ "${#DIFF}" -gt 100000 ]; then echo "pre-commit: diff too large for AI review, skipped. CI tests still run; split the change for AI review." exit 0fi
run_agent() { # macOS has no `timeout` unless coreutils is installed if command -v timeout >/dev/null; then timeout 180 agent "$@"; else agent "$@"; fi}
PROMPT="$(cat scripts/ai/pre-commit-review.txt)
<diff>$DIFF</diff>"
# --trust: this is the developer's own working tree. Without it, print mode can# stop on the workspace-trust question on the first commit in a new clone.if ! RESULT=$(run_agent -p "$PROMPT" --mode ask --trust --output-format text); then echo "pre-commit: Cursor agent unavailable, commit allowed. CI still runs." exit 0fi
echo "$RESULT"VERDICT=$(printf '%s\n' "$RESULT" | head -n 1 | tr -d '[:space:]*')[ "$VERDICT" = "PASS" ] && exit 0
if [ "${AI_REVIEW_MODE:-block}" = "warn" ]; then echo "pre-commit: findings above, commit allowed (AI_REVIEW_MODE=warn)." exit 0fiecho "Commit blocked. Fix the findings, or bypass once with: git commit --no-verify"exit 1Całe rozwiązanie opiera się na trzech szczegółach. tr usuwa pogrubienie Markdowna, więc **PASS** nadal przechodzi. Werdykt musi być dosłownie pierwszą linią, więc gadatliwa odpowiedź blokuje commit, zamiast się prześlizgnąć. AI_REVIEW_MODE=warn pozwala zespołowi zacząć od trybu doradczego i przejść na blokujący, gdy uwagi okażą się przydatne.
Review pull requestów w GitHub Actions bez dawania agentowi tokenu
Dział zatytułowany „Review pull requestów w GitHub Actions bez dawania agentowi tokenu”Job review PR to miejsce, w którym przy agentach uruchamianych ze skryptów najczęściej dochodzi do wycieku sekretów. Gałąź pull requesta może nieść własne reguły w .cursor/, konfigurację MCP i skrypty, a wszystko, co agent załaduje z takiego checkoutu, działa obok CURSOR_API_KEY. Dlatego ten workflow robi checkout commita bazowego, pobiera diff PR jako zwykły tekst, uruchamia agenta w trybie --mode ask bez --force i publikuje komentarz z drugiego joba, który nigdy nie uruchamia agenta.
name: Cursor PR review
on: pull_request: types: [opened, synchronize, ready_for_review]
permissions: {}
jobs: review: # Fork PRs receive no secrets; skip them instead of failing. if: github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest timeout-minutes: 15 permissions: contents: read pull-requests: read steps: - name: Check out the base commit (trusted code only) uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.base.sha }} persist-credentials: false
- name: Fetch the PR diff as data env: GH_TOKEN: ${{ github.token }} PR: ${{ github.event.pull_request.number }} run: gh pr diff "$PR" --repo "$GITHUB_REPOSITORY" > pr.diff
- name: Install Cursor CLI run: | curl -fsSL https://cursor.com/install | bash echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"
- name: Review (ask mode, no --force, no GitHub token in this step) env: CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} # --trust answers the headless workspace-trust prompt for this trusted # base checkout; confirm the flag in `agent --help` on your runner. run: | if [ "$(wc -c < pr.diff)" -gt 100000 ]; then echo "Diff is too large for a single-pass review. Split the PR or review it by directory." > review.md exit 0 fi { cat .github/prompts/pr-review.txt; printf '\n<diff>\n'; cat pr.diff; printf '\n</diff>\n'; } > prompt.txt agent -p "$(cat prompt.txt)" --mode ask --trust --output-format text > review.md test -s review.md
- uses: actions/upload-artifact@v7 with: name: cursor-review path: review.md
comment: needs: review runs-on: ubuntu-latest timeout-minutes: 5 permissions: pull-requests: write steps: - uses: actions/download-artifact@v8 with: name: cursor-review - name: Post the review (this job never runs the agent) env: GH_TOKEN: ${{ github.token }} PR: ${{ github.event.pull_request.number }} run: gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body-file review.mdPrompt również jest czytany z checkoutu bazowego, więc agent nigdy nie ładuje promptu recenzenta ani reguł .cursor/, które istnieją tylko na gałęzi. Nie jest to jednak zabezpieczenie przed manipulacją. Przy wyzwalaczu pull_request GitHub uruchamia plik workflow z merge refa samego pull requesta, więc gałąź z tego samego repozytorium może zmienić ten YAML tak, by zrobić checkout swojego heada, podmienić prompt albo wypisać CURSOR_API_KEY, a tak zmieniony job wykona się, zanim ktokolwiek go przejrzy. Każdy, kto ma prawo push, może zrobić to samo z workflow na dowolnej gałęzi, więc sekret jest bezpieczny dokładnie na tyle, na ile zaufana jest lista osób z prawem zapisu. Prawdziwą bramkę dają dwie kontrole. Wpis w CODEOWNERS dla .github/ plus reguła gałęzi lub ruleset wymagający review właściciela kodu sprawiają, że zmiana workflow albo promptu nie wejdzie bez przeglądu. Jeśli job ma działać z workflow, którego gałąź nie może zmienić, użyj pull_request_target, który uruchamia workflow z gałęzi domyślnej; zachowaj tam dokładnie ten kształt (bez checkoutu heada, diff pobrany jako tekst, blokada forków), bo taki job trzyma sekrety obok danych kontrolowanych przez autora PR. Zacommituj prompt jako .github/prompts/pr-review.txt:
Tagi oficjalnych akcji actions/* od GitHuba powyżej to aktualne wersje główne na 2026-09-26. Każdą akcję zewnętrzną przypinaj do pełnego SHA commita zamiast do tagu: przestawiony tag to mechanizm, przez który w kompromitacji tj-actions/changed-files z marca 2025 roku (CVE-2025-30066) wyciekły sekrety CI.
Cotygodniowe zadanie od zależności ze strażnikiem zmian wersji
Dział zatytułowany „Cotygodniowe zadanie od zależności ze strażnikiem zmian wersji”Mechaniczne podbicia wersji dobrze otwierają już Dependabot i Renovate. Agent przydaje się dopiero przy decyzjach wymagających osądu: które podbicia wziąć razem i w którym miejscu kodu każde z nich warto sprawdzić.
Workflow dzieli pracę na trzy joby, tak jak review PR, bo izolacja między krokami jednego joba nie istnieje. Agent uruchomiony z --force może wykonywać polecenia powłoki, więc może dopisać coś do $GITHUB_ENV albo $GITHUB_PATH (na przykład fałszywe gh lub git na początku ścieżki) i przechwycić token, który dostanie późniejszy krok tego samego joba. Może też przepisać sekcję scripts w package.json, którą potem uruchomiłyby npm ci i npm test.
proposemaCURSOR_API_KEYi token tylko do odczytu. Agent edytujepackage.jsoni piszeCHANGES.md, a z joba wychodzą jako artefakt tylko te dwa pliki. Wszystko inne, czego dotknął, znika razem z runnerem.testnie ma żadnych sekretów i zaczyna od świeżego checkoutu domyślnej gałęzi. Strażnik przerywa przebieg, jeśli wpackage.jsonzmieniło się cokolwiek poza numerami wersji zależności, a każda nowa wersja musi być zwykłym zakresem semver. Dopiero potem npm generuje lockfile i uruchamianpm ci --ignore-scriptsoraznpm test.prma token z prawem zapisu. Nie uruchamia ani agenta, ani twojego kodu: commituje przetestowanepackage.jsoni lockfile, a potem otwiera PR.
name: Weekly dependency bumps
on: schedule: - cron: '17 3 * * 1' # Mondays 03:17 UTC workflow_dispatch:
permissions: {}
jobs: propose: # The agent job: CURSOR_API_KEY and a read-only token, nothing else. runs-on: ubuntu-latest timeout-minutes: 20 permissions: contents: read outputs: changed: ${{ steps.collect.outputs.changed }} steps: - uses: actions/checkout@v7 with: persist-credentials: false
- name: List outdated packages and their sources (deterministic input) # Without node_modules, npm outdated lists every package as MISSING. run: | npm ci --ignore-scripts npm outdated --json > outdated.json || true jq -r 'keys[]' outdated.json | while read -r p; do printf '%s %s\n' "$p" "$(npm view "$p" repository.url 2>/dev/null)" done > sources.txt
- name: Install Cursor CLI run: | curl -fsSL https://cursor.com/install | bash echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"
- name: Agent edits package.json and writes CHANGES.md env: CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} # --trust answers the headless workspace-trust prompt for this trusted # checkout; confirm the flag in `agent --help` on your runner. # --force auto-approves every tool call, shell included: use it only on a # disposable runner like this one, with no write token, and restrict the # runner's network egress to the Cursor API and the npm registry. run: | agent -p "$(cat .github/prompts/deps-bump.txt)
<outdated> $(cat outdated.json) </outdated>
<sources> $(cat sources.txt) </sources>" --force --trust --output-format text
- name: Hand on exactly two files id: collect run: | mkdir -p proposed if git diff --quiet -- package.json; then echo "changed=false" >> "$GITHUB_OUTPUT"; exit 0 fi test -s CHANGES.md || { echo "::error::agent did not write CHANGES.md"; exit 1; } cp package.json CHANGES.md proposed/ echo "changed=true" >> "$GITHUB_OUTPUT"
- if: steps.collect.outputs.changed == 'true' uses: actions/upload-artifact@v7 with: name: deps-proposed path: proposed/
test: # No secrets here. The checks run on a fresh base checkout the agent never saw. needs: propose if: needs.propose.outputs.changed == 'true' runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read steps: - uses: actions/checkout@v7 with: persist-credentials: false
- uses: actions/download-artifact@v8 with: name: deps-proposed path: proposed
- name: Guard - only dependency version strings may change run: | shape='del(.dependencies, .devDependencies)' names='{d: (.dependencies // {} | keys), dd: (.devDependencies // {} | keys)}' diff <(jq -S "$shape" package.json) <(jq -S "$shape" proposed/package.json) \ || { echo "::error::package.json changed outside the dependency lists"; exit 1; } diff <(jq -S "$names" package.json) <(jq -S "$names" proposed/package.json) \ || { echo "::error::packages were added, removed or moved"; exit 1; } # Every changed version must be a plain semver range, not a URL, path or git ref. BAD=$(jq -r --slurpfile base package.json ' ["dependencies", "devDependencies"][] as $s | (.[$s] // {}) | to_entries[] | select(.value != $base[0][$s][.key]) | .value' proposed/package.json \ | grep -vE '^[~^]?[0-9]+\.[0-9]+\.[0-9]+$' || true) [ -z "$BAD" ] || { echo "::error::non-semver versions: $BAD"; exit 1; }
- name: Lockfile and tests, with install scripts off run: | cp proposed/package.json package.json npm install --package-lock-only --ignore-scripts npm ci --ignore-scripts npm test mkdir -p tested cp package.json package-lock.json proposed/CHANGES.md tested/
- uses: actions/upload-artifact@v7 with: name: deps-tested path: tested/
pr: # The only job with a write token. It runs neither the agent nor your code. needs: test runs-on: ubuntu-latest timeout-minutes: 5 permissions: contents: write pull-requests: write steps: - uses: actions/checkout@v7 with: persist-credentials: false
- uses: actions/download-artifact@v8 with: name: deps-tested
- name: Open or update the PR env: GH_TOKEN: ${{ github.token }} run: | git config user.name "cursor-deps-bot" git config user.email "cursor-deps-bot@users.noreply.github.com" git checkout -B chore/weekly-deps git add package.json package-lock.json git commit -m "chore: weekly dependency bumps" git push -f "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" chore/weekly-deps gh pr create --head chore/weekly-deps --title "chore: weekly dependency bumps" \ --body-file CHANGES.md || gh pr edit chore/weekly-deps --body-file CHANGES.mdWeryfikacją jest strażnik: porównuje package.json z wersją bazową po usunięciu obu list zależności, więc zmieniony skrypt, nowy pakiet albo adres github: przerywają job przed jakąkolwiek instalacją. --ignore-scripts nie pozwala uruchomić skryptów instalacyjnych podbitych pakietów; jeśli testy potrzebują kroku budowania któregoś z nich, uruchom npm rebuild <pakiet> tylko dla tego jednego, nazwanego pakietu. Żeby GitHub Actions mógł utworzyć PR, włącz w ustawieniach repozytorium opcję Allow GitHub Actions to create and approve pull requests. PR otwarte z GITHUB_TOKEN nie uruchamiają innych workflowów, więc wymagane checki nigdy nie ruszą na chore/weekly-deps. W jobie pr użyj tokenu aplikacji GitHub (actions/create-github-app-token) albo fine-grained PAT, ewentualnie zamknij i ponownie otwórz PR, żeby wymagane checki się uruchomiły.
Zacommituj prompt agenta jako .github/prompts/deps-bump.txt. Deterministyczny krok przed agentem przypisuje każdemu pakietowi adres repozytorium, więc każda linia CHANGES.md wskazuje recenzentowi źródło zamiast wymyślonego streszczenia:
Jak zweryfikować wynik agenta uruchamianego ze skryptu bez czytania każdej linii?
Dział zatytułowany „Jak zweryfikować wynik agenta uruchamianego ze skryptu bez czytania każdej linii?”Tekst agenta traktuj jako jedno z wejść deterministycznych bramek, nigdy jako samą bramkę. Każdy z powyższych workflowów ma kontrolę egzekwowaną przez maszynę i osobę, która go zatwierdza:
| Workflow | Co dowodzi wyniku | Kto zatwierdza |
|---|---|---|
| Review pre-commit | Pierwsza linia to dokładnie PASS; wymagane checki CI uruchamiają się ponownie przy każdym pushu | Autor commita. git commit --no-verify pomija hook i nie zostawia śladu, więc pominięte review wyłapują dopiero wymagane checki CI |
| Review PR | Review jest doradcze: publikuje komentarz i nigdy nie oblewa joba. Merge blokują wymagane checki (testy, typy, lint); review.md zostaje jako artefakt | Code owner robi merge; bot nigdy nie zatwierdza |
| Zadanie od zależności | Strażnik zmian wersji, npm ci --ignore-scripts i npm test przechodzą w jobie bez sekretów; lockfile generuje npm, a nie agent | Recenzent PR, na podstawie zielonych testów i strażnika. CHANGES.md to pomocnicze podsumowanie, którego linie wskazują źródło każdego pakietu |
Mierz recenzenta tak, jak mierzysz niestabilny test. Raz w miesiącu oznacz próbkę jego komentarzy jako przydatne albo szum. Git nie zapisuje użycia --no-verify, więc obejścia licz pośrednio: niech hook dopisuje każdy werdykt do pliku logu albo metryki, albo policz commity, w których review w CI wciąż znajduje blokery. Gdy obejść jest więcej niż poprawek, zaostrz prompt albo przełącz hook na AI_REVIEW_MODE=warn. Zachowuj artefakty: zasilają pakiet dowodów, dzięki któremu recenzent zatwierdza zmianę na podstawie dowodów, a nie czytania linia po linii.
Jak te same zadania wyglądają w Claude Code i Codex
Dział zatytułowany „Jak te same zadania wyglądają w Claude Code i Codex”Kształt workflowu jest taki sam we wszystkich trzech narzędziach: deterministyczne wejście, wąski krok agenta, deterministyczne bramki i osobny krok, który trzyma token z prawem zapisu. Różnią się tylko flagi:
| Potrzeba joba | Cursor CLI | Claude Code (2.1.283) | Codex (0.157.1) |
|---|---|---|---|
| Przebieg headless | agent -p "..." | claude -p "..." | codex exec "..." |
| Krok tylko do odczytu | --mode ask (albo przynajmniej bez --force); sprawdź w agent --help | --tools "Read,Grep,Glob" | -c default_permissions=":read-only" (beta) |
| Krok zapisujący pliki | --force | --permission-mode acceptEdits | -c default_permissions=":workspace" (beta) |
| Wynik czytelny dla maszyny | --output-format text plus ścisła pierwsza linia | --output-format json, --json-schema | --output-schema FILE, -o FILE |
| Limit wydatków | timeout-minutes joba | --max-budget-usd | timeout-minutes joba (brak flagi budżetu w codex exec --help, 0.157.1) |
Kolumna Codexa używa profili uprawnień, które zaleca OpenAI. Starsze --sandbox read-only i --sandbox workspace-write nadal działają w 0.157.1, ale oba systemy się nie łączą: w jednym jobie wybierz jeden z nich, nigdy oba.
Pełne wersje tych zadań znajdziesz w przewodnikach skrypty z Claude Code i Codex bez interakcji. Modele uprawnień obok siebie opisuje strona uprawnienia, sandboksy i tryby zatwierdzania.