Skip to main content

플러그 인 디렉터리

플러그 인 디렉터리를 사용하여 단일 매니페스트에서 기술, 후크, MCP 서버, 사용자 지정 에이전트 및 LSP 설정을 로드합니다.

이 가이드에서는 플러그 인 폴더 레이아웃, 디렉터리에서 플러그 인을 로드하는 방법, 플러그 인 디렉터리를 사용하는 경우와 개별 확장을 등록하는 경우 및 플러그 인 집합을 결정적으로 만드는 방법을 설명합니다.

플러그 인 디렉터리를 사용하는 경우

다음을 수행하려는 경우 플러그 인 디렉터리를 사용합니다.

  • 기능 번들을 하나의 단위로 배포합니다( 예: 기술이 있는 "TypeScript 검토자" 팩, preToolUse lint를 적용하는 후크 및 검토자를 실행하는 사용자 지정 에이전트).
  • 공급업체 기능은 리포지토리에 압축 되므로 호스트 애플리케이션의 모든 클론이 동일한 확장을 결정적으로 로드합니다.
  • 마켓플레이스에 게시하기 전에 로컬로 플러그 인을 개발합니다.
  • 테스트를 위해 로컬 체크 아웃을 사용하여 마켓플레이스 설치 플러그 인을 재정의하거나 확장합니다.

단일 MCP 서버, 단일 후크 또는 단일 사용자 지정 에이전트만 추가해야 하는 경우 SDK 구성(mcpServers, hooks, customAgents)을 통해 인라인으로 등록할 수 있습니다. 플러그 인 디렉터리를 함께 제공하는 세 개 이상의 관련 확장이 있으면 가장 유용합니다.

플러그 인 폴더 레이아웃

Copilot CLI는 각 플러그 인 디렉터리에서 plugin.json 매니페스트 또는 루트 수준 SKILL.md 검사합니다. 최소 플러그 인은 다음과 같습니다.

my-plugin/
├── plugin.json              # manifest (required unless using SKILL.md only)
├── SKILL.md                 # optional: top-level skill
├── hooks.json               # optional: hooks config
├── .mcp.json                # optional: MCP server config
├── agents/                  # optional: custom agents (one .md file per agent)
│   └── code-reviewer.md
└── skills/                  # optional: additional skills
    └── lint-fix/
        └── SKILL.md

매니페스트는 .github/plugin.json 또는 .github/plugin/plugin.json에도 있을 수 있으므로, 플러그인이 기존 리포지토리 내부에 루트 레이아웃을 변경하지 않고도 위치할 수 있습니다. 각 하위 시스템(후크, MCP, LSP, 기술, 에이전트)에는 자체 로더가 있으며 선택 사항입니다. 플러그 인에는 기여하는 부분만 필요합니다.

전체 매니페스트 스키마는 CLI의 /plugin 슬래시 명령에서 참조되는 런타임 설명서를 참조하세요.

SDK에서 플러그 인 디렉터리 로드

플러그인 디렉터리는 SDK가 Copilot CLI를 생성할 때 --plugin-dir <path>를 전달하여 로드됩니다. 각 언어는 런타임 연결의 추가 인수 옵션을 통해 이를 노출합니다. 플래그를 반복하여 여러 플러그 인을 로드할 수 있습니다.

TypeScript
import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk";

async function main() {
  const client = new CopilotClient({
    connection: RuntimeConnection.forStdio({
      args: [
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
      ],
    }),
  });

  await client.start();
}

main();
import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk";

const client = new CopilotClient({
  connection: RuntimeConnection.forStdio({
    args: [
      "--plugin-dir", "./plugins/code-reviewer",
      "--plugin-dir", "./plugins/lint-fix",
    ],
  }),
});

await client.start();
Python
from copilot import CopilotClient, StdioRuntimeConnection

client = CopilotClient(
    connection=StdioRuntimeConnection(
        args=(
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        ),
    ),
)
await client.start()
Go
package main

