Przejdź do treści
Skille to za mało. Zbuduj własny plugin do GitHub Copilot CLI
··12 min czytania

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

Frontmatter 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-review
description: 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-reviewer
description: 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

Terminal window
copilot plugin install ./dotnet-guard

copilot plugin i copilot plugins są wymienne — używaj tego, co lepiej brzmi.

6. Weryfikacja

Terminal window
copilot plugin list

Albo w sesji interaktywnej:

/plugin list
/agent
/skills list

Każ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:

Terminal window
copilot plugin install ./dotnet-guard

Przy iteracyjnym pisaniu pluginu wpisz to sobie w skrypt albo alias. Odinstalowanie wykonuje się po nazwie z manifestu, nie po ścieżce:

Terminal window
copilot plugin uninstall dotnet-guard

Puł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_DIRS

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

Terminal window
copilot plugin marketplace add acme/copilot-plugins

i instaluje z niego:

Terminal window
copilot plugin install dotnet-guard@acme-copilot

Nazwa 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ć

Terminal window
copilot plugin update dotnet-guard
copilot plugin update --all

Pluginy 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:

Terminal window
copilot plugin disable dotnet-guard
copilot plugin enable dotnet-guard

Zanim 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 sha w marketplace.json zamiast 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.json to zmiana w kodzie wykonywalnym.
  • Kontrola diffa w CI: poza hookami i skryptami tylko .md i .json, blokada wzorców typu Invoke-Expression czy DownloadString.
  • 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.

UdostępnijX / TwitterLinkedIn
Marcin Szymański

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.