Przejdź do głównej zawartości

Praca z wieloma repozytoriami w Cursorze

Praca z wieloma repozytoriami w Cursorze polega na tym, by Agent widział kontrakty innych repozytoriów, a granice własności pozostały wyraźne. Repozytoria zmieniane razem otwiera się jako jeden workspace z wieloma folderami, z repozytoriów tylko konsumowanych trzyma się przypięte migawki kontraktów, a każda zmiana obejmująca kilka repozytoriów zaczyna się od kontraktu i przechodzi przez CI każdego z nich.

Ta strona jest dla programistów i tech leadów, których serwisy są w osobnych repozytoriach. Masz dodać phoneNumber do profilu użytkownika. Zmiana dotyka shared-types, user-service, schematu bramki API i dwóch frontendów. Otwierasz Cursora w user-service, Agent nie widzi wspólnych typów, więc wymyśla UserDto, który prawie się zgadza. Trzy PR-y później aplikacja webowa kompiluje się względem pola, którego API nigdy nie zwraca.

Źródła tej strony, sprawdzone 2026-09-26: ładowanie wielu folderów i format plików reguł sprawdziliśmy w runtime agenta dołączonym do własnego pakietu Cursora @cursor/sdk 1.0.32. Jego opcja dirs dla lokalnego agenta jest opisana jako „Additional / multi-root workspace folders”, z których „project rules, skills, and request-context workspace metadata load from every unique path”. Adresy serwera GitHub MCP pochodzą z dokumentacji github-mcp-server. Polecenia openapi-typescript 7.13.0 i @graphql-inspector/cli 7.0.0 uruchomiliśmy lokalnie. Z naszego środowiska nie było dostępu do cursor.com, więc nazwy menu porównaj z własną wersją Cursora 3.x.

  • Tabelę decyzyjną: które repozytoria otworzyć, które trzymać jako migawkę, a które czytać przez MCP.
  • Wersjonowane repozytorium workspace’u z plikiem .code-workspace i jedną regułą własności, którą ładuje każda sesja agenta.
  • Kontrolę dryfu, która zatrzymuje CI, gdy dołączony kontrakt przestaje zgadzać się z wygenerowanymi typami, oraz zaplanowane sprawdzenie, które kończy się błędem, gdy upstream opublikuje nowszy tag kontraktu.
  • Kolejność pracy „najpierw kontrakt” z czterema promptami do skopiowania: planowanie, producent, konsumenci i sprawdzenie workspace’u.
  • Tabelę dowodów, dzięki której recenzent zatwierdza pięć powiązanych PR-ów po przeczytaniu jednego diffu kontraktu, a nie każdej wygenerowanej linii.

Które podejście do wielu repozytoriów pasuje do twoich repozytoriów?

Dział zatytułowany „Które podejście do wielu repozytoriów pasuje do twoich repozytoriów?”

Agent dobrze radzi sobie z tym, co może otworzyć, a słabo z tym, co musi zgadywać. Zasada: otwieraj to, co edytujesz; z tego, co tylko czytasz, trzymaj migawkę.

Twoja sytuacjaCo dajesz AgentowiDlaczego
Zmieniasz razem od dwóch do czterech repozytoriów i wszystkie są twojeWorkspace z wieloma folderamiAgent czyta i edytuje prawdziwy kod, a reguły ładują się z każdego folderu
Korzystasz z serwisu innego zespołu i nigdy go nie edytujeszPrzypiętą migawkę kontraktu i regułę, która na nią wskazujeAgent widzi dokładny kontrakt, a zaplanowane sprawdzenie mówi ci, kiedy upstream opublikuje nowszy tag
Od czasu do czasu musisz coś sprawdzić w repozytorium, którego nie klonujeszSerwer GitHub MCP w trybie tylko do odczytuBez klonowania, bez prawa zapisu, jedno zapytanie naraz
Wszystkie serwisy są w jednym repozytoriumNie ta stronaZobacz strategie dla dużych baz kodu w Cursorze i pracę z monorepo

Przewodnik po mikroserwisach w Cursorze omawia granice serwisów i reguły topologii. Ta strona omawia mechanikę, która pojawia się dopiero wtedy, gdy kod jest rozdzielony między repozytoria.

Skonfiguruj workspace z wieloma folderami dla repozytoriów zmienianych razem

