
Skille to za mało. Zbuduj własny plugin do GitHub Copilot CLI
Skill rozwiązuje problem wiedzy. Plugin rozwiązuje problem paczki
Skill to najprostszy sposób nauczenia agenta czegoś, czego nie wie: katalog z plikiem SKILL.md, w środku instrukcje. Działa świetnie — dopóki masz jeden skill i jedną maszynę.
Problem zaczyna się, kiedy masz pięć skilli, dwóch własnych agentów, hooka pilnującego formatowania i serwer MCP do wewnętrznego API. Nagle onboarding wygląda tak: skopiuj te katalogi tam, tamten plik tu, MCP dopisz ręcznie do configu. Pisałem już o tym, że dystrybucja jest częścią produktu — plugin jest odpowiedzią GitHuba na dokładnie ten problem.
Plugin to instalowalna paczka, która pakuje agentów, skille, hooki oraz konfiguracje MCP i LSP w jeden katalog z manifestem. Instaluje się jedną komendą, wersjonuje, aktualizuje i można ją rozdawać przez własny marketplace.
Poniżej pełny przepływ: od pustego katalogu do pluginu, który zespół instaluje jedną linijką.
Warstwa 1: skill, czyli najmniejsza jednostka
Zanim zbudujesz plugin, musisz umieć napisać jego zawartość. Skill to katalog z plikiem SKILL.md:
skills/└── ef-core-review/ └── SKILL.mdFrontmatter SKILL.md przyjmuje następujące pola:
| Pole | Typ | Wymagane | Opis |
|---|---|---|---|
name |
string | tak | Identyfikator. Litery, cyfry, myślniki. Max 64 znaki. |
description |
string | tak | Co robi i kiedy tego użyć. Max 1024 znaki. |
argument-hint |
string | nie | Podpowiedź argumentów w pickerze, np. [target] [mode]. |
allowed-tools |
string | string[] | nie | Narzędzia auto-dozwolone, gdy skill jest aktywny. * = wszystkie. |
user-invocable |
boolean | nie | Czy da się wywołać przez /NAZWA. Domyślnie true. |
disable-model-invocation |
boolean | nie | Blokuje automatyczne wywołanie przez agenta. Domyślnie false. |
Przykładowy SKILL.md:
---name: ef-core-reviewdescription: Review kodu Entity Framework Core pod kątem N+1, tracking i projekcji. Użyj przy przeglądzie repozytoriów, zapytań LINQ i migracji.allowed-tools: ["view", "glob", "rg"]---
## Kiedy używać
Przy każdym pliku, który zawiera `DbContext`, `IQueryable` lub metody repozytorium.
## Checklista
1. Czy zapytanie w pętli? To N+1 — zaproponuj `Include` albo projekcję.2. Czy odczyt jest tylko do prezentacji? Wymuś `AsNoTracking()`.3. Czy zwracamy encję zamiast DTO? Zaproponuj `Select` do typu wynikowego.4. Czy migracja usuwa kolumnę? Oznacz jako breaking i wymagaj dwuetapowego wdrożenia.Najważniejsze zdanie w całym pliku to description. Agent nie czyta wszystkich skilli za każdym razem — czyta ich opisy i na tej podstawie decyduje, co załadować. Opis w stylu „pomaga z EF Core“ nie odpali się nigdy. Opis, który mówi kiedy sięgnąć po skill, odpala się wtedy, kiedy trzeba.
Skille ładowane są z wielu lokalizacji: .github/skills/, .agents/skills/, .claude/skills/ w projekcie, ~/.copilot/skills/ i ~/.agents/skills/ globalnie, a na końcu z pluginów. Do tego zmienna COPILOT_SKILLS_DIRS. Zapamiętaj tę kolejność — wrócimy do niej przy precedencji, bo to źródło najgłupszych błędów.
Warstwa 2: anatomia pluginu
Plugin to katalog. Minimum to manifest plugin.json w jego korzeniu — reszta jest opcjonalna:
my-plugin/├── plugin.json # Wymagany manifest├── agents/ # Własni agenci (opcjonalnie)│ └── helper.agent.md├── skills/ # Skille (opcjonalnie)│ └── deploy/│ └── SKILL.md├── hooks.json # Konfiguracja hooków (opcjonalnie)└── .mcp.json # Konfiguracja serwerów MCP (opcjonalnie)Warto wiedzieć, że manifest jest szukany w czterech miejscach, w tej kolejności: .plugin/plugin.json, plugin.json, .github/plugin/plugin.json, .claude-plugin/plugin.json. Ostatnia ścieżka to zgodność wsteczna z pluginami Claude Code — jeśli już masz taki plugin, Copilot CLI go zobaczy.
Warstwa 3: plugin.json
Wymagane jest dokładnie jedno pole:
| Pole | Typ | Opis |
|---|---|---|
name |
string | Nazwa kebab-case (litery, cyfry, myślniki). Max 64 znaki. |
Metadane opcjonalne: description (max 1024 znaki), version, author (name wymagane, email i url opcjonalne), homepage, repository, license, keywords, category, tags.
Osobna grupa to pola ścieżek do komponentów. Wszystkie są opcjonalne — CLI stosuje konwencję, jeśli ich nie podasz:
| Pole | Typ | Domyślnie | Opis |
|---|---|---|---|
agents |
string | string[] | agents/ |
Katalogi z plikami .agent.md. |
skills |
string | string[] | skills/ |
Katalogi z plikami SKILL.md. |
commands |
string | string[] | — | Katalogi z komendami. |
hooks |
string | object | — | Ścieżka do pliku hooków albo obiekt inline. |
mcpServers |
string | object | — | Ścieżka do .mcp.json albo definicje inline. |
lspServers |
string | object | — | Konfiguracja serwerów LSP. |
extensions |
string | string[] | object | — | Katalogi rozszerzeń. { "paths": [...], "exclusive": true } wyłącza wbudowane. |
Kompletny przykład z dokumentacji:
{ "name": "my-dev-tools", "description": "React development utilities", "version": "1.2.0", "author": { "name": "Jane Doe", "email": "jane@example.com" }, "license": "MIT", "keywords": ["react", "frontend"], "agents": "agents/", "skills": ["skills/", "extra-skills/"], "hooks": "hooks.json", "mcpServers": ".mcp.json"}Zwróć uwagę, że skills przyjmuje tablicę. To pozwala trzymać w jednym pluginie skille z różnych źródeł — np. własne w skills/ i wygenerowane albo współdzielone w extra-skills/.
Budujemy plugin krok po kroku
Zrobimy dotnet-guard — plugin ze skillem do review EF Core, agentem do przeglądania migracji i hookiem, który przypomina o standardzie przy starcie sesji.
1. Manifest
{ "name": "dotnet-guard", "description": "Standardy review dla .NET: EF Core, migracje, testy integracyjne.", "version": "0.1.0", "author": { "name": "Platform Team", "email": "platform@example.com" }, "license": "MIT", "keywords": ["dotnet", "ef-core", "code-review"], "hooks": "hooks.json"}agents i skills pominięte celowo — konwencja agents/ i skills/ wystarczy.
2. Agent
Agent to plik NAZWA.agent.md w katalogu agents/. ID agenta bierze się z nazwy pliku, nie z pola name — plik migration-reviewer.agent.md daje agenta o ID migration-reviewer.
---name: migration-reviewerdescription: Przegląda migracje EF Core pod kątem zmian łamiących zgodność wstecznątools: ["bash", "view", "glob"]---
Jesteś recenzentem migracji bazodanowych. Dla każdej migracji:
1. Sklasyfikuj operacje: additive, in-place, destructive.2. Każdą operację destructive oznacz jako blokującą i zaproponuj wariant dwuetapowy (najpierw deploy kodu tolerującego oba schematy, potem usunięcie kolumny).3. Sprawdź, czy istnieje migracja `Down` i czy jest odwracalna.
Nie proponuj zmian w kodzie aplikacji poza tym, co wymusza podział na dwa etapy.3. Skill
Katalog skills/ef-core-review/SKILL.md — dokładnie ten z pierwszej sekcji.
4. Hook
hooks.json z wersją schematu i mapą zdarzeń. Dostępne zdarzenia: sessionStart, sessionEnd, userPromptSubmitted, preToolUse, postToolUse, agentStop, subagentStop, errorOccurred.
{ "version": 1, "hooks": { "sessionStart": [ { "type": "command", "bash": "echo 'dotnet-guard aktywny: review EF Core wg standardu zespolu'", "powershell": "Write-Output 'dotnet-guard aktywny: review EF Core wg standardu zespolu'", "timeoutSec": 5 } ] }}Pola wpisu hooka: type (zawsze "command"), bash i/lub powershell, opcjonalnie cwd, env i timeoutSec (domyślnie 30 sekund). Podanie obu wariantów daje przenośność — na Windowsie wykonuje się powershell, gdzie indziej bash. Na Windowsie potrzebny jest PowerShell 7+ (pwsh --version).
Do ścieżek wewnątrz pluginu używaj zmiennej ${PLUGIN_ROOT}, a na dane runtime’owe — ${COPILOT_PLUGIN_DATA}, czyli trwały, zapisywalny katalog przypisany do instalacji pluginu. Nie zapisuj niczego do katalogu z zainstalowanym pluginem: reinstalacja go nadpisze.
5. Instalacja lokalna
copilot plugin install ./dotnet-guardcopilot plugin i copilot plugins są wymienne — używaj tego, co lepiej brzmi.
6. Weryfikacja
copilot plugin listAlbo w sesji interaktywnej:
/plugin list/agent/skills listKażdy komponent sprawdzasz osobno: /agent pokaże agentów, /skills list skille. Jeśli plugin jest na liście, a jego skilla nie ma — to nie jest błąd instalacji, tylko konflikt nazw. Wracamy do tego niżej.
Pułapka numer jeden: cache
To jest miejsce, w którym traci się pierwszą godzinę.
Przy instalacji komponenty pluginu są cache’owane i CLI czyta z cache’u w kolejnych sesjach. Edytujesz SKILL.md, restartujesz sesję, nic się nie zmienia. Restart nie pomaga, bo cache przeżywa restart.
Jedyne, co odświeża zawartość, to ponowna instalacja:
copilot plugin install ./dotnet-guardPrzy iteracyjnym pisaniu pluginu wpisz to sobie w skrypt albo alias. Odinstalowanie wykonuje się po nazwie z manifestu, nie po ścieżce:
copilot plugin uninstall dotnet-guardPułapka numer dwa: precedencja
Tu jest niespodzianka, która potrafi zablokować cały zespół na wdrożeniu.
Agenci i skille działają w trybie first-found-wins. Wygrywa ten, który załadował się pierwszy — a pluginy są prawie na końcu kolejki:
AGENCI (dedup po ID z nazwy pliku) SKILLE (dedup po polu name)1. ~/.copilot/agents/ 1. <projekt>/.github/skills/2. <projekt>/.github/agents/ 2. <projekt>/.agents/skills/3. <rodzice>/.github/agents/ 3. <projekt>/.claude/skills/4. <projekt>/.claude/agents/ 4. <rodzice>/.github/skills/ itd.5. <rodzice>/.claude/agents/ 5. ~/.copilot/skills/6. PLUGIN: agents/ 6. ~/.agents/skills/7. Agenci zdalni org/enterprise 7. PLUGIN: skills/ 8. COPILOT_SKILLS_DIRSKonsekwencja jest brutalna: jeśli ktoś ma lokalny skill o tej samej nazwie co skill z twojego pluginu, skill z pluginu jest po cichu ignorowany. Bez ostrzeżenia. Plugin nie nadpisuje konfiguracji projektowej ani osobistej — nigdy.
Praktyczny wniosek: nazwy skilli i agentów w pluginie prefiksuj nazwą pluginu albo zespołu. dotnet-guard-ef-review zamiast review. Kosztuje kilkanaście znaków, oszczędza godzinę debugowania „dlaczego u mnie nie działa“.
Serwery MCP działają odwrotnie — last-wins. Plugin nadpisze serwer o tej samej nazwie zainstalowany wcześniej. Jeśli dwa pluginy deklarują ten sam serwer, wygrywa ten załadowany później, a CLI wypisuje ostrzeżenie z nazwami pozostałych. Nadpisać z linii poleceń możesz flagą --additional-mcp-config.
Wbudowanych narzędzi i agentów (bash, view, apply_patch, glob, rg, task, agenci explore, code-review, general-purpose, research) nie nadpiszesz niczym.
Dystrybucja: własny marketplace
Instalowanie z lokalnej ścieżki jest do developmentu. Do zespołu potrzebujesz marketplace’u — a to jest po prostu jeden plik JSON w repozytorium.
Utwórz .github/plugin/marketplace.json:
{ "name": "acme-copilot", "owner": { "name": "Acme Platform Team", "email": "platform@example.com" }, "metadata": { "description": "Wewnetrzne pluginy Copilota", "version": "1.0.0" }, "plugins": [ { "name": "dotnet-guard", "source": "./plugins/dotnet-guard", "description": "Standardy review dla .NET.", "version": "0.1.0", "license": "MIT" }, { "name": "security-checks", "source": { "source": "github", "repo": "acme/copilot-security", "sha": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "path": "plugins/security-checks" }, "description": "Kontrole bezpieczenstwa przed commitem.", "version": "1.3.0" } ]}Katalog plugins/dotnet-guard wrzucasz do tego samego repozytorium. Zespół rejestruje marketplace jedną komendą:
copilot plugin marketplace add acme/copilot-pluginsi instaluje z niego:
copilot plugin install dotnet-guard@acme-copilotNazwa rejestracji marketplace’u bierze się z pola name w marketplace.json — nie da się nadać lokalnego aliasu.
source przyjmuje ścieżkę względną, obiekt github (repo, ref, path, sha) albo URL Gita. Pinowanie do sha — pełne 40 znaków — daje instalacje odporne na force-push i przesunięcie tagu. Przy pluginach rozdawanych na firmowy sprzęt traktuj to jako domyślne, nie jako opcję.
Instalować można też bez marketplace’u:
| Format | Przykład |
|---|---|
| Marketplace | plugin@marketplace |
| Repozytorium GitHub | OWNER/REPO |
| Podkatalog repozytorium | OWNER/REPO:PATH/TO/PLUGIN |
| URL Gita | https://github.com/o/r.git |
| Ścieżka lokalna | ./my-plugin lub /abs/path |
Aktualizacje i przełącznik, o którym warto wiedzieć
copilot plugin update dotnet-guardcopilot plugin update --allPluginy first-party — z wbudowanych marketplace’ów copilot-plugins i awesome-copilot — aktualizują się same na starcie sesji w zaufanym katalogu roboczym. Wyłącza to ustawienie autoUpdate: false albo zmienna COPILOT_AUTO_UPDATE=false. W CI auto-update jest domyślnie pomijany.
Twój własny marketplace może się dopisać do tego mechanizmu przez autoUpdate: true we wpisie extraKnownMarketplaces w ustawieniach użytkownika. Istotny szczegół bezpieczeństwa: to opt-in działa wyłącznie z ustawień użytkownika — ustawienie repozytoryjne ani zarządzane przez MDM nie może włączyć ani przekierować auto-update’u dla marketplace’u. To sensowna decyzja projektowa: gdyby repozytorium mogło to włączyć, wystarczyłby jeden PR, żeby dostać automatyczne wykonanie kodu na maszynach wszystkich, którzy je sklonują.
Plugin możesz też wyłączyć bez odinstalowywania:
copilot plugin disable dotnet-guardcopilot plugin enable dotnet-guardZanim wypchniesz to zespołowi
Plugin to nie konfiguracja. To kod, który wykonuje się na maszynach zespołu — hooki uruchamiają komendy shellowe, serwery MCP to procesy, a SKILL.md to instrukcje, według których działa agent z dostępem do twojego repozytorium.
Minimum, które warto mieć:
- Pinowanie do
shawmarketplace.jsonzamiast do gałęzi. Tag i branch da się przesunąć, commit nie. - Review na katalogu pluginu jako osobna polityka ścieżkowa, z zakazem samozatwierdzania. Zmiana w
hooks.jsonto zmiana w kodzie wykonywalnym. - Kontrola diffa w CI: poza hookami i skryptami tylko
.mdi.json, blokada wzorców typuInvoke-ExpressionczyDownloadString. - Prefiksowanie nazw komponentów, żeby precedencja nie wycinała ci skilli po cichu.
- Wersjonowanie i możliwość rollbacku — plugin z błędnym skillem potrafi popsuć review w całym zespole w jeden poranek.
Szerzej o tym, dlaczego kanał dystrybucji jest równocześnie powierzchnią ataku, pisałem w artykule o dystrybucji skilli w zespole.
Ściąga komend
| Komenda | Działanie |
|---|---|
copilot plugin install SPEC |
Instalacja (marketplace, repo, URL, ścieżka) |
copilot plugin install --skill ./s/SKILL.md |
Instalacja pojedynczego skilla (bez marketplace’u) |
copilot plugin install --skill --scope project ... |
Skill do .github/skills w repozytorium |
copilot plugin uninstall NAME |
Usunięcie (po nazwie z manifestu) |
copilot plugin list |
Lista zainstalowanych |
copilot plugin update NAME / --all |
Aktualizacja |
copilot plugin enable / disable NAME |
Włączenie / wyłączenie bez usuwania |
copilot plugin marketplace add SOURCE |
Rejestracja marketplace’u |
copilot plugin marketplace browse NAME |
Przegląd katalogu pluginów |
copilot plugin marketplace update [NAME] |
Odświeżenie katalogu (alias refresh) |
copilot plugin marketplace remove NAME [--force] |
Wyrejestrowanie (--force usuwa też pluginy) |
/plugin list, /agent, /skills list |
Weryfikacja w sesji interaktywnej |
Lokalizacje, które warto znać: zainstalowane pluginy leżą w ~/.copilot/installed-plugins/MARKETPLACE/NAZWA (albo _direct/SOURCE-ID/ przy instalacji bezpośredniej), cache marketplace’ów w katalogu cache systemu (~/.cache/copilot/marketplaces/ na Linuksie, ~/Library/Caches/copilot/marketplaces/ na macOS), nadpisywalny przez COPILOT_CACHE_HOME.
Wniosek
Skill odpowiada na pytanie „co agent ma wiedzieć“. Plugin odpowiada na pytanie „jak to trafi do trzydziestu osób i jak zostanie zaktualizowane“ — a to jest pytanie, na którym w praktyce wykłada się większość wewnętrznych standardów.
Jeśli masz dziś więcej niż dwa skille i choć jednego własnego agenta, przepakowanie tego w plugin to praca na jedno popołudnie. Manifest ma jedno wymagane pole. Cała reszta to konwencja katalogów, którą i tak już masz.
Dwie rzeczy, które warto sobie zapisać na ścianie: cache trzeba przebić reinstalacją, a plugin przegrywa każdy konflikt nazw z konfiguracją lokalną, po cichu. Reszta jest prosta.
Źródła: Creating a plugin for Copilot CLI, Copilot CLI plugin reference, Creating a plugin marketplace, About plugins, Agent skills, Hooks. Stan dokumentacji: sierpień 2026.
Nowe materiały prosto na maila
Publikuję nowy artykuł albo lekcję kursu — dostajesz jednego maila. Nic poza tym.
Bez cyklu wydawniczego i newsów o każdej premierze modelu
Zapisy do newslettera otwieram partiami, żeby nie zamieniać go w kolejny kanał ze spamem i recyklingiem linków.
Jeśli chcesz dostać informację przy następnym otwarciu zapisów, napisz na kontakt@efektywnydev.pl.

O autorze
Marcin Szymański
Od kilkunastu lat buduję systemy w .NET i Azure — obecnie rozproszony monitoring PingView. Piszę tu o pracy człowiek + agenci AI na realnych workflow, nie na demach. Zawodowo prowadzę audyty, rescue projektów i wdrożenia AI-assisted delivery w zespołach.