
Github Copilot Sdk
- 123 installs
- 70 repo stars
- Updated July 26, 2026
- rysweet/amplihack
Wire GitHub Copilot SDK into custom agents, CLIs, or editor extensions so your product can offer Copilot-grade code intelligence, chat, and completion inside proprietary workflows.
About
Skill for integrating the GitHub Copilot SDK into amplihack agent projects: client setup, authentication, request orchestration, and embedding Copilot-powered suggestions, chat, and code actions into custom assistants, extensions, and terminal workflows.
- Official Copilot SDK integration patterns
- Targets agent, API, and CLI surfaces
- Embeds LLM-assisted coding in products
- Supports auth and client configuration
- Accelerates AI-native dev tooling
Github Copilot Sdk by the numbers
- 123 all-time installs (skills.sh)
- +1 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #3,721 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/rysweet/amplihack --skill github-copilot-sdkAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 123 |
|---|---|
| repo stars | ★ 70 |
| Last updated | July 26, 2026 |
| Repository | rysweet/amplihack ↗ |
What it does
Wire GitHub Copilot SDK into custom agents, CLIs, or editor extensions so your product can offer Copilot-grade code intelligence, chat, and completion inside proprietary workflows.
Files
GitHub Copilot SDK - Comprehensive Skill
Overview
The GitHub Copilot SDK enables developers to embed Copilot's agentic workflows programmatically in their applications. It exposes the same engine behind Copilot CLI as a production-tested agent runtime you can invoke from code.
When to Use the Copilot SDK
Use the Copilot SDK when:
- Building applications that need Copilot's AI capabilities
- Implementing custom AI assistants with tool-calling abilities
- Creating integrations that leverage Copilot's code understanding
- Connecting to MCP (Model Context Protocol) servers for standardized tools
- Need streaming responses in custom UIs
Don't use when:
- GitHub Copilot CLI is sufficient (use CLI directly)
- No programmatic integration needed (use Copilot in VS Code)
- Building simple chat without tools (use standard LLM API)
Language Support
| SDK | Installation |
|---|---|
| Node.js/TypeScript | npm install @github/copilot-sdk |
| Python | pip install github-copilot-sdk |
| Go | go get github.com/github/copilot-sdk/go |
| .NET | dotnet add package GitHub.Copilot.SDK |
Prerequisites
1. GitHub Copilot CLI installed and authenticated 2. Active Copilot subscription (free tier available with limits)
Quick Start
Minimal Example (TypeScript)
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({ prompt: "What is 2 + 2?" });
console.log(response?.data.content);
await client.stop();Minimal Example (Python)
import asyncio
from copilot import CopilotClient
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session({"model": "gpt-4.1"})
response = await session.send_and_wait({"prompt": "What is 2 + 2?"})
print(response.data.content)
await client.stop()
asyncio.run(main())Add Streaming
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
});
session.on((event) => {
if (event.type === "assistant.message_delta") {
process.stdout.write(event.data.deltaContent);
}
});
await session.sendAndWait({ prompt: "Tell me a joke" });Add Custom Tool
import { defineTool } from "@github/copilot-sdk";
const getWeather = defineTool("get_weather", {
description: "Get weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
},
required: ["city"],
},
handler: async ({ city }) => ({
city,
temperature: `${Math.floor(Math.random() * 30) + 50}°F`,
condition: "sunny",
}),
});
const session = await client.createSession({
model: "gpt-4.1",
tools: [getWeather],
});Core Concepts
Architecture
Your Application → SDK Client → JSON-RPC → Copilot CLI (server mode)The SDK manages the CLI process lifecycle automatically or connects to an external CLI server.
Key Components
1. CopilotClient: Entry point - manages connection to Copilot CLI 2. Session: Conversation context with model, tools, and history 3. Tools: Custom functions Copilot can invoke 4. Events: Streaming responses and tool call notifications 5. MCP Integration: Connect to Model Context Protocol servers
Session Configuration
const session = await client.createSession({
model: "gpt-4.1", // Model to use
streaming: true, // Enable streaming
tools: [myTool], // Custom tools
mcpServers: {
// MCP server connections
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
},
},
systemMessage: {
// Custom system prompt
content: "You are a helpful assistant.",
},
customAgents: [
{
// Custom agent personas
name: "code-reviewer",
displayName: "Code Reviewer",
description: "Reviews code for best practices",
prompt: "Focus on security and performance.",
},
],
});Navigation Guide
When to Read Supporting Files
reference.md - Read when you need:
- Complete API reference for all 4 languages
- All method signatures with parameters and return types
- Session configuration options
- Event types and handling
- External CLI server connection
examples.md - Read when you need:
- Working copy-paste code for all 4 languages
- Error handling patterns
- Multiple sessions management
- Interactive assistant implementation
- Custom agent definition examples
patterns.md - Read when you need:
- Production-ready architectural patterns
- Streaming UI integration
- MCP server integration patterns
- Rate limiting and retry patterns
- Structured output extraction
drift-detection.md - Read when you need:
- Understanding how this skill stays current
- Validation workflow
- Update procedures
Quick Reference
Common Event Types
| Event Type (TS/Go) | Python Enum |
|---|---|
assistant.message_delta | SessionEventType.ASSISTANT_MESSAGE_DELTA |
session.idle | SessionEventType.SESSION_IDLE |
tool.invocation | SessionEventType.TOOL_EXECUTION_START |
tool.result | SessionEventType.TOOL_EXECUTION_COMPLETE |
Python: Import from copilot.generated.session_events import SessionEventTypeDefault Tools
The SDK operates in --allow-all mode by default, enabling:
- File system operations
- Git operations
- Web requests
- All first-party Copilot tools
Integration with Amplihack
Use the Copilot SDK to build custom agents within amplihack:
# Create Copilot-powered agent for specific domain
from copilot import CopilotClient
async def create_code_review_agent():
client = CopilotClient()
await client.start()
session = await client.create_session({
"model": "gpt-4.1",
"streaming": True,
"systemMessage": {
"content": "You are an expert code reviewer."
}
})
return sessionNext Steps
1. Start Simple: Basic send/receive with default model 2. Add Streaming: Real-time responses for better UX 3. Add Tools: Custom functions for your domain 4. Connect MCP: Use GitHub MCP server for repo access 5. Build UI: Integrate into your application
For complete API details, see reference.md. For working code in all languages, see examples.md. For production patterns, see patterns.md.
GitHub Copilot SDK - Drift Detection
Purpose
This document describes how this skill stays current with the official GitHub Copilot SDK documentation and API changes.
Source URLs
This skill is based on the following official sources:
| Source | URL | Last Verified |
|---|---|---|
| SDK Repository | https://github.com/github/copilot-sdk | 2025-01-25 |
| Getting Started | https://github.com/github/copilot-sdk/blob/main/docs/getting-started.md | 2025-01-25 |
| Node.js Cookbook | https://github.com/github/copilot-sdk/tree/main/cookbook/nodejs | 2025-01-25 |
| Python Cookbook | https://github.com/github/copilot-sdk/tree/main/cookbook/python | 2025-01-25 |
| Go Cookbook | https://github.com/github/copilot-sdk/tree/main/cookbook/go | 2025-01-25 |
| .NET Cookbook | https://github.com/github/copilot-sdk/tree/main/cookbook/dotnet | 2025-01-25 |
| Awesome Copilot | https://github.com/github/awesome-copilot/blob/main/collections/copilot-sdk.md | 2025-01-25 |
Drift Detection Process
Automated Check
Run the drift detection script to check for source changes:
python .claude/skills/github-copilot-sdk/scripts/check_drift.pyWhat it checks:
1. GitHub API for repository commits since last update 2. README.md changes in the SDK repository 3. Cookbook directory changes 4. Getting started guide changes
Output:
CURRENT: No changes detected since last updateDRIFT DETECTED: Sources have changed, update neededERROR: Could not check sources
Manual Verification
Periodically verify these key documents:
1. SDK README.md: Check for new features, API changes, deprecations 2. Getting Started Guide: Verify examples still work 3. Cookbook Examples: Confirm patterns are current 4. Language-specific READMEs: Check for breaking changes
Update Schedule
| Frequency | Action |
|---|---|
| Weekly | Run automated drift check |
| Monthly | Manual source verification |
| On SDK Release | Full skill review and update |
Update Procedure
When drift is detected:
1. Identify Changes
# Check SDK commits since last update
gh api repos/github/copilot-sdk/commits \
--jq '.[0:10] | .[] | "\(.sha[0:7]) \(.commit.message | split("\n")[0])"'2. Categorize Impact
| Change Type | Files to Update | Priority |
|---|---|---|
| New API method | reference.md | High |
| New example | examples.md | Medium |
| New pattern | patterns.md | Medium |
| Breaking change | All files | Critical |
| Bug fix | May not require update | Low |
| Documentation | SKILL.md, reference.md | Medium |
3. Update Files
1. SKILL.md: Update overview, quick start if affected 2. reference.md: Update API documentation 3. examples.md: Add/update code examples 4. patterns.md: Add new production patterns 5. README.md: Update version info 6. drift-detection.md: Update "Last Verified" dates
4. Validate Updates
Run validation checks:
# Check YAML frontmatter
python scripts/check_drift.py --validate-yaml
# Count tokens (should be < 2000 for SKILL.md)
python scripts/check_drift.py --count-tokens
# Verify examples compile (basic syntax check)
python scripts/check_drift.py --check-examples5. Document Update
Update the SKILL.md frontmatter:
last_updated: YYYY-MM-DDUpdate this file's verification dates.
Validation Report
The check_drift.py script can generate a validation report:
python scripts/check_drift.py --reportReport Contents:
- Last source check date
- Files checked and results
- Token counts for each file
- Example syntax validation
- Recommendations
Breaking Change Protocol
When a breaking SDK change is detected:
1. IMMEDIATE: Add deprecation notice to SKILL.md 2. WITHIN 24H: Update affected examples 3. WITHIN 48H: Update patterns if affected 4. WITHIN 1 WEEK: Complete skill update and validation
Version Tracking
| Skill Version | SDK Version | Date |
|---|---|---|
| 1.0.0 | Technical Preview | 2025-01-25 |
Self-Validation
This skill includes self-validation mechanisms:
Token Budget Check
SKILL.md should stay under 2000 tokens:
python scripts/check_drift.py --count-tokens SKILL.mdExample Syntax Check
Verify code examples are syntactically valid:
python scripts/check_drift.py --check-examples examples.mdLink Validation
Verify source URLs are accessible:
python scripts/check_drift.py --check-linksIntegration with CI
Add to repository CI pipeline:
# .github/workflows/skill-drift-check.yml
name: Skill Drift Check
on:
schedule:
- cron: "0 0 * * 0" # Weekly
workflow_dispatch:
jobs:
check-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install requests tiktoken
- run: python .github/skills/github-copilot-sdk/scripts/check_drift.py --reportContact
For questions about this skill or drift detection:
- Repository Issues: https://github.com/rysweet/amplihack/issues
- Skill Label:
skill:github-copilot-sdk
GitHub Copilot SDK - Practical Examples
Example 1: Hello World (All Languages)
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({ prompt: "What is 2 + 2?" });
console.log(response?.data.content);
await client.stop();
process.exit(0);Python
import asyncio
from copilot import CopilotClient
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session({"model": "gpt-4.1"})
response = await session.send_and_wait({"prompt": "What is 2 + 2?"})
print(response.data.content)
await client.stop()
asyncio.run(main())Go
package main
import (
"fmt"
"log"
"os"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient(nil)
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(&copilot.SessionConfig{Model: "gpt-4.1"})
if err != nil {
log.Fatal(err)
}
response, err := session.SendAndWait(copilot.MessageOptions{Prompt: "What is 2 + 2?"}, 0)
if err != nil {
log.Fatal(err)
}
fmt.Println(*response.Data.Content)
os.Exit(0)
}.NET
using GitHub.Copilot.SDK;
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4.1" });
var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2 + 2?" });
Console.WriteLine(response?.Data.Content);---
Example 2: Streaming Responses
TypeScript
import { CopilotClient, SessionEvent } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
});
session.on((event: SessionEvent) => {
if (event.type === "assistant.message_delta") {
process.stdout.write(event.data.deltaContent);
}
if (event.type === "session.idle") {
console.log();
}
});
await session.sendAndWait({ prompt: "Tell me a short joke" });
await client.stop();
process.exit(0);Python
import asyncio
import sys
from copilot import CopilotClient
from copilot.generated.session_events import SessionEventType
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session({
"model": "gpt-4.1",
"streaming": True,
})
def handle_event(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
sys.stdout.write(event.data.delta_content)
sys.stdout.flush()
if event.type == SessionEventType.SESSION_IDLE:
print()
session.on(handle_event)
await session.send_and_wait({"prompt": "Tell me a short joke"})
await client.stop()
asyncio.run(main())Go
package main
import (
"fmt"
"log"
"os"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient(nil)
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(&copilot.SessionConfig{
Model: "gpt-4.1",
Streaming: true,
})
if err != nil {
log.Fatal(err)
}
session.On(func(event copilot.SessionEvent) {
if event.Type == "assistant.message_delta" {
fmt.Print(*event.Data.DeltaContent)
}
if event.Type == "session.idle" {
fmt.Println()
}
})
_, err = session.SendAndWait(copilot.MessageOptions{Prompt: "Tell me a short joke"}, 0)
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}.NET
using GitHub.Copilot.SDK;
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
Streaming = true,
});
session.On(ev =>
{
if (ev is AssistantMessageDeltaEvent deltaEvent)
{
Console.Write(deltaEvent.Data.DeltaContent);
}
if (ev is SessionIdleEvent)
{
Console.WriteLine();
}
});
await session.SendAndWaitAsync(new MessageOptions { Prompt = "Tell me a short joke" });---
Example 3: Custom Tool - Weather
TypeScript
import { CopilotClient, defineTool, SessionEvent } from "@github/copilot-sdk";
const getWeather = defineTool("get_weather", {
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "The city name" },
},
required: ["city"],
},
handler: async ({ city }: { city: string }) => {
const conditions = ["sunny", "cloudy", "rainy", "partly cloudy"];
const temp = Math.floor(Math.random() * 30) + 50;
const condition = conditions[Math.floor(Math.random() * conditions.length)];
return { city, temperature: `${temp}°F`, condition };
},
});
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
tools: [getWeather],
});
session.on((event: SessionEvent) => {
if (event.type === "assistant.message_delta") {
process.stdout.write(event.data.deltaContent);
}
});
await session.sendAndWait({
prompt: "What's the weather like in Seattle and Tokyo?",
});
await client.stop();
process.exit(0);Python
import asyncio
import random
import sys
from copilot import CopilotClient
from copilot.tools import define_tool
from copilot.generated.session_events import SessionEventType
from pydantic import BaseModel, Field
class GetWeatherParams(BaseModel):
city: str = Field(description="The name of the city to get weather for")
@define_tool(description="Get the current weather for a city")
async def get_weather(params: GetWeatherParams) -> dict:
conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]
temp = random.randint(50, 80)
condition = random.choice(conditions)
return {"city": params.city, "temperature": f"{temp}°F", "condition": condition}
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session({
"model": "gpt-4.1",
"streaming": True,
"tools": [get_weather],
})
def handle_event(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
sys.stdout.write(event.data.delta_content)
sys.stdout.flush()
session.on(handle_event)
await session.send_and_wait({
"prompt": "What's the weather like in Seattle and Tokyo?"
})
await client.stop()
asyncio.run(main())Go
package main
import (
"fmt"
"log"
"math/rand"
"os"
copilot "github.com/github/copilot-sdk/go"
)
type WeatherParams struct {
City string `json:"city" jsonschema:"The city name"`
}
type WeatherResult struct {
City string `json:"city"`
Temperature string `json:"temperature"`
Condition string `json:"condition"`
}
func main() {
getWeather := copilot.DefineTool(
"get_weather",
"Get the current weather for a city",
func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {
conditions := []string{"sunny", "cloudy", "rainy", "partly cloudy"}
temp := rand.Intn(30) + 50
condition := conditions[rand.Intn(len(conditions))]
return WeatherResult{
City: params.City,
Temperature: fmt.Sprintf("%d°F", temp),
Condition: condition,
}, nil
},
)
client := copilot.NewClient(nil)
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(&copilot.SessionConfig{
Model: "gpt-4.1",
Streaming: true,
Tools: []copilot.Tool{getWeather},
})
if err != nil {
log.Fatal(err)
}
session.On(func(event copilot.SessionEvent) {
if event.Type == "assistant.message_delta" {
fmt.Print(*event.Data.DeltaContent)
}
})
_, err = session.SendAndWait(copilot.MessageOptions{
Prompt: "What's the weather like in Seattle and Tokyo?",
}, 0)
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}.NET
using GitHub.Copilot.SDK;
using Microsoft.Extensions.AI;
using System.ComponentModel;
await using var client = new CopilotClient();
var getWeather = AIFunctionFactory.Create(
([Description("The city name")] string city) =>
{
var conditions = new[] { "sunny", "cloudy", "rainy", "partly cloudy" };
var temp = Random.Shared.Next(50, 80);
var condition = conditions[Random.Shared.Next(conditions.Length)];
return new { city, temperature = $"{temp}°F", condition };
},
"get_weather",
"Get the current weather for a city"
);
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
Streaming = true,
Tools = [getWeather],
});
session.On(ev =>
{
if (ev is AssistantMessageDeltaEvent deltaEvent)
{
Console.Write(deltaEvent.Data.DeltaContent);
}
});
await session.SendAndWaitAsync(new MessageOptions
{
Prompt = "What's the weather like in Seattle and Tokyo?",
});---
Example 4: Interactive CLI Assistant
TypeScript
import { CopilotClient, defineTool, SessionEvent } from "@github/copilot-sdk";
import * as readline from "readline";
const getWeather = defineTool("get_weather", {
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "The city name" },
},
required: ["city"],
},
handler: async ({ city }) => {
const conditions = ["sunny", "cloudy", "rainy"];
const temp = Math.floor(Math.random() * 30) + 50;
return { city, temperature: `${temp}°F`, condition: conditions[0] };
},
});
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
tools: [getWeather],
});
session.on((event: SessionEvent) => {
if (event.type === "assistant.message_delta") {
process.stdout.write(event.data.deltaContent);
}
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log("🌤️ Weather Assistant (type 'exit' to quit)");
const prompt = () => {
rl.question("You: ", async (input) => {
if (input.toLowerCase() === "exit") {
await client.stop();
rl.close();
return;
}
process.stdout.write("Assistant: ");
await session.sendAndWait({ prompt: input });
console.log("\n");
prompt();
});
};
prompt();---
Example 5: Multiple Sessions
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
// Create two independent sessions
const codeReviewSession = await client.createSession({
model: "gpt-4.1",
systemMessage: { content: "You are a code reviewer." },
});
const documentationSession = await client.createSession({
model: "gpt-4.1",
systemMessage: { content: "You are a documentation writer." },
});
// Use sessions independently
const review = await codeReviewSession.sendAndWait({
prompt: "Review: function add(a,b){return a+b}",
});
console.log("Review:", review?.data.content);
const docs = await documentationSession.sendAndWait({
prompt: "Document: function add(a,b){return a+b}",
});
console.log("Docs:", docs?.data.content);
await client.stop();Python
import asyncio
from copilot import CopilotClient
async def main():
client = CopilotClient()
await client.start()
# Create two independent sessions
review_session = await client.create_session({
"model": "gpt-4.1",
"systemMessage": {"content": "You are a code reviewer."},
})
docs_session = await client.create_session({
"model": "gpt-4.1",
"systemMessage": {"content": "You are a documentation writer."},
})
# Use sessions independently
review = await review_session.send_and_wait({
"prompt": "Review: def add(a,b): return a+b",
})
print("Review:", review.data.content)
docs = await docs_session.send_and_wait({
"prompt": "Document: def add(a,b): return a+b",
})
print("Docs:", docs.data.content)
await client.stop()
asyncio.run(main())---
Example 6: Custom System Message
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
systemMessage: {
content: `You are a Python expert focused on writing clean, idiomatic code.
Always follow PEP 8 guidelines.
Include type hints and docstrings in all code examples.`,
},
});
const response = await session.sendAndWait({
prompt: "Write a function to calculate the nth Fibonacci number",
});
console.log(response?.data.content);
await client.stop();---
Example 7: Error Handling
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
async function main() {
const client = new CopilotClient();
try {
const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({
prompt: "Explain async/await in JavaScript",
});
console.log(response?.data.content);
} catch (error: any) {
if (error.code === "CONNECTION_ERROR") {
console.error("Failed to connect to Copilot CLI. Is it installed?");
} else if (error.code === "AUTHENTICATION_ERROR") {
console.error("Not authenticated. Run: copilot auth login");
} else {
console.error("Error:", error.message);
}
} finally {
await client.stop();
}
}
main();Python
import asyncio
from copilot import CopilotClient
from copilot.errors import ConnectionError, AuthenticationError
async def main():
client = CopilotClient()
try:
await client.start()
session = await client.create_session({"model": "gpt-4.1"})
response = await session.send_and_wait({
"prompt": "Explain async/await in Python"
})
print(response.data.content)
except ConnectionError:
print("Failed to connect to Copilot CLI. Is it installed?")
except AuthenticationError:
print("Not authenticated. Run: copilot auth login")
except Exception as e:
print(f"Error: {e}")
finally:
await client.stop()
asyncio.run(main())---
Example 8: MCP GitHub Integration
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
mcpServers: {
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
},
},
});
session.on((event) => {
if (event.type === "assistant.message_delta") {
process.stdout.write(event.data.deltaContent);
}
});
await session.sendAndWait({
prompt: "List the open issues in the microsoft/TypeScript repository",
});
await client.stop();---
Example 9: Custom Agent Definition
TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
customAgents: [
{
name: "security-reviewer",
displayName: "Security Reviewer",
description: "Reviews code for security vulnerabilities",
prompt: `You are a security expert. When reviewing code:
1. Check for SQL injection vulnerabilities
2. Look for XSS possibilities
3. Identify authentication/authorization issues
4. Find hardcoded secrets
5. Rate severity: Critical, High, Medium, Low`,
},
],
});
const response = await session.sendAndWait({
prompt: `Review this code:
const query = "SELECT * FROM users WHERE id = " + userId;
db.query(query);`,
});
console.log(response?.data.content);
await client.stop();---
Example 10: External CLI Server Connection
TypeScript
// First, start CLI in server mode:
// $ copilot --server --port 4321
import { CopilotClient } from "@github/copilot-sdk";
// Connect to external server instead of managing CLI
const client = new CopilotClient({
cliUrl: "localhost:4321",
});
const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({
prompt: "What is the capital of France?",
});
console.log(response?.data.content);
await client.stop();Python
# First, start CLI in server mode:
# $ copilot --server --port 4321
import asyncio
from copilot import CopilotClient
async def main():
# Connect to external server
client = CopilotClient({"cli_url": "localhost:4321"})
await client.start()
session = await client.create_session({"model": "gpt-4.1"})
response = await session.send_and_wait({
"prompt": "What is the capital of France?"
})
print(response.data.content)
await client.stop()
asyncio.run(main())Go
package main
import (
"fmt"
"log"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
// Connect to external server
client := copilot.NewClient(&copilot.ClientOptions{
CLIUrl: "localhost:4321",
})
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(&copilot.SessionConfig{Model: "gpt-4.1"})
if err != nil {
log.Fatal(err)
}
response, err := session.SendAndWait(copilot.MessageOptions{
Prompt: "What is the capital of France?",
}, 0)
if err != nil {
log.Fatal(err)
}
fmt.Println(*response.Data.Content)
}.NET
// First, start CLI in server mode:
// $ copilot --server --port 4321
using GitHub.Copilot.SDK;
await using var client = new CopilotClient(new CopilotClientOptions
{
CliUrl = "localhost:4321"
});
await using var session = await client.CreateSessionAsync(
new SessionConfig { Model = "gpt-4.1" }
);
var response = await session.SendAndWaitAsync(
new MessageOptions { Prompt = "What is the capital of France?" }
);
Console.WriteLine(response?.Data.Content);---
Example Summary
| # | Example | Languages | Key Concepts |
|---|---|---|---|
| 1 | Hello World | All 4 | Basic send/receive |
| 2 | Streaming | All 4 | Real-time output |
| 3 | Custom Tool | All 4 | Tool definition |
| 4 | Interactive CLI | TS | readline, conversation |
| 5 | Multiple Sessions | TS, Python | Session isolation |
| 6 | System Message | TS | Persona customization |
| 7 | Error Handling | TS, Python | Try/catch patterns |
| 8 | MCP GitHub | TS | MCP integration |
| 9 | Custom Agent | TS | Agent definition |
| 10 | External Server | All 4 | CLI server mode |
---
Complete Runnable Examples
For complete, tested goal-seeking agent implementations, see the examples/ subdirectory:
- [goal-seeking-agent-python.py](examples/goal-seeking-agent-python.py) -
Full autonomous agent with custom tools (tested and working)
- [goal-seeking-agent-typescript.ts](examples/goal-seeking-agent-typescript.ts) -
TypeScript equivalent
These demonstrate combining the Copilot SDK with goal-seeking agent patterns for autonomous task completion.
#!/usr/bin/env python3
# pyright: reportMissingImports=false
"""
Goal-Seeking Agent using GitHub Copilot SDK
This example demonstrates how to build an autonomous goal-seeking agent
that can adapt its approach based on intermediate results.
The agent receives a high-level goal and:
1. Plans execution phases
2. Executes each phase with custom tools
3. Adapts strategy based on results
4. Self-assesses progress toward goal
Prerequisites:
- pip install github-copilot-sdk pydantic
- Copilot CLI installed and authenticated
"""
import asyncio
import random
import sys
from copilot import CopilotClient
from copilot.generated.session_events import SessionEventType
from copilot.tools import define_tool
from pydantic import BaseModel, Field
# ============================================================================
# PHASE 1: Define Goal-Seeking Tools (Pydantic Models)
# ============================================================================
class PlanExecutionParams(BaseModel):
"""Parameters for planning execution phases."""
goal: str = Field(description="The high-level goal to achieve")
current_state: str | None = Field(default=None, description="Current state of progress")
completed_phases: list[str] | None = Field(default=None, description="List of completed phases")
class ExecutePhaseParams(BaseModel):
"""Parameters for executing a specific phase."""
phase: str = Field(description="Phase name to execute")
context: str | None = Field(default=None, description="Additional context for phase")
class AssessProgressParams(BaseModel):
"""Parameters for assessing progress toward goal."""
goal: str = Field(description="The original goal")
completed_phases: list[str] = Field(description="Phases completed so far")
total_phases: int | None = Field(default=4, description="Total phases planned")
# ============================================================================
# PHASE 2: Define Tool Implementations
# ============================================================================
@define_tool(description="Analyze the current goal and plan the next execution phases")
async def plan_execution(params: PlanExecutionParams) -> dict:
"""Goal-seeking logic: determine next phases based on context."""
phases = []
completed = params.completed_phases or []
if "research" not in completed:
phases.append(
{
"phase": "research",
"description": "Gather information about the problem space",
"priority": 1,
}
)
if "design" not in completed:
phases.append(
{
"phase": "design",
"description": "Design solution approach",
"priority": 2,
}
)
if "implement" not in completed:
phases.append(
{
"phase": "implement",
"description": "Implement the solution",
"priority": 3,
}
)
if "verify" not in completed:
phases.append(
{
"phase": "verify",
"description": "Verify solution meets goal",
"priority": 4,
}
)
return {
"goal": params.goal,
"current_state": params.current_state or "initial",
"next_phases": phases,
"recommended_action": (
f"Execute phase: {phases[0]['phase']}" if phases else "Goal achieved!"
),
}
@define_tool(description="Execute a specific phase and return results")
async def execute_phase(params: ExecutePhaseParams) -> dict:
"""Simulate phase execution with varying success rates."""
success = random.random() > 0.2 # 80% success rate
results = {
"research": {
"output": "Identified key requirements and constraints",
"artifacts": ["requirements.md", "constraints.json"],
"next_steps": ["Proceed to design phase"],
},
"design": {
"output": "Created solution architecture",
"artifacts": ["architecture.md", "api-spec.yaml"],
"next_steps": ["Proceed to implementation"],
},
"implement": {
"output": "Implemented core functionality",
"artifacts": ["src/main.py", "tests/test_main.py"],
"next_steps": ["Run verification"],
},
"verify": {
"output": "All tests passing, solution validated",
"artifacts": ["test-results.json"],
"next_steps": ["Goal complete!"],
},
}
phase_result = results.get(
params.phase,
{"output": f"Executed {params.phase}", "artifacts": [], "next_steps": []},
)
return {
"phase": params.phase,
"success": success,
**phase_result,
"failure_reason": None if success else "Recoverable error - retry recommended",
}
@define_tool(description="Evaluate progress toward the goal and determine if complete")
async def assess_progress(params: AssessProgressParams) -> dict:
"""Assess overall progress toward the goal."""
completed_count = len(params.completed_phases) if params.completed_phases else 0
total = params.total_phases or 4
progress = (completed_count / total) * 100
is_complete = progress >= 100
return {
"goal": params.goal,
"completed_phases": params.completed_phases,
"progress_percent": round(progress),
"is_complete": is_complete,
"status": (
"GOAL_ACHIEVED" if is_complete else "ON_TRACK" if progress > 50 else "IN_PROGRESS"
),
"recommendation": (
"Goal successfully achieved!"
if is_complete
else f"Continue with remaining phases ({100 - progress:.0f}% remaining)"
),
}
# ============================================================================
# PHASE 3: Create Goal-Seeking Agent
# ============================================================================
async def create_goal_seeking_agent():
"""Create and configure the goal-seeking agent."""
client = CopilotClient()
await client.start()
session = await client.create_session(
{
"model": "gpt-4.1",
"streaming": True,
"tools": [plan_execution, execute_phase, assess_progress],
"systemMessage": {
"content": """You are an autonomous goal-seeking agent. Your purpose is to:
1. UNDERSTAND the user's high-level goal
2. PLAN execution by breaking the goal into phases
3. EXECUTE phases iteratively, adapting to results
4. ASSESS progress continuously
5. ADAPT strategy if phases fail (retry or try alternatives)
Your decision-making process:
- Use plan_execution to determine next steps
- Use execute_phase to run each phase
- Use assess_progress to evaluate overall progress
- Continue until goal is achieved or you determine it's not achievable
Be autonomous: make decisions based on tool results, don't ask for permission.
Be adaptive: if a phase fails, analyze why and adjust approach.
Be goal-oriented: focus on achieving the outcome, not following a rigid script."""
},
}
)
# Handle streaming events
def handle_event(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
sys.stdout.write(event.data.delta_content)
sys.stdout.flush()
elif event.type == SessionEventType.TOOL_EXECUTION_START:
print(f"\n[🔧 Tool: {event.data.tool_name}]")
elif event.type == SessionEventType.TOOL_EXECUTION_COMPLETE:
print("[✓ Result received]")
session.on(handle_event)
return client, session
# ============================================================================
# PHASE 4: Run Goal-Seeking Agent
# ============================================================================
async def main():
"""Main entry point for the goal-seeking agent demo."""
print("🎯 Goal-Seeking Agent - Copilot SDK Demo (Python)\n")
print("=" * 60)
client, session = await create_goal_seeking_agent()
try:
# Give the agent a high-level goal
goal = """
I need to build a REST API for a todo application.
The API should support:
- Creating, reading, updating, deleting todos
- User authentication
- Data persistence
Please autonomously plan, execute, and verify this goal.
"""
print(f"\n📋 Goal: {goal.strip()}\n")
print("=" * 60)
print("\n🤖 Agent Response:\n")
await session.send_and_wait({"prompt": goal})
print("\n\n" + "=" * 60)
print("✅ Goal-seeking agent completed execution\n")
finally:
await client.stop()
if __name__ == "__main__":
asyncio.run(main())
/**
* Goal-Seeking Agent using GitHub Copilot SDK
*
* This example demonstrates how to build an autonomous goal-seeking agent
* that can adapt its approach based on intermediate results.
*
* The agent receives a high-level goal and:
* 1. Plans execution phases
* 2. Executes each phase with custom tools
* 3. Adapts strategy based on results
* 4. Self-assesses progress toward goal
*
* Prerequisites:
* - npm install @github/copilot-sdk
* - Copilot CLI installed and authenticated
*/
import { CopilotClient, defineTool, SessionEvent } from "@github/copilot-sdk";
// ============================================================================
// PHASE 1: Define Goal-Seeking Tools
// ============================================================================
/**
* Tool: Analyze current state and plan next steps
*/
const planExecution = defineTool("plan_execution", {
description: "Analyze the current goal and state, then plan the next execution phases",
parameters: {
type: "object",
properties: {
goal: { type: "string", description: "The high-level goal to achieve" },
currentState: {
type: "string",
description: "Current state of progress",
},
completedPhases: {
type: "array",
items: { type: "string" },
description: "List of completed phases",
},
},
required: ["goal"],
},
handler: async ({ goal, currentState, completedPhases }) => {
// Goal-seeking logic: determine next phases based on context
const phases = [];
if (!completedPhases?.includes("research")) {
phases.push({
phase: "research",
description: "Gather information about the problem space",
priority: 1,
});
}
if (!completedPhases?.includes("design")) {
phases.push({
phase: "design",
description: "Design solution approach",
priority: 2,
});
}
if (!completedPhases?.includes("implement")) {
phases.push({
phase: "implement",
description: "Implement the solution",
priority: 3,
});
}
if (!completedPhases?.includes("verify")) {
phases.push({
phase: "verify",
description: "Verify solution meets goal",
priority: 4,
});
}
return {
goal,
currentState: currentState || "initial",
nextPhases: phases,
recommendedAction: phases.length > 0 ? `Execute phase: ${phases[0].phase}` : "Goal achieved!",
};
},
});
/**
* Tool: Execute a specific phase and report results
*/
const executePhase = defineTool("execute_phase", {
description: "Execute a specific phase and return results",
parameters: {
type: "object",
properties: {
phase: { type: "string", description: "Phase name to execute" },
context: { type: "string", description: "Additional context for phase" },
},
required: ["phase"],
},
handler: async ({ phase, context }) => {
// Simulate phase execution with varying success rates
const success = Math.random() > 0.2; // 80% success rate
const results: Record<string, { output: string; artifacts: string[]; nextSteps: string[] }> = {
research: {
output: "Identified key requirements and constraints",
artifacts: ["requirements.md", "constraints.json"],
nextSteps: ["Proceed to design phase"],
},
design: {
output: "Created solution architecture",
artifacts: ["architecture.md", "api-spec.yaml"],
nextSteps: ["Proceed to implementation"],
},
implement: {
output: "Implemented core functionality",
artifacts: ["src/main.ts", "tests/main.test.ts"],
nextSteps: ["Run verification"],
},
verify: {
output: "All tests passing, solution validated",
artifacts: ["test-results.json"],
nextSteps: ["Goal complete!"],
},
};
const phaseResult = results[phase] || {
output: `Executed ${phase}`,
artifacts: [],
nextSteps: [],
};
return {
phase,
success,
...phaseResult,
failureReason: success ? null : "Recoverable error - retry recommended",
};
},
});
/**
* Tool: Assess progress toward goal
*/
const assessProgress = defineTool("assess_progress", {
description: "Evaluate progress toward the goal and determine if complete",
parameters: {
type: "object",
properties: {
goal: { type: "string", description: "The original goal" },
completedPhases: {
type: "array",
items: { type: "string" },
description: "Phases completed so far",
},
totalPhases: { type: "number", description: "Total phases planned" },
},
required: ["goal", "completedPhases"],
},
handler: async ({ goal, completedPhases, totalPhases }) => {
const progress = ((completedPhases?.length || 0) / (totalPhases || 4)) * 100;
const isComplete = progress >= 100;
return {
goal,
completedPhases,
progressPercent: Math.round(progress),
isComplete,
status: isComplete ? "GOAL_ACHIEVED" : progress > 50 ? "ON_TRACK" : "IN_PROGRESS",
recommendation: isComplete
? "Goal successfully achieved!"
: `Continue with remaining phases (${100 - progress}% remaining)`,
};
},
});
// ============================================================================
// PHASE 2: Create Goal-Seeking Agent
// ============================================================================
async function createGoalSeekingAgent() {
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
tools: [planExecution, executePhase, assessProgress],
systemMessage: {
content: `You are an autonomous goal-seeking agent. Your purpose is to:
1. UNDERSTAND the user's high-level goal
2. PLAN execution by breaking the goal into phases
3. EXECUTE phases iteratively, adapting to results
4. ASSESS progress continuously
5. ADAPT strategy if phases fail (retry or try alternatives)
Your decision-making process:
- Use plan_execution to determine next steps
- Use execute_phase to run each phase
- Use assess_progress to evaluate overall progress
- Continue until goal is achieved or you determine it's not achievable
Be autonomous: make decisions based on tool results, don't ask for permission.
Be adaptive: if a phase fails, analyze why and adjust approach.
Be goal-oriented: focus on achieving the outcome, not following a rigid script.`,
},
});
// Handle streaming events
session.on((event: SessionEvent) => {
if (event.type === "assistant.message_delta") {
process.stdout.write(event.data.deltaContent);
}
if (event.type === "tool.invocation") {
console.log(`\n[🔧 Tool: ${event.data.toolName}]`);
}
if (event.type === "tool.result") {
console.log(`[✓ Result received]`);
}
});
return { client, session };
}
// ============================================================================
// PHASE 3: Run Goal-Seeking Agent
// ============================================================================
async function main() {
console.log("🎯 Goal-Seeking Agent - Copilot SDK Demo\n");
console.log("=".repeat(60));
const { client, session } = await createGoalSeekingAgent();
try {
// Give the agent a high-level goal
const goal = `
I need to build a REST API for a todo application.
The API should support:
- Creating, reading, updating, deleting todos
- User authentication
- Data persistence
Please autonomously plan, execute, and verify this goal.
`;
console.log(`\n📋 Goal: ${goal.trim()}\n`);
console.log("=".repeat(60));
console.log("\n🤖 Agent Response:\n");
await session.sendAndWait({ prompt: goal });
console.log("\n\n" + "=".repeat(60));
console.log("✅ Goal-seeking agent completed execution\n");
} finally {
await client.stop();
}
}
// Run the agent
main().catch(console.error);
GitHub Copilot SDK - Production Patterns
Pattern 1: Streaming UI Integration
Real-time response display for better user experience.
Problem
Users experience long wait times before seeing any output when responses are large.
Solution
Enable streaming and display chunks as they arrive.
import { CopilotClient, SessionEvent } from "@github/copilot-sdk";
class StreamingUI {
private outputBuffer: string = "";
async run(prompt: string) {
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
});
// Handle streaming events
session.on((event: SessionEvent) => {
if (event.type === "assistant.message_delta") {
const chunk = event.data.deltaContent;
this.outputBuffer += chunk;
this.renderChunk(chunk);
}
if (event.type === "session.idle") {
this.onComplete(this.outputBuffer);
}
});
await session.sendAndWait({ prompt });
await client.stop();
}
private renderChunk(chunk: string) {
// Append to UI element
process.stdout.write(chunk);
}
private onComplete(fullResponse: string) {
// Handle complete response
console.log("\n[Complete]");
}
}When to Use
- Interactive CLI applications
- Web chat interfaces
- Real-time documentation generation
- Code explanation tools
---
Pattern 2: Tool Chaining
Compose multiple tools for complex workflows.
Problem
Complex tasks require multiple steps with intermediate results.
Solution
Define tools that work together, letting Copilot orchestrate.
import { CopilotClient, defineTool } from "@github/copilot-sdk";
// Tool 1: Search for files
const searchFiles = defineTool("search_files", {
description: "Search for files matching a pattern",
parameters: {
type: "object",
properties: {
pattern: { type: "string" },
directory: { type: "string" },
},
required: ["pattern"],
},
handler: async ({ pattern, directory = "." }) => {
// Implementation
return { files: ["file1.ts", "file2.ts"] };
},
});
// Tool 2: Read file content
const readFile = defineTool("read_file", {
description: "Read the content of a file",
parameters: {
type: "object",
properties: {
path: { type: "string" },
},
required: ["path"],
},
handler: async ({ path }) => {
// Implementation
return { content: "file contents..." };
},
});
// Tool 3: Analyze code
const analyzeCode = defineTool("analyze_code", {
description: "Analyze code for patterns or issues",
parameters: {
type: "object",
properties: {
code: { type: "string" },
analysisType: { type: "string" },
},
required: ["code", "analysisType"],
},
handler: async ({ code, analysisType }) => {
// Implementation
return { findings: ["finding1", "finding2"] };
},
});
// Copilot chains tools automatically
const session = await client.createSession({
model: "gpt-4.1",
tools: [searchFiles, readFile, analyzeCode],
});
await session.sendAndWait({
prompt: "Find all TypeScript files and analyze them for security issues",
});When to Use
- Code analysis workflows
- Data processing pipelines
- Multi-step automation tasks
---
Pattern 3: Session Isolation
Keep contexts separate for different concerns.
Problem
Different tasks contaminate each other's context.
Solution
Use separate sessions for independent workflows.
import { CopilotClient } from "@github/copilot-sdk";
class SessionManager {
private client: CopilotClient;
private sessions: Map<string, any> = new Map();
constructor() {
this.client = new CopilotClient();
}
async getSession(purpose: string, config?: any) {
if (!this.sessions.has(purpose)) {
const session = await this.client.createSession({
model: "gpt-4.1",
...config,
});
this.sessions.set(purpose, session);
}
return this.sessions.get(purpose);
}
async execute(purpose: string, prompt: string) {
const session = await this.getSession(purpose);
return await session.sendAndWait({ prompt });
}
}
// Usage
const manager = new SessionManager();
// Different sessions for different tasks
await manager.execute("code-review", "Review this function...");
await manager.execute("documentation", "Document this API...");
await manager.execute("testing", "Generate tests for...");When to Use
- Multi-tenant applications
- Different personas/roles
- Parallel independent tasks
- Context isolation requirements
---
Pattern 4: Retry with Backoff
Handle transient failures gracefully.
Problem
Network issues or rate limits cause occasional failures.
Solution
Implement exponential backoff retry logic.
import { CopilotClient } from "@github/copilot-sdk";
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
let lastError: Error | null = null;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
lastError = error;
// Don't retry on authentication errors
if (error.code === "AUTHENTICATION_ERROR") {
throw error;
}
// Exponential backoff
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Retry ${attempt + 1}/${maxRetries} in ${delay}ms`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Usage
const response = await withRetry(async () => {
const session = await client.createSession({ model: "gpt-4.1" });
return await session.sendAndWait({ prompt: "Hello" });
});When to Use
- Production applications
- High-availability requirements
- Network-unreliable environments
---
Pattern 5: Tool Result Validation
Verify tool outputs before returning to model.
Problem
Tool errors can confuse the model or cause cascading failures.
Solution
Validate and sanitize tool results.
import { defineTool } from "@github/copilot-sdk";
interface ToolResult<T> {
success: boolean;
data?: T;
error?: string;
}
function createValidatedTool<TInput, TOutput>(
name: string,
description: string,
schema: any,
handler: (input: TInput) => Promise<TOutput>,
validator: (output: TOutput) => boolean
) {
return defineTool(name, {
description,
parameters: schema,
handler: async (input: TInput): Promise<ToolResult<TOutput>> => {
try {
const result = await handler(input);
if (!validator(result)) {
return {
success: false,
error: "Output validation failed",
};
}
return {
success: true,
data: result,
};
} catch (error: any) {
return {
success: false,
error: error.message,
};
}
},
});
}
// Usage
const apiTool = createValidatedTool(
"fetch_user",
"Fetch user data from API",
{
type: "object",
properties: { userId: { type: "string" } },
required: ["userId"],
},
async ({ userId }) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
(user) => user && typeof user.id === "string"
);When to Use
- External API integrations
- Database operations
- File system operations
- Any tool with unreliable output
---
Pattern 6: Conversation History Management
Maintain context across multiple exchanges.
Problem
Long conversations exceed context limits or lose focus.
Solution
Manage conversation history with summarization.
import { CopilotClient } from "@github/copilot-sdk";
class ConversationManager {
private history: Array<{ role: string; content: string }> = [];
private maxHistoryLength = 10;
private client: CopilotClient;
private session: any;
async init() {
this.client = new CopilotClient();
this.session = await this.client.createSession({ model: "gpt-4.1" });
}
async send(userMessage: string): Promise<string> {
// Add user message to history
this.history.push({ role: "user", content: userMessage });
// Trim history if too long
if (this.history.length > this.maxHistoryLength) {
await this.summarizeHistory();
}
// Build context from history
const context = this.history.map((m) => `${m.role}: ${m.content}`).join("\n");
const response = await this.session.sendAndWait({
prompt: `Previous conversation:\n${context}\n\nUser: ${userMessage}`,
});
const assistantMessage = response?.data.content || "";
this.history.push({ role: "assistant", content: assistantMessage });
return assistantMessage;
}
private async summarizeHistory() {
// Keep last 2 messages, summarize the rest
const toSummarize = this.history.slice(0, -2);
const toKeep = this.history.slice(-2);
const summary = await this.session.sendAndWait({
prompt: `Summarize this conversation in 2-3 sentences:\n${toSummarize
.map((m) => `${m.role}: ${m.content}`)
.join("\n")}`,
});
this.history = [
{ role: "system", content: `Previous context: ${summary?.data.content}` },
...toKeep,
];
}
}When to Use
- Long-running chat sessions
- Complex multi-step workflows
- Context-limited models
---
Pattern 7: MCP Server Composition
Combine multiple MCP servers for rich capabilities.
Problem
Single MCP server has limited capabilities.
Solution
Connect multiple MCP servers for comprehensive tool access.
import { CopilotClient } from "@github/copilot-sdk";
const session = await client.createSession({
model: "gpt-4.1",
mcpServers: {
// GitHub repository access
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
},
// Local filesystem access
filesystem: {
type: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "./workspace"],
},
// Database access (example)
database: {
type: "stdio",
command: "npx",
args: ["-y", "mcp-server-sqlite", "./data.db"],
env: {
DB_READ_ONLY: "true",
},
},
},
});
// Copilot can now use tools from all servers
await session.sendAndWait({
prompt:
"Find open issues in my repo, check related files, and query the database for linked tickets",
});When to Use
- Complex automation workflows
- Cross-system integrations
- Development environment tools
---
Pattern 8: Graceful Degradation
Handle capability failures without breaking the application.
Problem
Tool failures or missing capabilities shouldn't crash the app.
Solution
Implement fallback behaviors and error boundaries.
import { CopilotClient, defineTool } from "@github/copilot-sdk";
// Primary tool with fallback
const fetchDataTool = defineTool("fetch_data", {
description: "Fetch data from API with caching fallback",
parameters: {
type: "object",
properties: { id: { type: "string" } },
required: ["id"],
},
handler: async ({ id }) => {
try {
// Try primary API
const response = await fetch(`https://api.example.com/data/${id}`);
if (!response.ok) throw new Error("API failed");
return await response.json();
} catch (primaryError) {
try {
// Try cache
return await getFromCache(id);
} catch (cacheError) {
// Return graceful error
return {
error: true,
message: `Data unavailable for ${id}`,
suggestion: "Try again later or use a different ID",
};
}
}
},
});
// Session with graceful error handling
async function safeQuery(prompt: string) {
const client = new CopilotClient();
try {
const session = await client.createSession({
model: "gpt-4.1",
tools: [fetchDataTool],
});
return await session.sendAndWait({ prompt });
} catch (error: any) {
// Return useful error instead of crashing
return {
success: false,
error: error.message,
fallbackResponse: "I'm unable to complete this request. Please try again.",
};
} finally {
await client.stop();
}
}When to Use
- Production applications
- User-facing interfaces
- Unreliable external services
---
Pattern 9: Rate Limiting
Control request frequency to avoid quota exhaustion.
Problem
High request volume exceeds API limits.
Solution
Implement token bucket or sliding window rate limiting.
class RateLimiter {
private tokens: number;
private maxTokens: number;
private refillRate: number;
private lastRefill: number;
constructor(maxTokens: number, refillPerSecond: number) {
this.tokens = maxTokens;
this.maxTokens = maxTokens;
this.refillRate = refillPerSecond;
this.lastRefill = Date.now();
}
async acquire(): Promise<void> {
this.refill();
if (this.tokens < 1) {
const waitTime = ((1 - this.tokens) / this.refillRate) * 1000;
await new Promise((resolve) => setTimeout(resolve, waitTime));
this.refill();
}
this.tokens -= 1;
}
private refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
}
// Usage with Copilot SDK
const limiter = new RateLimiter(10, 1); // 10 requests, 1 per second refill
async function rateLimitedQuery(prompt: string) {
await limiter.acquire();
return await session.sendAndWait({ prompt });
}When to Use
- Shared API quotas
- Multi-user applications
- Cost control
---
Pattern 10: Structured Output Extraction
Get structured data from model responses.
Problem
Need structured data but model returns prose.
Solution
Use tools to enforce output structure.
import { defineTool } from "@github/copilot-sdk";
// Tool that enforces structured output
const extractEntities = defineTool("extract_entities", {
description: "Return the analysis result as structured data",
parameters: {
type: "object",
properties: {
entities: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
type: { type: "string" },
confidence: { type: "number" },
},
},
},
summary: { type: "string" },
},
required: ["entities", "summary"],
},
handler: async (data) => data, // Just return the structured data
});
// Force model to use the tool
const session = await client.createSession({
model: "gpt-4.1",
tools: [extractEntities],
systemMessage: {
content: "Always use the extract_entities tool to return your analysis results.",
},
});
const response = await session.sendAndWait({
prompt: "Analyze this text and identify all people, places, and organizations: ...",
});When to Use
- Data extraction pipelines
- API response generation
- Form filling automation
---
Anti-Patterns to Avoid
1. Ignoring Cleanup
Bad:
const client = new CopilotClient();
const session = await client.createSession({ model: "gpt-4.1" });
// No cleanup - resource leak!Good:
const client = new CopilotClient();
try {
const session = await client.createSession({ model: "gpt-4.1" });
// Use session
} finally {
await client.stop();
}2. Blocking on Streaming
Bad:
// Waiting for full response while streaming
const response = await session.sendAndWait({ prompt: "..." });
// No streaming handler - defeats purposeGood:
session.on((event) => {
if (event.type === "assistant.message_delta") {
displayChunk(event.data.deltaContent);
}
});
await session.sendAndWait({ prompt: "..." });3. Unbounded Tool Execution
Bad:
const dangerousTool = defineTool("execute", {
handler: async ({ command }) => {
return execSync(command).toString(); // Dangerous!
},
});Good:
const safeTool = defineTool("execute", {
handler: async ({ command }) => {
if (!ALLOWED_COMMANDS.includes(command)) {
throw new Error("Command not allowed");
}
return execSync(command).toString();
},
});---
Summary
| Pattern | Use Case | Key Benefit |
|---|---|---|
| Streaming UI | Chat interfaces | Real-time UX |
| Tool Chaining | Complex workflows | Automation |
| Session Isolation | Multi-tenant | Context safety |
| Retry with Backoff | Production | Reliability |
| Tool Validation | External APIs | Error handling |
| History Management | Long conversations | Context control |
| MCP Composition | Rich integrations | Capability expansion |
| Graceful Degradation | User-facing apps | Resilience |
| Rate Limiting | Shared resources | Cost control |
| Structured Output | Data pipelines | Type safety |
GitHub Copilot SDK Skill
Comprehensive knowledge base for using the GitHub Copilot SDK to embed Copilot's agentic workflows in applications across Python, TypeScript, Go, and .NET.
Overview
The GitHub Copilot SDK exposes the same production-tested agent runtime behind Copilot CLI as a programmable SDK. You define agent behavior; Copilot handles planning, tool invocation, file edits, and more.
Quick Links
| Resource | Description |
|---|---|
| SKILL.md | Core instructions and quick start |
| reference.md | Complete API reference (all 4 languages) |
| examples.md | 10+ runnable code examples |
| patterns.md | 8+ production patterns |
| drift-detection.md | Update procedures |
Installation
| Language | Command |
|---|---|
| Node.js/TypeScript | npm install @github/copilot-sdk |
| Python | pip install github-copilot-sdk |
| Go | go get github.com/github/copilot-sdk/go |
| .NET | dotnet add package GitHub.Copilot.SDK |
Prerequisites
1. Copilot CLI - Install and authenticate (guide) 2. Copilot Subscription - Required (free tier available)
Verify CLI is working:
copilot --versionKey Features
- Multi-language support: Python, TypeScript, Go, .NET
- Streaming responses: Real-time output for better UX
- Custom tools: Define functions Copilot can invoke
- MCP integration: Connect to Model Context Protocol servers
- Session management: Multiple conversations, persistence
- BYOK support: Use your own API keys for LLM providers
Architecture
Your Application
↓
SDK Client
↓ JSON-RPC
Copilot CLI (server mode)Official Resources
- GitHub Repository: https://github.com/github/copilot-sdk
- Getting Started Guide: https://github.com/github/copilot-sdk/blob/main/docs/getting-started.md
- Cookbook: https://github.com/github/copilot-sdk/tree/main/cookbook
- Awesome Copilot: https://github.com/github/awesome-copilot
Status
The GitHub Copilot SDK is currently in Technical Preview. While functional for development and testing, it may not yet be suitable for production use.
Skill Maintenance
This skill tracks the official GitHub Copilot SDK documentation. See drift-detection.md for update procedures and validation workflow.
Last Updated: 2025-01-25 Source Version: GitHub Copilot SDK v1.0 (Technical Preview)
GitHub Copilot SDK - Complete API Reference
Architecture
SDK Client Communication
The SDK communicates with Copilot CLI via JSON-RPC over a local connection:
┌─────────────────────────────────────────────────────────────┐
│ Your Application │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Session │ │ Session │ │ Custom Tools │ │
│ │ #1 │ │ #2 │ │ + MCP Servers │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ └────────────────┼─────────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ SDK Client │ │
│ └──────┬──────┘ │
└──────────────────────────┼───────────────────────────────────┘
│ JSON-RPC
┌───────▼───────┐
│ Copilot CLI │
│ (server mode) │
└───────────────┘Lifecycle Management
Automatic Mode (default):
- SDK starts CLI process automatically
- Manages process lifecycle
- Cleans up on client stop
External Server Mode:
- Connect to pre-running CLI server
- SDK doesn't manage CLI process
- Useful for debugging and resource sharing
---
CopilotClient API
Constructor / Initialization
TypeScript:
import { CopilotClient } from "@github/copilot-sdk";
// Default - auto-manages CLI process
const client = new CopilotClient();
// Connect to external CLI server
const client = new CopilotClient({
cliUrl: "localhost:4321",
});Python:
from copilot import CopilotClient
# Default - auto-manages CLI process
client = CopilotClient()
# Connect to external CLI server
client = CopilotClient({"cli_url": "localhost:4321"})Go:
import copilot "github.com/github/copilot-sdk/go"
// Default - auto-manages CLI process
client := copilot.NewClient(nil)
// Connect to external CLI server
client := copilot.NewClient(&copilot.ClientOptions{
CLIUrl: "localhost:4321",
}).NET:
using GitHub.Copilot.SDK;
// Default - auto-manages CLI process
await using var client = new CopilotClient();
// Connect to external CLI server
await using var client = new CopilotClient(new CopilotClientOptions {
CliUrl = "localhost:4321"
});Client Methods
| Method | Description | Returns |
|---|---|---|
start() | Initialize connection (Python/Go) | void |
stop() | Close connection and cleanup | void |
createSession(config) | Create new conversation session | Session |
ClientOptions
| Option | Type | Description |
|---|---|---|
cliUrl | string | External CLI server URL (e.g., localhost:4321) |
---
Session API
createSession Configuration
Complete TypeScript Example:
const session = await client.createSession({
// Model selection
model: "gpt-4.1",
// Enable streaming responses
streaming: true,
// Custom tools
tools: [weatherTool, calculatorTool],
// MCP server connections
mcpServers: {
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
},
filesystem: {
type: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/path"],
},
},
// Custom system message
systemMessage: {
content: "You are a helpful assistant focused on code review.",
},
// Custom agents
customAgents: [
{
name: "pr-reviewer",
displayName: "PR Reviewer",
description: "Reviews pull requests for best practices",
prompt: "Focus on security, performance, and maintainability.",
},
],
});Complete Python Example:
session = await client.create_session({
"model": "gpt-4.1",
"streaming": True,
"tools": [weather_tool, calculator_tool],
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
},
},
"systemMessage": {
"content": "You are a helpful assistant.",
},
"customAgents": [{
"name": "code-helper",
"displayName": "Code Helper",
"description": "Helps with coding tasks",
"prompt": "Focus on clean, maintainable code.",
}],
})SessionConfig Options
| Option | Type | Description |
|---|---|---|
model | string | Model identifier (e.g., gpt-4.1) |
streaming | boolean | Enable streaming responses |
tools | Tool[] | Custom tools for the session |
mcpServers | object | MCP server configurations |
systemMessage | object | Custom system message |
customAgents | Agent[] | Custom agent definitions |
Session Methods
| Method | Description | Returns |
|---|---|---|
sendAndWait(options) | Send message and wait for response | Response |
on(handler) | Register event handler (streaming) | void |
MessageOptions
| Option | Type | Description |
|---|---|---|
prompt | string | User message to send |
---
Tools API
defineTool Function
TypeScript:
import { defineTool } from "@github/copilot-sdk";
const myTool = defineTool("tool_name", {
description: "What this tool does",
parameters: {
type: "object",
properties: {
param1: { type: "string", description: "Parameter description" },
param2: { type: "number", description: "Another parameter" },
},
required: ["param1"],
},
handler: async (args) => {
// Tool implementation
return { result: args.param1 };
},
});Python:
from copilot.tools import define_tool
from pydantic import BaseModel, Field
class MyToolParams(BaseModel):
param1: str = Field(description="Parameter description")
param2: int = Field(default=0, description="Another parameter")
@define_tool(description="What this tool does")
async def my_tool(params: MyToolParams) -> dict:
return {"result": params.param1}Go:
type MyParams struct {
Param1 string `json:"param1" jsonschema:"Parameter description"`
Param2 int `json:"param2" jsonschema:"Another parameter"`
}
type MyResult struct {
Result string `json:"result"`
}
myTool := copilot.DefineTool(
"tool_name",
"What this tool does",
func(params MyParams, inv copilot.ToolInvocation) (MyResult, error) {
return MyResult{Result: params.Param1}, nil
},
).NET:
using Microsoft.Extensions.AI;
using System.ComponentModel;
var myTool = AIFunctionFactory.Create(
([Description("Parameter description")] string param1,
[Description("Another parameter")] int param2 = 0) =>
{
return new { result = param1 };
},
"tool_name",
"What this tool does"
);Tool Definition Schema
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique tool identifier |
description | string | Yes | Clear description of capability |
parameters | JSON Schema | Yes | Parameter schema |
handler | function | Yes | Implementation function |
---
Events API
Event Types
| Event Type (TS/Go) | Python Enum | Description |
|---|---|---|
assistant.message_delta | SessionEventType.ASSISTANT_MESSAGE_DELTA | Streaming text chunk |
session.idle | SessionEventType.SESSION_IDLE | Response complete |
tool.invocation | SessionEventType.TOOL_EXECUTION_START | Tool being called |
tool.result | SessionEventType.TOOL_EXECUTION_COMPLETE | Tool execution result |
⚠️ Python Note: Python uses SessionEventType enum fromcopilot.generated.session_events. The enum names differ from TypeScript/Gostring literals (e.g.,TOOL_EXECUTION_STARTvstool.invocation).
Event Handling
TypeScript:
session.on((event: SessionEvent) => {
switch (event.type) {
case "assistant.message_delta":
process.stdout.write(event.data.deltaContent);
break;
case "session.idle":
console.log("\n[Complete]");
break;
case "tool.invocation":
console.log(`[Tool: ${event.data.toolName}]`);
break;
}
});Python:
from copilot.generated.session_events import SessionEventType
def handle_event(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
sys.stdout.write(event.data.delta_content)
sys.stdout.flush()
elif event.type == SessionEventType.TOOL_EXECUTION_START:
print(f"[Tool: {event.data.tool_name}]")
elif event.type == SessionEventType.TOOL_EXECUTION_COMPLETE:
print(f"[Result: {event.data.result}]")
elif event.type == SessionEventType.SESSION_IDLE:
print()
session.on(handle_event)Go:
session.On(func(event copilot.SessionEvent) {
switch event.Type {
case "assistant.message_delta":
fmt.Print(*event.Data.DeltaContent)
case "session.idle":
fmt.Println()
}
}).NET:
session.On(ev =>
{
if (ev is AssistantMessageDeltaEvent deltaEvent)
{
Console.Write(deltaEvent.Data.DeltaContent);
}
if (ev is SessionIdleEvent)
{
Console.WriteLine();
}
});---
MCP Integration
MCP Server Types
HTTP Type (remote servers):
mcpServers: {
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
}
}stdio Type (process-based):
mcpServers: {
filesystem: {
type: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allow"],
env: { /* optional environment variables */ }
}
}Common MCP Servers
| Server | Package | Description |
|---|---|---|
| GitHub | @github/github-mcp-server | Repository access |
| Filesystem | @modelcontextprotocol/server-filesystem | File operations |
---
External CLI Server
Running CLI in Server Mode
# Start CLI server on specific port
copilot --server --port 4321
# Random available port
copilot --serverConnecting SDK to External Server
Benefits:
- Debugging: Keep CLI running between SDK restarts
- Resource sharing: Multiple SDK clients share one CLI
- Development: Custom CLI settings
All Languages:
TypeScript: cliUrl: "localhost:4321" Python: cli_url: "localhost:4321" Go: CLIUrl: "localhost:4321" .NET: CliUrl = "localhost:4321"
---
Models
Available Models
The SDK supports all models available via Copilot CLI. Use the SDK's model discovery method to list available models at runtime.
Specifying Model
const session = await client.createSession({
model: "gpt-4.1", // Default recommended model
});---
BYOK (Bring Your Own Key)
The SDK supports using your own API keys from LLM providers (OpenAI, Azure, Anthropic). Refer to individual SDK documentation for configuration details.
---
Error Handling
Common Errors
| Error | Cause | Solution |
|---|---|---|
ConnectionError | CLI not running | Install and start Copilot CLI |
AuthenticationError | Not authenticated | Run copilot auth login |
TimeoutError | Response timeout | Increase timeout or retry |
Error Handling Pattern
try {
const response = await session.sendAndWait({ prompt: "..." });
console.log(response?.data.content);
} catch (error) {
if (error.code === "CONNECTION_ERROR") {
console.error("CLI not running");
} else if (error.code === "TIMEOUT") {
console.error("Request timed out");
} else {
console.error("Unexpected error:", error);
}
}---
Billing
SDK usage counts toward your premium request quota, same as Copilot CLI. See GitHub Copilot billing for details.
#!/usr/bin/env python3
"""
GitHub Copilot SDK Skill - Drift Detection and Validation Script
Checks for updates to official SDK documentation and validates skill content.
"""
import argparse
import json
import os
import re
import sys
from datetime import datetime
from pathlib import Path
# Optional imports - graceful degradation if not available
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
try:
import tiktoken
HAS_TIKTOKEN = True
except ImportError:
HAS_TIKTOKEN = False
# Configuration
SKILL_DIR = Path(__file__).parent.parent
SOURCE_URLS = {
"sdk_repo": "https://api.github.com/repos/github/copilot-sdk/commits",
"readme": "https://api.github.com/repos/github/copilot-sdk/commits?path=README.md",
"getting_started": "https://api.github.com/repos/github/copilot-sdk/commits?path=docs/getting-started.md",
}
LAST_UPDATED = "2025-01-25"
TOKEN_BUDGET = 2000
def check_drift() -> dict:
"""Check if source documentation has changed since last update."""
if not HAS_REQUESTS:
return {"status": "ERROR", "message": "requests library not installed"}
results = {"status": "CURRENT", "sources": {}}
last_update = datetime.strptime(LAST_UPDATED, "%Y-%m-%d")
for name, url in SOURCE_URLS.items():
try:
response = requests.get(url, headers={"Accept": "application/vnd.github.v3+json"})
if response.status_code == 200:
commits = response.json()
if commits:
latest = commits[0]
commit_date = datetime.strptime(
latest["commit"]["committer"]["date"][:10], "%Y-%m-%d"
)
if commit_date > last_update:
results["status"] = "DRIFT DETECTED"
results["sources"][name] = {
"changed": True,
"latest_commit": latest["sha"][:7],
"date": commit_date.strftime("%Y-%m-%d"),
"message": latest["commit"]["message"].split("\n")[0][:50],
}
else:
results["sources"][name] = {"changed": False}
else:
results["sources"][name] = {"error": f"HTTP {response.status_code}"}
except Exception as e:
results["sources"][name] = {"error": str(e)}
return results
def count_tokens(file_path: Path) -> int | None:
"""Count tokens in a markdown file."""
if not HAS_TIKTOKEN:
return None
try:
encoding = tiktoken.encoding_for_model("gpt-4")
content = file_path.read_text()
return len(encoding.encode(content))
except Exception:
return None
def validate_yaml_frontmatter(file_path: Path) -> dict:
"""Validate YAML frontmatter in a skill file."""
content = file_path.read_text()
# Check for frontmatter
if not content.startswith("---"):
return {"valid": False, "error": "No YAML frontmatter found"}
# Extract frontmatter
parts = content.split("---", 2)
if len(parts) < 3:
return {"valid": False, "error": "Invalid frontmatter structure"}
frontmatter = parts[1].strip()
# Check required fields
required = ["name", "description"]
missing = []
for field in required:
if f"{field}:" not in frontmatter:
missing.append(field)
if missing:
return {"valid": False, "error": f"Missing required fields: {missing}"}
return {"valid": True}
def check_example_syntax(file_path: Path) -> dict:
"""Basic syntax check on code examples."""
content = file_path.read_text()
results = {"valid": True, "issues": []}
# Extract code blocks
code_blocks = re.findall(r"```(\w+)\n(.*?)```", content, re.DOTALL)
for lang, code in code_blocks:
# Basic checks
if lang in ["typescript", "javascript"]:
# Check for unclosed braces
if code.count("{") != code.count("}"):
results["issues"].append(f"{lang}: Unbalanced braces")
if code.count("(") != code.count(")"):
results["issues"].append(f"{lang}: Unbalanced parentheses")
elif lang == "python":
# Check for obvious issues
if "import" in code and code.count("import") > code.count("\n") + 1:
results["issues"].append("Python: Multiple imports on same line")
elif lang in ["go", "golang"]:
if code.count("{") != code.count("}"):
results["issues"].append("Go: Unbalanced braces")
elif lang == "csharp":
if code.count("{") != code.count("}"):
results["issues"].append("C#: Unbalanced braces")
if results["issues"]:
results["valid"] = False
return results
def check_links() -> dict:
"""Verify source URLs are accessible."""
if not HAS_REQUESTS:
return {"status": "ERROR", "message": "requests library not installed"}
urls_to_check = [
"https://github.com/github/copilot-sdk",
"https://github.com/github/awesome-copilot",
]
results = {"all_valid": True, "urls": {}}
for url in urls_to_check:
try:
response = requests.head(url, allow_redirects=True, timeout=10)
valid = response.status_code == 200
results["urls"][url] = {"valid": valid, "status": response.status_code}
if not valid:
results["all_valid"] = False
except Exception as e:
results["urls"][url] = {"valid": False, "error": str(e)}
results["all_valid"] = False
return results
def generate_report() -> str:
"""Generate a full validation report."""
lines = [
"# GitHub Copilot SDK Skill - Validation Report",
f"\n**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
f"**Last Updated**: {LAST_UPDATED}",
"\n## Drift Detection\n",
]
drift = check_drift()
lines.append(f"**Status**: {drift['status']}\n")
if drift.get("sources"):
for name, info in drift["sources"].items():
if info.get("changed"):
lines.append(f"- ⚠️ **{name}**: Changed on {info['date']} - {info['message']}")
elif info.get("error"):
lines.append(f"- ❌ **{name}**: Error - {info['error']}")
else:
lines.append(f"- ✅ **{name}**: No changes")
lines.append("\n## Token Counts\n")
skill_files = [
"SKILL.md",
"reference.md",
"examples.md",
"patterns.md",
]
for filename in skill_files:
file_path = SKILL_DIR / filename
if file_path.exists():
tokens = count_tokens(file_path)
if tokens is not None:
status = "✅" if filename != "SKILL.md" or tokens < TOKEN_BUDGET else "⚠️"
lines.append(f"- {status} **{filename}**: {tokens} tokens")
else:
lines.append(f"- ⚪ **{filename}**: Token counting unavailable")
lines.append("\n## YAML Validation\n")
skill_md = SKILL_DIR / "SKILL.md"
if skill_md.exists():
yaml_result = validate_yaml_frontmatter(skill_md)
if yaml_result["valid"]:
lines.append("- ✅ SKILL.md frontmatter is valid")
else:
lines.append(f"- ❌ SKILL.md frontmatter error: {yaml_result['error']}")
lines.append("\n## Example Syntax\n")
examples_md = SKILL_DIR / "examples.md"
if examples_md.exists():
syntax_result = check_example_syntax(examples_md)
if syntax_result["valid"]:
lines.append("- ✅ All code examples pass basic syntax checks")
else:
for issue in syntax_result["issues"]:
lines.append(f"- ⚠️ {issue}")
lines.append("\n## Link Validation\n")
link_result = check_links()
if link_result.get("all_valid"):
lines.append("- ✅ All source links are accessible")
elif link_result.get("urls"):
for url, info in link_result["urls"].items():
if info.get("valid"):
lines.append(f"- ✅ {url}")
else:
lines.append(f"- ❌ {url}: {info.get('error', info.get('status'))}")
lines.append("\n## Recommendations\n")
if drift["status"] == "DRIFT DETECTED":
lines.append("- 🔄 **UPDATE REQUIRED**: Source documentation has changed")
lines.append(" - Review changes in the SDK repository")
lines.append(" - Update skill files as needed")
lines.append(" - Update LAST_UPDATED in this script")
else:
lines.append("- ✅ Skill is current with source documentation")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="GitHub Copilot SDK Skill Drift Detection")
parser.add_argument("--check", action="store_true", help="Check for drift")
parser.add_argument("--validate-yaml", action="store_true", help="Validate YAML frontmatter")
parser.add_argument("--count-tokens", metavar="FILE", help="Count tokens in a file")
parser.add_argument("--check-examples", metavar="FILE", help="Check example syntax")
parser.add_argument("--check-links", action="store_true", help="Verify source links")
parser.add_argument("--report", action="store_true", help="Generate full report")
args = parser.parse_args()
if args.check:
result = check_drift()
print(json.dumps(result, indent=2))
sys.exit(0 if result["status"] == "CURRENT" else 1)
elif args.validate_yaml:
result = validate_yaml_frontmatter(SKILL_DIR / "SKILL.md")
print(json.dumps(result, indent=2))
sys.exit(0 if result["valid"] else 1)
elif args.count_tokens:
file_path = (
SKILL_DIR / args.count_tokens
if not os.path.isabs(args.count_tokens)
else Path(args.count_tokens)
)
tokens = count_tokens(file_path)
if tokens is not None:
print(f"{file_path.name}: {tokens} tokens")
budget_status = "UNDER" if tokens < TOKEN_BUDGET else "OVER"
print(f"Budget ({TOKEN_BUDGET}): {budget_status}")
else:
print("Token counting unavailable (install tiktoken)")
sys.exit(0)
elif args.check_examples:
file_path = (
SKILL_DIR / args.check_examples
if not os.path.isabs(args.check_examples)
else Path(args.check_examples)
)
result = check_example_syntax(file_path)
print(json.dumps(result, indent=2))
sys.exit(0 if result["valid"] else 1)
elif args.check_links:
result = check_links()
print(json.dumps(result, indent=2))
sys.exit(0 if result.get("all_valid") else 1)
elif args.report:
print(generate_report())
sys.exit(0)
else:
# Default: quick status check
drift = check_drift()
print(f"Drift Status: {drift['status']}")
if drift["status"] == "DRIFT DETECTED":
print("\nChanged sources:")
for name, info in drift.get("sources", {}).items():
if info.get("changed"):
print(f" - {name}: {info['date']} - {info['message']}")
sys.exit(0 if drift["status"] == "CURRENT" else 1)
if __name__ == "__main__":
main()