Dział zatytułowany „Skonfiguruj workspace z wieloma folderami dla repozytoriów zmienianych razem”

Cursor otwiera pliki .code-workspace z VS Code. Umieść taki plik we własnym, małym repozytorium, żeby lista folderów i reguła własności były wersjonowane, recenzowane i wspólne dla całego zespołu.

  1. Sklonuj repozytoria obok siebie i utwórz obok nich repozytorium workspace’u:

    ~/src/acme/
    ├── platform-workspace/ # nowe: plik workspace'u + reguła dla całego workspace'u
    ├── shared-types/
    ├── user-service/
    ├── api-gateway/
    └── web-app/
  2. Zapisz platform-workspace/platform.code-workspace. Ścieżki są względne wobec pliku workspace’u, a name to etykieta każdego folderu, którą widzisz ty i Agent:

    {
    "folders": [
    { "path": ".", "name": "platform-workspace" },
    { "path": "../shared-types", "name": "shared-types" },
    { "path": "../user-service", "name": "user-service" },
    { "path": "../api-gateway", "name": "api-gateway" },
    { "path": "../web-app", "name": "web-app" }
    ],
    "settings": {
    "search.exclude": { "**/node_modules": true, "**/dist": true }
    }
    }
  3. Dodaj .cursorignore do każdego repozytorium, żeby artefakty buildu i wygenerowane paczki nie trafiały do kontekstu Agenta:

    dist/
    build/
    coverage/
    *.min.js
  4. Otwórz plik przez File › Open Workspace from File, a potem uruchom poniższy prompt sprawdzający w nowym czacie Agenta.

Jeśli w tabeli brakuje folderu, ścieżka w pliku workspace’u jest błędna. Nie polegaj na samej liście reguł, bo Agent może błędnie podać, co ma załadowane. Obserwowalnym sprawdzeniem jest cytat: jeśli odpowiedź parafrazuje linię „Dependency direction:” albo ją pomija, reguła nie jest załadowana. Jeśli brakuje reguły, sprawdź, czy leży w .cursor/rules/ i ma rozszerzenie .mdc: runtime ładuje reguły projektu z .cursor/rules/**/*.mdc. Ostatnia odpowiedź jest ważna, bo runtime SDK ma jeden główny folder dla poleceń powłoki. Licz się z tym, że npm test uruchomi się w jednym repozytorium, jeśli Agent wcześniej nie zmieni katalogu, i pisz polecenia weryfikacji z cd albo git -C, żeby nie mogły trafić w złe miejsce.

Napisz jedną regułę, która mówi, kto czym zarządza

Dział zatytułowany „Napisz jedną regułę, która mówi, kto czym zarządza”

Reguła, która kopiuje listy pól, dezaktualizuje się w tygodniu, w którym zmienia się kontrakt, a nieaktualna reguła jest gorsza niż żadna, bo Agent jej ufa. W regule zapisz własność, kierunek zależności i polecenia weryfikacji, a zamiast przepisywać kontrakty, wskaż ich pliki.

Zapisz platform-workspace/.cursor/rules/workspace-map.mdc:

---
description: Ownership, dependency direction and verify commands for the platform workspace
alwaysApply: true
---
Repositories in this workspace and what each owns:
- shared-types: the source of truth for API types. Published as @acme/shared-types.
- user-service: owns the users table and /api/users. Its contract is user-service/openapi.yaml.
- api-gateway: owns the public GraphQL schema in api-gateway/schema.graphql.
- web-app: consumes the gateway only. It never calls user-service directly.
Dependency direction: shared-types -> user-service -> api-gateway -> web-app.
A change always lands in that order. Never edit a repository to the left of the
one the task names unless the task says so.
Contract rules:
- Change a contract additively first (new optional field, new endpoint). Remove
or rename only in a later change, after every consumer has shipped.
- Read types from the contract files above. Never hand-write a copy of them.
Verify commands (run from the workspace root):
- shared-types: cd ../shared-types && npm test && npx tsc --noEmit
- user-service: cd ../user-service && npm test
- api-gateway: cd ../api-gateway && npm test
- web-app: cd ../web-app && npx tsc --noEmit && npm test

Projektowanie reguł w szczegółach, w tym reguły z globs dla pojedynczych serwisów, opisują reguły projektu w Cursorze oraz własne reguły i szablony.

