Przejdź do głównej zawartości

Cursor 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.

  • Hook pre-commit, który wysyła do agenta zmiany dodane do indeksu (git add) i blokuje commit przy werdykcie FAIL, 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.

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.

PotrzebujeszUżyjDlaczego
Kontroli w chwili commitu, na maszynie programistyagent -p w git hookuSzybka informacja zwrotna, zanim kod opuści laptopa; jedno wywołanie na commit
Joba w istniejącym CI, na twoim runnerze, obok twoich bramekagent -p w GitHub ActionsPolityka sekretów, testy i wymagane checki zostają po twojej stronie
Przebiegów cyklicznych lub wyzwalanych zdarzeniem na maszynach CursoraCloud agents i AutomationsBrak runnera do utrzymania; wyzwalacze z GitHuba, Slacka, Lineara, Sentry i innych
Przebiegów agenta startowanych i śledzonych z własnego koduCursor SDKStrumień zdarzeń, anulowanie i artefakty przez API
Zarządzanego review PR bez pliku workflowBugbotRecenzenta 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.

  1. Zainstaluj CLI na maszynie lub runnerze. Dokumentacja instalacji Cursora (sprawdzona 2026-10-12) podaje curl https://cursor.com/install -fsS | bash dla macOS, Linuksa i WSL oraz irm 'https://cursor.com/install?win32=true' | iex dla Windowsa. Tej linii używa Dockerfile w cookbooku Cursora, a z niej korzystają workflowy poniżej:

    Okno terminala
    curl -fsSL https://cursor.com/install | bash
    export PATH="$HOME/.cursor/bin:$PATH" # w GitHub Actions: echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"

    Instalację sprawdzisz przez agent --version, a później zaktualizujesz przez agent update.

  2. 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.

  3. 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
  4. Dla każdego joba zdecyduj, czy agent może zmieniać pliki. --force dodawaj 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: --force zatwierdza 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.

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 diff
set -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 0
fi
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 0
fi
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 0
fi
echo "Commit blocked. Fix the findings, or bypass once with: git commit --no-verify"
exit 1

Cał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.

.github/workflows/cursor-pr-review.yml
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.md

Prompt 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.

  • propose ma CURSOR_API_KEY i token tylko do odczytu. Agent edytuje package.json i pisze CHANGES.md, a z joba wychodzą jako artefakt tylko te dwa pliki. Wszystko inne, czego dotknął, znika razem z runnerem.
  • test nie ma żadnych sekretów i zaczyna od świeżego checkoutu domyślnej gałęzi. Strażnik przerywa przebieg, jeśli w package.json zmienił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 uruchamia npm ci --ignore-scripts oraz npm test.
  • pr ma token z prawem zapisu. Nie uruchamia ani agenta, ani twojego kodu: commituje przetestowane package.json i lockfile, a potem otwiera PR.
.github/workflows/cursor-weekly-deps.yml
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.md

Weryfikacją 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:

WorkflowCo dowodzi wynikuKto zatwierdza
Review pre-commitPierwsza linia to dokładnie PASS; wymagane checki CI uruchamiają się ponownie przy każdym pushuAutor commita. git commit --no-verify pomija hook i nie zostawia śladu, więc pominięte review wyłapują dopiero wymagane checki CI
Review PRReview jest doradcze: publikuje komentarz i nigdy nie oblewa joba. Merge blokują wymagane checki (testy, typy, lint); review.md zostaje jako artefaktCode owner robi merge; bot nigdy nie zatwierdza
Zadanie od zależnościStrażnik zmian wersji, npm ci --ignore-scripts i npm test przechodzą w jobie bez sekretów; lockfile generuje npm, a nie agentRecenzent 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.

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 jobaCursor CLIClaude Code (2.1.283)Codex (0.157.1)
Przebieg headlessagent -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ówtimeout-minutes joba--max-budget-usdtimeout-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.