# 플러그 인 디렉터리

플러그 인은 SDK 확장(기술, 후크, MCP 서버, 사용자 지정 에이전트 및 LSP 구성)을 단일 매니페스트 뒤에 번들로 묶는 디렉터리입니다. 플러그 인 디렉터리에서 SDK를 가리키면 플러그 인이 기여하는 모든 항목이 로드되므로 모든 호스트 애플리케이션에서 확장별 배선을 작성하지 않고도 재사용 가능한 기능 팩을 제공할 수 있습니다.

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

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

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

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

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

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

## 플러그 인 폴더 레이아웃

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

```text
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>`를 전달하여 로드됩니다. 각 언어는 런타임 연결의 추가 인수 옵션을 통해 이를 노출합니다. 플래그를 반복하여 여러 플러그 인을 로드할 수 있습니다.

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
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();
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

<!-- docs-validate: wrap-async -->

```python
from copilot import CopilotClient, StdioRuntimeConnection

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

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
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
}
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
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();
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

```java
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();
```

</div>

<div class="ghd-codetab" data-lang="rust" data-label="Rust"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Rust</div>

```rust
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?;
```

</div>

</div>

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

## 신뢰할 수 있는 호스트 번들 플러그 인 디렉터리

자체 신뢰할 수 있는 플러그 인을 제공하는 애플리케이션은 클라이언트 시작 옵션으로 등록할 수 있습니다. SDK는 연결을 설정하고 프로토콜을 검증한 후, `start`가 반환되기 전이자 어떠한 세션도 생성되기 전에 완전한 ordered set을 전송합니다. 경로는 절대 경로여야 합니다. 옵션을 설정하지 않거나 비워 두면 RPC 호출이 발생하지 않습니다.

각 SDK의 동등한 옵션은 다음과 같습니다.

| SDK                 | 시작 옵션                                                 |
| ------------------- | ----------------------------------------------------- |
| Node.js/ TypeScript | `builtinPluginDirectories: string[]`                  |
| Python              | `builtin_plugin_directories=[...]`                    |
| Go                  | `BuiltinPluginDirectories: []string{...}`             |
| .NET                | `BuiltinPluginDirectories = [...]`                    |
| Java                | `.setBuiltinPluginDirectories(List.of(Path.of(...)))` |
| 러스트                 | `.with_builtin_plugin_directories([...])`             |

호스트 애플리케이션에서 번들로 묶고 제어하는 플러그 인에 대한 신뢰 경계입니다. 일반 플러그 인 디렉터리를 명시적으로 로드하기 위한 CLI 프로세스 시작 인수인 이 인수와는 다릅니다 `--plugin-dir`. 시작 옵션은 프로세스 인수로 전달되지 않고 JSON-RPC 통해 전송되므로 기존 런타임에 연결할 때도 작동합니다.

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

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

| 플러그 인 기여                                | 세션에 다음과 같이 표시          |
| --------------------------------------- | ---------------------- |
| 기술(`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(...)`을 통해 초기화됨  |                        |

플러그 인 에이전트는 [플릿 모드](/ko/copilot/how-tos/copilot-sdk/features/fleet-mode)의 일류 하위 에이전트입니다. 부모 에이전트는 이를 디스패치 `agent_type`할 수 있으며 런타임은 다른 하위 에이전트와 마찬가지로 후크를 실행 `subagentStart` / `subagentStop` 합니다.

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

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

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

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

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

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

<details open>
<summary>
<strong>Node.js/ TypeScript</strong></summary>

```typescript
process.env.COPILOT_PLUGIN_DIR_ONLY = "true";

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

</details>

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

## 로드된 플러그 인 검사

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

<details open>
<summary>
<strong>Node.js/ TypeScript</strong></summary>

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

</details>

통해 로드된 `--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` 합니다.

## Related

* [사용자 정의 에이전트 및 하위 에이전트 오케스트레이션](/ko/copilot/how-tos/copilot-sdk/features/custom-agents): 플러그인의 `agents/` 폴더에 포함되는 에이전트를 작성합니다.
* [사용자 지정 기술](/ko/copilot/how-tos/copilot-sdk/features/skills): 파일이 로드되는 방법 `SKILL.md` 및 기술 계층 순서 규칙입니다.
* [후크를 사용한 작업](/ko/copilot/how-tos/copilot-sdk/features/hooks): 플러그인에 의해 정의된 후크는 SDK에 등록된 후크와 함께 실행됩니다.
* [GitHub Copilot SDK에서 MCP 서버 사용](/ko/copilot/how-tos/copilot-sdk/features/mcp): 플러그 인 제공 MCP 서버는 인라인 등록과 동일한 방식으로 통합됩니다.
* [플릿 모드](/ko/copilot/how-tos/copilot-sdk/features/fleet-mode): 플러그 인 제공 에이전트는 하위 에이전트로 디스패치할 수 있습니다.