Trzymaj przypięte migawki repozytoriów, które tylko konsumujesz

Dział zatytułowany „Trzymaj przypięte migawki repozytoriów, które tylko konsumujesz”

Gdy serwisem zarządza inny zespół, nie otwieraj jego kodu. Dołącz do repozytorium opublikowany kontrakt w otagowanej wersji, wygeneruj z niego typy i pozwól CI powiedzieć ci, kiedy migawka i typy przestają się zgadzać albo kiedy upstream idzie dalej.

Okno terminala
# scripts/sync-contracts.sh (uruchamiasz w katalogu głównym repozytorium konsumenta)
set -euo pipefail
TAG="${1:?usage: sync-contracts.sh <user-service tag>}"
gh api "repos/acme/user-service/contents/openapi.yaml?ref=${TAG}" \
-H "Accept: application/vnd.github.raw+json" > contracts/user-service.openapi.yaml
echo "${TAG}" > contracts/user-service.version
npx openapi-typescript contracts/user-service.openapi.yaml -o src/generated/user-service.ts

Bramka w CI to dwa polecenia. --check kończy się kodem 1 i wypisuje „Generated types are not up-to-date!”, gdy ktoś ręcznie zmieni migawkę albo wygenerowany plik. Sprawdzenie typów potwierdza potem, że kod klienta nadal kompiluje się względem kontraktu:

Okno terminala
npx openapi-typescript contracts/user-service.openapi.yaml -o src/generated/user-service.ts --check
npx tsc --noEmit

Ta bramka porównuje tylko dołączoną migawkę z wygenerowanymi z niej typami. Nigdy nie zagląda do samego user-service, więc nowy tag w upstreamie przechodzi niezauważony. Dryf względem upstreamu wyłapuj osobnym zadaniem uruchamianym według harmonogramu (codzienny cron w CI), które porównuje przypięty tag z najnowszym wydaniem:

Okno terminala
latest=$(gh release view --repo acme/user-service --json tagName -q .tagName)
[ "$latest" = "$(cat contracts/user-service.version)" ] || { echo "user-service $latest available"; exit 1; }

W CI daj temu zadaniu GH_TOKEN z dostępem tylko do odczytu do Contents w acme/user-service (token fine-grained albo token GitHub App z twojego magazynu sekretów). Domyślny GITHUB_TOKEN nie może czytać innych repozytoriów. Nadaj workflow uprawnienia permissions: contents: read.

Gdy zadanie się nie powiedzie, uruchom scripts/sync-contracts.sh z nowym tagiem i otwórz PR. Sprawdzenie typów w tym PR-ze pokaże, co psuje nowy kontrakt.

Dodaj regułę ograniczoną do plików, .cursor/rules/consumed-contracts.mdc, żeby Agent używał wygenerowanych typów, zamiast wymyślać własne:

---
description: How this repository consumes user-service
globs: src/clients/**/*.ts
alwaysApply: false
---
Types for user-service come only from src/generated/user-service.ts, which is
generated from contracts/user-service.openapi.yaml (version in
contracts/user-service.version). Never edit either file by hand.
If a field you need is missing from the generated types, stop and tell me.
Do not add it locally: the contract has to change upstream first.

Czytaj niesklonowane repozytorium przez serwer GitHub MCP

Dział zatytułowany „Czytaj niesklonowane repozytorium przez serwer GitHub MCP”

Przy jednorazowym pytaniu („jak serwis billingu podpisuje webhooki?”) klonowanie to przesada. Serwer MCP od GitHuba czyta pliki i przeszukuje kod w każdym repozytorium, do którego ma dostęp twój token. Użyj zestawu narzędzi repos w trybie tylko do odczytu, żeby sesja nie mogła pushować, komentować ani zakładać issues. Dodaj go do swojego pliku użytkownika ~/.cursor/mcp.json, a nie do pliku w repozytorium, żeby token pozostał prywatny:

{
"mcpServers": {
"github-read": {
"url": "https://api.githubcopilot.com/mcp/x/repos/readonly",
"headers": { "Authorization": "Bearer ${env:GITHUB_READ_TOKEN}" }
}
}
}