import (
    "context"

    copilot "github.com/github/copilot-sdk/go"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(&copilot.ClientOptions{
        Connection: copilot.StdioConnection{
            Args: []string{
                "--plugin-dir", "./plugins/code-reviewer",
                "--plugin-dir", "./plugins/lint-fix",
            },
        },
    })
    if err := client.Start(ctx); err != nil {
        return
    }
}
client := copilot.NewClient(&copilot.ClientOptions{
    Connection: copilot.StdioConnection{
        Args: []string{
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        },
    },
})
if err := client.Start(ctx); err != nil {
    return err
}
.NET
using GitHub.Copilot;

await using var client = new CopilotClient(new CopilotClientOptions
{
    Connection = RuntimeConnection.ForStdio(args: new[]
    {
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
    }),
});

await client.StartAsync();
Java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.CopilotClientOptions;

public class PluginDirectoriesExample {
    public static void main(String[] args) throws Exception {
        var options = new CopilotClientOptions()
            .setCliArgs(new String[] {
                "--plugin-dir", "./plugins/code-reviewer",
                "--plugin-dir", "./plugins/lint-fix",
            });

        var client = new CopilotClient(options);
        client.start().get();
    }
}
var options = new CopilotClientOptions()
    .setCliArgs(new String[] {
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
    });

var client = new CopilotClient(options);
client.start().get();
Rust
use github_copilot_sdk::{Client, ClientOptions};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _client = Client::start(
        ClientOptions::new().with_extra_args([
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        ]),
    )
    .await?;
    Ok(())
}
use github_copilot_sdk::{Client, ClientOptions};

let client = Client::start(
    ClientOptions::new().with_extra_args([
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
    ]),
)
.await?;

위의 예제에서는 SDK가 CLI를 번들로 묶을 때 기본값인 stdio 런타임 연결을 사용합니다. URL(forUri / ForUri)을 통해 외부 런타임에 연결하는 경우, 시작할 때 장시간 실행되는 CLI 서버에 --plugin-dir를 전달해야 합니다. SDK는 자체적으로 생성하지 않은 런타임에는 --plugin-dir를 전달하지 않습니다.

플러그 인이 기여할 수 있는 사항

플러그 인 디렉터리를 로드하면 해당 확장이 클라이언트에서 만든 모든 세션에 표시됩니다. 런타임은 플러그인이 제공하는 확장을 인라인으로 등록한 모든 확장과 병합합니다:

