# 사용자 지정 기술

스킬은 Copilot의 기능을 확장하는 재사용 가능한 프롬프트 모듈입니다. 디렉터리에서 기술을 로드하여 특정 도메인 또는 워크플로에 대해 Copilot 특수한 기능을 제공합니다.

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

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

## Overview

스킬은 Copilot에 지침을 제공하는 Markdown 문서인 `SKILL.md` 파일을 포함하는 이름이 지정된 디렉터리입니다. 로드되면 기술의 콘텐츠가 세션 컨텍스트에 삽입됩니다.

기술을 통해 다음을 수행할 수 있습니다.

* 재사용 가능한 모듈로 도메인 전문 지식 패키지
* 프로젝트 간에 특수한 동작 공유
* 복잡한 에이전트 구성 정리
* 세션당 기능 사용/사용 안 함

## 기술 불러오기

세션을 만들 때 기술을 포함하는 디렉터리를 지정합니다.

<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 } from "@github/copilot-sdk";

const client = new CopilotClient();
const session = await client.createSession({
    model: "gpt-4.1",
    skillDirectories: [
        "./skills/code-review",
        "./skills/documentation",
    ],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Copilot now has access to skills in those directories
await session.sendAndWait({ prompt: "Review this code for security issues" });
```

</div>

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

```python
from copilot import CopilotClient, PermissionDecisionApproveOnce

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(
        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
        model="gpt-4.1",
        skill_directories=[
            "./skills/code-review",
            "./skills/documentation",
        ],
    )

    # Copilot now has access to skills in those directories
    await session.send_and_wait("Review this code for security issues")

    await client.stop()
```

</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
package main

import (
    "context"
    "log"
    copilot "github.com/github/copilot-sdk/go"
    "github.com/github/copilot-sdk/go/rpc"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    if err := client.Start(ctx); err != nil {
        log.Fatal(err)
    }
    defer client.Stop()

    session, err := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-4.1",
        SkillDirectories: []string{
            "./skills/code-review",
            "./skills/documentation",
        },
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    // Copilot now has access to skills in those directories
    _, err = session.SendAndWait(ctx, copilot.MessageOptions{
        Prompt: "Review this code for security issues",
    })
    if err != nil {
        log.Fatal(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;
using GitHub.Copilot.Rpc;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-4.1",
    SkillDirectories = new List<string>
    {
        "./skills/code-review",
        "./skills/documentation",
    },
    OnPermissionRequest = (req, inv) =>
        Task.FromResult(PermissionDecision.ApproveOnce()),
});

// Copilot now has access to skills in those directories
await session.SendAndWaitAsync(new MessageOptions
{
    Prompt = "Review this code for security issues"
});
```

</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
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;
import java.util.List;

try (var client = new CopilotClient()) {
    client.start().get();

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-4.1")
            .setSkillDirectories(List.of(
                "./skills/code-review",
                "./skills/documentation"
            ))
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();

    // Copilot now has access to skills in those directories
    session.sendAndWait(new MessageOptions()
        .setPrompt("Review this code for security issues")
    ).get();
}
```

</div>

</div>

## 기능 비활성화

다른 사람을 활성 상태로 유지하면서 특정 기술을 사용하지 않도록 설정합니다.

<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
const session = await client.createSession({
    skillDirectories: ["./skills"],
    disabledSkills: ["experimental-feature", "deprecated-tool"],
});
```

</div>

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

```python
from copilot.session import PermissionHandler

session = await client.create_session(
    on_permission_request=PermissionHandler.approve_all,
    skill_directories=["./skills"],
    disabled_skills=["experimental-feature", "deprecated-tool"],
)
```

</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
package main

import (
    "context"
    copilot "github.com/github/copilot-sdk/go"
    "github.com/github/copilot-sdk/go/rpc"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)

    session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
        SkillDirectories: []string{"./skills"},
        DisabledSkills:   []string{"experimental-feature", "deprecated-tool"},
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })
    _ = session
}
```

```golang
session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
    SkillDirectories: []string{"./skills"},
    DisabledSkills:   []string{"experimental-feature", "deprecated-tool"},
})
```

</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;
using GitHub.Copilot.Rpc;

public static class SkillsExample
{
    public static async Task Main()
    {
        await using var client = new CopilotClient();

        var session = await client.CreateSessionAsync(new SessionConfig
        {
            SkillDirectories = new List<string> { "./skills" },
            DisabledSkills = new List<string> { "experimental-feature", "deprecated-tool" },
            OnPermissionRequest = (req, inv) =>
                Task.FromResult(PermissionDecision.ApproveOnce()),
        });
    }
}
```

```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
    SkillDirectories = new List<string> { "./skills" },
    DisabledSkills = new List<string> { "experimental-feature", "deprecated-tool" },
});
```

</div>

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

<!-- docs-validate: skip -->

```java
import com.github.copilot.rpc.*;
import java.util.List;

var session = client.createSession(
    new SessionConfig()
        .setSkillDirectories(List.of("./skills"))
        .setDisabledSkills(List.of("experimental-feature", "deprecated-tool"))
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
```

</div>

</div>

## 기술 디렉터리 구조

각 기술은 파일을 포함하는 명명된 하위 디렉터리입니다.`SKILL.md`

```text
skills/
├── code-review/
│   └── SKILL.md
└── documentation/
    └── SKILL.md
```

이 `skillDirectories` 옵션은 부모 디렉터리(예: `./skills`)를 가리킵니다. CLI는 즉시 하위 디렉터리의 모든 `SKILL.md` 파일을 검색합니다.