Utwórz GITHUB_READ_TOKEN jako szczegółowy (fine-grained) personal access token z dostępem tylko do odczytu do Contents w potrzebnych repozytoriach i wyeksportuj go w powłoce, z której uruchamiasz Cursora. Odwołanie ${env:…} trzyma token poza plikiem. Wszystko, co zwraca serwer, traktuj jako niezaufane dane wejściowe: plik w innym repozytorium może zawierać instrukcje skierowane do twojego agenta, co jest kolejnym powodem, by zestaw narzędzi był tylko do odczytu. Przewodnik po serwerze GitHub MCP opisuje pozostałe zestawy narzędzi i lokalną instalację w Dockerze.

Jeśli czytasz to samo repozytorium codziennie, przestań je odpytywać. Sklonuj je i dodaj do workspace’u albo zrób migawkę jego kontraktu.

Wprowadzaj zmianę w wielu repozytoriach, zaczynając od kontraktu

Dział zatytułowany „Wprowadzaj zmianę w wielu repozytoriach, zaczynając od kontraktu”

Zmiana obejmująca kilka repozytoriów psuje się w szczelinach między PR-ami, więc kolejność jest ważniejsza niż którykolwiek pojedynczy prompt. Zaplanuj całą zmianę raz, a potem implementuj ją po jednym repozytorium, każde w osobnym czacie Agenta, żeby każdy diff mieścił się w jednym PR-ze.

  1. Zaplanuj w całym workspace. Przełącz czat w Plan Mode, który „creates detailed implementation plans before writing any code”, i wklej poniższy prompt planujący. Po zatwierdzeniu planu zapisz go jako platform-workspace/plans/phone-number.md (na przykład przełącz się w tryb Agent i poproś go o zapisanie pliku), żeby każdy kolejny czat mógł się do niego odwołać.

  2. Zmień kontrakt addytywnie. W shared-types (albo w pliku OpenAPI producenta) dodaj pole jako opcjonalne i opublikuj wersję przedpremierową, na przykład @acme/shared-types@2.4.0-rc.1. Przed wydaniem finalnym sprawdź ją bramką: dla pakietu TypeScript zainstaluj rc w każdym konsumencie i uruchom npx tsc --noEmit bez zmian w kodzie. Musi przejść, bo pole opcjonalne niczego nie psuje. Dla kontraktów OpenAPI lub GraphQL uruchom diff wykrywający zmiany niekompatybilne wstecz (breaking changes) z tabeli dowodów poniżej.

  3. Zaimplementuj zmianę u producenta. W nowym czacie ograniczonym do user-service uruchom prompt dla producenta. Jego PR łączysz, gdy jego własne CI jest zielone.

  4. Dostosuj każdego konsumenta. Jeden czat na konsumenta, wskazujący na wersję przedpremierową. Każdy PR konsumenta pokazuje sprawdzenie typów i testy względem nowego kontraktu.

  5. Łącz w kolejności zależności, potem posprzątaj. Kontrakt, potem producent, potem konsumenci. Usunięcie albo zmiana nazwy starego kształtu to osobna zmiana, po wdrożeniu wszystkich konsumentów.

Żeby prowadzić konsumentów równolegle, trzymaj jeden czat na repozytorium. Worktrees w Cursorze „let Agent work in isolated Git checkouts” (dokumentacja Cursora, sprawdzone 2026-08-28). Worktree Gita zawsze należy do jednego repozytorium, więc Worktrees nadają się do równoległych prób w obrębie repozytorium, a nie do zmiany obejmującej kilka repozytoriów. W Agents Window obserwujesz kilka takich sesji obok siebie. Nienadzorowane uruchomienia tej samej zmiany w wielu repozytoriach opisuje strona o Cloud Agents i Automations.

Jak udowodnić, że zmiana w wielu repozytoriach działa, bez czytania każdego diffu?

Dział zatytułowany „Jak udowodnić, że zmiana w wielu repozytoriach działa, bez czytania każdego diffu?”

Nikt nie powinien czytać pięciu wygenerowanych diffów linia po linii. Recenzent czyta diff kontraktu, który jest mały, a resztę przyjmuje na podstawie dowodów z CI każdego repozytorium:

DowódPolecenie lub źródłoCo wyłapuje
Zmiana kontraktu nie jest niekompatybilna wstecznpx @graphql-inspector/cli diff old.graphql new.graphql kończy się kodem 1 przy zmianie niekompatybilnej i 0 przy addytywnej; dla OpenAPI użyj diffu zmian niekompatybilnych z przewodnika po mikroserwisachUsunięte albo przemianowane pole, które wślizgnęło się do kroku „addytywnego”
Dołączone migawki zgadzają się z wygenerowanymi typaminpx openapi-typescript … --checkRęcznie zmienioną migawkę albo wygenerowany plik
Przypięty kontrakt jest najnowszyZaplanowane sprawdzenie gh release view opisane wyżejKonsumenta przypiętego do nieaktualnego tagu upstreamu
Każdy konsument kompiluje się względem nowego kontraktunpx tsc --noEmit w każdym konsumencieKonsumenta, który polega na starym kształcie
Oczekiwania konsumentów są spełnione po stronie producentaTesty kontraktowe sterowane przez konsumenta z @pact-foundation/pact, weryfikowane w CI producentaZmianę producenta, która przechodzi sprawdzenie typów, ale zwraca złe dane
Każde repozytorium nadal działaWłasne CI każdego PR-a, na zielonoRegresje niezwiązane z kontraktem

Powiązane PR-y, kolejność łączenia i wynik każdej bramki umieść w opisie PR-a z kontraktem, w formacie pakietu dowodów. Zatwierdza ten, kto jest właścicielem: właściciel kontraktu (wskazany w CODEOWNERS dla pliku schematu) zatwierdza PR z kontraktem po przeczytaniu jego diffu. Właściciel każdego repozytorium zatwierdza PR swojego repozytorium na podstawie jego dowodów.

Co psuje się przy pracy z wieloma repozytoriami w Cursorze?

Dział zatytułowany „Co psuje się przy pracy z wieloma repozytoriami w Cursorze?”

Agent edytuje repozytorium, którego zadanie nie wskazało. W workspace z wieloma folderami każdy folder jest zapisywalny. Przywróć zabłąkane pliki z checkpointu czatu albo przez git -C ../api-gateway restore ., a potem uruchom git -C ../api-gateway status --short i usuń nieśledzone pliki z listy (git -C ../api-gateway clean -n pokazuje podgląd, clean -f usuwa): restore przywraca tylko pliki śledzone. Potem ograniczaj czaty implementacyjne do jednego repozytorium i nazywaj dozwolony folder w pierwszej linii promptu, tak jak robią to powyższe prompty.

Test przechodzi w niewłaściwym repozytorium. Agent uruchomił npm test w głównym folderze, a nie w tym, który zmienił. Dodaj cd ../<repo> && albo git -C do każdego polecenia weryfikacji w regule i proś o kod wyjścia każdego polecenia, a nie o podsumowanie.

Reguła w ogóle się nie ładuje. Uruchom prompt sprawdzający workspace. Reguła, której nie widać, to zwykle plik .md zamiast .mdc, plik poza .cursor/rules/ albo zepsuty frontmatter.

Migawka rozjeżdża się z upstreamem. Konsument kompiluje się względem kontraktu sprzed miesiąca. Bramka --check tego nie widzi: wyłapuje tylko lokalne ręczne zmiany. Uruchom opisane wyżej zaplanowane sprawdzenie upstreamu, uruchom ponownie scripts/sync-contracts.sh z nowym tagiem i pozwól Agentowi naprawić błędy typów, które się pojawią. Ustaw bramkę --check jako wymagany status CI, żeby ręczne zmiany też nie przeszły.

Zmiana niekompatybilna trafiła do main przed konsumentami. Pole zmieniło nazwę, zamiast zostać dodane. Najpierw cofnij PR z kontraktem. Potem wprowadź go ponownie jako: dodaj, zmigruj konsumentów, usuń później. Konsumenci, którzy już zostali wdrożeni, działają dalej, bo revert przywraca stary kształt.

Konsumenci są wdrażani przed producentem. Aplikacja webowa prosi o phoneNumber i dostaje undefined. Pola opcjonalne i dopuszczające null sprawiają, że nic się nie psuje, i właśnie dlatego prompt planujący wymaga uzasadnienia wstecznej zgodności na każdym kroku. Przy zmianach zachowania schowaj kod konsumenta za flagą, dopóki producent nie działa na produkcji.

Workspace jest wolny, a odpowiedzi Agenta wciągają niezwiązany kod. Za dużo folderów albo za mało wykluczeń. Usuń foldery, które tylko czytasz, i zastąp je migawkami, a każdy .cursorignore rozszerz.