플러그 인 기여세션에 다음과 같이 표시
기술(SKILL.md, skills/*/SKILL.md)
session.skills.list()의 항목; 이름으로 주입 가능
사용자 지정 에이전트(agents/*.md)
task(agent_type=...) 도구를 통해 전송 가능
고리 (hooks.json)SDK를 통해 등록된 후크와 함께 실행됨
MCP 서버(.mcp.json)
session.mcp.*을 통해 액세스할 수 있는 도구 및 리소스
LSP 서버(.lsp.json)
session.lsp.initialize(...)을 통해 초기화됨

플러그 인 에이전트는 플릿 모드의 일류 하위 에이전트입니다. 부모 에이전트는 이를 디스패치 agent_type할 수 있으며 런타임은 다른 하위 에이전트와 마찬가지로 후크를 실행 subagentStart / subagentStop 합니다.

플러그 인-dir 및 마켓플레이스 플러그 인

런타임에는 플러그 인을 설치하는 두 가지 방법이 있으며, 둘 다 세션과 동일하게 표시됩니다.

  • Marketplace/직접 리포지토리 플러그 인 은 CLI의 /plugin 슬래시 명령 또는 기본 installedPlugins 사용자 설정을 통해 영구적으로 설치됩니다. 그것들은 앰비언트이며, 동일한 사용자 구성을 사용하는 모든 세션에서 이를 인식할 수 있고 플러그인 검색 규칙의 적용을 받습니다.
  • --plugin-dir 플러그 인명시적이고 임시적입니다 . 해당 플래그를 사용하여 시작한 CLI 프로세스에만 적용됩니다. 이들은 자동 검색보다 우선하며, 동일한 캐시 경로를 가진 마켓플레이스 항목과는 중복이 제거되므로 양쪽에서 모두 참조하더라도 동일한 플러그인이 두 번 로드되지 않습니다.

SDK 기반 애플리케이션의 경우 일반적으로 --plugin-dir가 올바른 선택입니다. 애플리케이션은 컴퓨터별 사용자 상태에 의존하지 않고 플러그인 세트를 자체적으로 제어할 수 있기 때문입니다.

플러그인 집합을 결정론적으로 만들기

호스트 머신에 다른 플러그인(마켓플레이스 또는 개인용)이 설치되어 있을 수 있는 경우, 자동 플러그인 검색을 억제하려면 런타임 환경에서 COPILOT_PLUGIN_DIR_ONLY=true를 설정하세요. --plugin-dir를 통해 전달한 디렉터리만 로드됩니다.

Node.js/ TypeScript
import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk";

async function main() {
  process.env.COPILOT_PLUGIN_DIR_ONLY = "true";
  const client = new CopilotClient({
    connection: RuntimeConnection.forStdio({
      args: ["--plugin-dir", "./plugins/code-reviewer"],
    }),
  });
  await client.start();
}

main();
process.env.COPILOT_PLUGIN_DIR_ONLY = "true";

const client = new CopilotClient({
  connection: RuntimeConnection.forStdio({
    args: ["--plugin-dir", "./plugins/code-reviewer"],
  }),
});
await client.start();

CI, 헤드리스 서버 배포 및 호스트의 사용자 구성에 의존하지 않는 재현 가능한 플러그 인 집합을 원하는 모든 위치에서 사용합니다.

로드된 플러그 인 검사

세션이 만들어지면 활성 플러그 인을 나열하여 디렉터리가 올바르게 선택되었는지 확인합니다.

Node.js/ TypeScript
import { CopilotClient } from "@github/copilot-sdk";

async function main() {
  const client = new CopilotClient();
  await client.start();
  const session = await client.createSession({
    onPermissionRequest: async () => ({ kind: "approve-once" }),
  });

  const plugins = await session.rpc.plugins.list();
  for (const plugin of plugins.plugins) {
    console.log(`${plugin.name} (${plugin.enabled ? "enabled" : "disabled"})`);
  }
}

main();
const plugins = await session.rpc.plugins.list();
for (const plugin of plugins.plugins) {
  console.log(`${plugin.name} (${plugin.enabled ? "enabled" : "disabled"})`);
}

통해 로드된 --plugin-dir 플러그 인은 제공한 디렉터리로 설정된 캐시 경로와 함께 이 목록에 표시됩니다. Marketplace에서 설치된 항목에는 해당 레지스트리 출처 태그가 붙습니다.

Troubleshooting

  • "dir<에 >plugin.json 또는 SKILL.md 없습니다." - 디렉터리가 존재하지만 플러그 인으로 한정되지 않습니다. plugin.json 루트(또는 아래.github/)에 매니페스트를 추가하거나 최상위 수준을 SKILL.md포함합니다.
  • 플러그 인이 로드되었지만 에이전트/기술이 표시되지 않음 - 플러그 인 매니페스트가 기여하는 에이전트/기술을 선언하는지 확인하거나 암시적 레이아웃(agents/*.md, skills/*/SKILL.md)을 사용합니다. 그런 다음 다시 시작하지 않고 변경 내용을 선택하도록 호출 session.rpc.skills.reload() 합니다.
  • 중복 후크 실행 — 런타임은 cache_path를 통해 중복을 제거하지만, 동일한 디렉터리가 마켓플레이스 설치와 --plugin-dir 둘 다로 참조되는 경우에만 그렇습니다. 서로 다른 두 디렉터리에 동일한 플러그 인이 포함되어 있으면 둘 다 로드됩니다. 하나를 제거하거나 COPILOT_PLUGIN_DIR_ONLY=true를 사용하세요.
  • --plugin-dir 는 외부 런타임에 연결할 때 무시됩니다 . SDK는 CLI 자체를 생성할 때만 추가 인수를 전달합니다. 외부 런타임(forUri/ForUri)의 경우 런타임 서버를 시작하는 명령줄을 전달 --plugin-dir 합니다.