### SKILL.md 형식

`SKILL.md` 파일은 선택적 YAML 프런트매터가 있는 markdown 문서입니다.

```markdown
---
name: code-review
description: Specialized code review capabilities
---

# Code Review Guidelines

When reviewing code, always check for:

1. **Security vulnerabilities** - SQL injection, XSS, etc.
2. **Performance issues** - N+1 queries, memory leaks
3. **Code style** - Consistent formatting, naming conventions
4. **Test coverage** - Are critical paths tested?

Provide specific line-number references and suggested fixes.
```

프런트매터 필드:

* **`name`**: 기술의 식별자입니다(`disabledSkills`와 함께 사용하여 이를 선택적으로 비활성화하는 데 사용됨). 생략하면 디렉터리 이름이 사용됩니다.
* **`description`**: 기술이 수행하는 일에 대한 간단한 설명입니다.

markdown 본문에는 기술이 로드될 때 세션 컨텍스트에 삽입되는 지침이 포함되어 있습니다.

## 구성 옵션

### SessionConfig 기술 필드

| 언어      | Field               | Type           | Description      |
| ------- | ------------------- | -------------- | ---------------- |
| Node.js | `skillDirectories`  | `string[]`     | 기술을 로드하기 위한 디렉터리 |
| Node.js | `disabledSkills`    | `string[]`     | 사용하지 않도록 설정하는 기술 |
| Python  | `skill_directories` | `list[str]`    | 기술을 로드하기 위한 디렉터리 |
| Python  | `disabled_skills`   | `list[str]`    | 사용하지 않도록 설정하는 기술 |
| Go      | `SkillDirectories`  | `[]string`     | 기술을 로드하기 위한 디렉터리 |
| Go      | `DisabledSkills`    | `[]string`     | 사용하지 않도록 설정하는 기술 |
| .NET    | `SkillDirectories`  | `List<string>` | 기술을 로드하기 위한 디렉터리 |
| .NET    | `DisabledSkills`    | `List<string>` | 사용하지 않도록 설정하는 기술 |

## 모범 사례

1. **도메인별로 구성** - 관련 기술 그룹화(예: `skills/security/`, `skills/testing/`)

2. **프런트매터 사용** - 명확성을 위해 YAML 프런트매터 포함 `name` 및 `description` 포함

3. **문서 종속성** - 기술에 필요한 도구 또는 MCP 서버에 유의하세요.

4. **격리된 기술 테스트** - 기술을 결합하기 전에 기술 작동 확인

5. **상대 경로 사용** - 환경에서 기술을 이식 가능한 상태로 유지

## 다른 기능과 결합

### 기술 + 사용자 지정 에이전트

에이전트 `skills` 필드에 나열된 기술은 **즉시 미리 로드됩니다**. 시작 시 에이전트의 컨텍스트에 전체 콘텐츠가 삽입되므로 에이전트는 기술 도구를 호출할 필요 없이 즉시 기술 지침에 액세스할 수 있습니다. 스킬 이름은 세션 수준의 `skillDirectories`에서 조회됩니다.

```typescript
const session = await client.createSession({
    skillDirectories: ["./skills/security"],
    customAgents: [{
        name: "security-auditor",
        description: "Security-focused code reviewer",
        prompt: "Focus on OWASP Top 10 vulnerabilities",
        skills: ["security-scan", "dependency-check"],
    }],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});
```

> \[!NOTE]
> 스킬은 옵트인 방식이므로 `skills`가 생략되면 스킬 콘텐츠가 주입되지 않습니다. 하위 에이전트는 부모로부터 기술을 상속하지 않습니다. 에이전트별로 명시적으로 나열해야 합니다.

### 기술 + MCP 서버

기술은 MCP 서버 기능을 보완할 수 있습니다.

```typescript
const session = await client.createSession({
    skillDirectories: ["./skills/database"],
    mcpServers: {
        postgres: {
            type: "local",
            command: "npx",
            args: ["-y", "@modelcontextprotocol/server-postgres"],
            tools: ["*"],
        },
    },
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});
```

## Troubleshooting

### 기술이 로드되지 않음

1. **경로 확인** - 기술 디렉터리 경로가 올바르고 파일이 있는 하위 디렉터리가 `SKILL.md` 포함되어 있는지 확인합니다.
2. **권한 확인** - SDK가 디렉터리를 읽을 수 있는지 확인
3. **SKILL.md 형식 확인** - markdown이 올바른 형식이고 YAML 프런트매터가 유효한 구문을 사용하는지 확인합니다.
4. **디버그 로깅 활성화** - 스킬 로딩 로그를 보려면 `logLevel: "debug"`으로 설정

### 기술 충돌

여러 기술이 충돌하는 지침을 제공하는 경우:

* `disabledSkills`를 사용하여 충돌하는 기술을 제외하십시오.
* 겹치지 않도록 기술 디렉터리 다시 구성

## 참고하십시오

* [첫 번째 Copilot 기반 앱 빌드](/ko/copilot/how-tos/copilot-sdk/getting-started#create-custom-agents) - 전문 AI 페르소나 정의
* [첫 번째 Copilot 기반 앱 빌드](/ko/copilot/how-tos/copilot-sdk/getting-started#step-4-add-a-custom-tool) - 사용자 고유의 도구 빌드
* [Using MCP servers with the GitHub Copilot SDK](/ko/copilot/how-tos/copilot-sdk/features/mcp) - 외부 도구 공급자 연결