mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-23 20:05:12 +00:00
Moving the copilot-sdk cookbook content in here
This commit is contained in:
55
cookbook/copilot-sdk/dotnet/recipe/README.md
Normal file
55
cookbook/copilot-sdk/dotnet/recipe/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Runnable Recipe Examples
|
||||
|
||||
This folder contains standalone, executable C# examples for each cookbook recipe. These are [file-based apps](https://learn.microsoft.com/dotnet/core/sdk/file-based-apps) that can be run directly with `dotnet run`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10.0 or later
|
||||
- GitHub Copilot SDK package (referenced automatically)
|
||||
|
||||
## Running Examples
|
||||
|
||||
Each `.cs` file is a complete, runnable program. Simply use:
|
||||
|
||||
```bash
|
||||
dotnet run <filename>.cs
|
||||
```
|
||||
|
||||
### Available Recipes
|
||||
|
||||
| Recipe | Command | Description |
|
||||
| -------------------- | ------------------------------------ | ------------------------------------------ |
|
||||
| Error Handling | `dotnet run error-handling.cs` | Demonstrates error handling patterns |
|
||||
| Multiple Sessions | `dotnet run multiple-sessions.cs` | Manages multiple independent conversations |
|
||||
| Managing Local Files | `dotnet run managing-local-files.cs` | Organizes files using AI grouping |
|
||||
| PR Visualization | `dotnet run pr-visualization.cs` | Generates PR age charts |
|
||||
| Persisting Sessions | `dotnet run persisting-sessions.cs` | Save and resume sessions across restarts |
|
||||
|
||||
### Examples with Arguments
|
||||
|
||||
**PR Visualization with specific repo:**
|
||||
|
||||
```bash
|
||||
dotnet run pr-visualization.cs -- --repo github/copilot-sdk
|
||||
```
|
||||
|
||||
**Managing Local Files (edit the file to change target folder):**
|
||||
|
||||
```bash
|
||||
# Edit the targetFolder variable in managing-local-files.cs first
|
||||
dotnet run managing-local-files.cs
|
||||
```
|
||||
|
||||
## File-Based Apps
|
||||
|
||||
These examples use .NET's file-based app feature, which allows single-file C# programs to:
|
||||
|
||||
- Run without a project file
|
||||
- Automatically reference common packages
|
||||
- Support top-level statements
|
||||
|
||||
## Learning Resources
|
||||
|
||||
- [.NET File-Based Apps Documentation](https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps)
|
||||
- [GitHub Copilot SDK Documentation](https://github.com/github/copilot-sdk/blob/main/dotnet/README.md)
|
||||
- [Parent Cookbook](../README.md)
|
||||
38
cookbook/copilot-sdk/dotnet/recipe/error-handling.cs
Normal file
38
cookbook/copilot-sdk/dotnet/recipe/error-handling.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
#:property PublishAot=false
|
||||
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
var client = new CopilotClient();
|
||||
|
||||
try
|
||||
{
|
||||
await client.StartAsync();
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5"
|
||||
});
|
||||
|
||||
var done = new TaskCompletionSource<string>();
|
||||
session.On(evt =>
|
||||
{
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
done.SetResult(msg.Data.Content);
|
||||
}
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = "Hello!" });
|
||||
var response = await done.Task;
|
||||
Console.WriteLine(response);
|
||||
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await client.StopAsync();
|
||||
}
|
||||
56
cookbook/copilot-sdk/dotnet/recipe/managing-local-files.cs
Normal file
56
cookbook/copilot-sdk/dotnet/recipe/managing-local-files.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
#:property PublishAot=false
|
||||
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
// Create and start client
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
|
||||
// Define tools for file operations
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5"
|
||||
});
|
||||
|
||||
// Wait for completion
|
||||
var done = new TaskCompletionSource();
|
||||
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AssistantMessageEvent msg:
|
||||
Console.WriteLine($"\nCopilot: {msg.Data.Content}");
|
||||
break;
|
||||
case ToolExecutionStartEvent toolStart:
|
||||
Console.WriteLine($" → Running: {toolStart.Data.ToolName} ({toolStart.Data.ToolCallId})");
|
||||
break;
|
||||
case ToolExecutionCompleteEvent toolEnd:
|
||||
Console.WriteLine($" ✓ Completed: {toolEnd.Data.ToolCallId}");
|
||||
break;
|
||||
case SessionIdleEvent:
|
||||
done.SetResult();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Ask Copilot to organize files
|
||||
// Change this to your target folder
|
||||
var targetFolder = @"C:\Users\Me\Downloads";
|
||||
|
||||
await session.SendAsync(new MessageOptions
|
||||
{
|
||||
Prompt = $"""
|
||||
Analyze the files in "{targetFolder}" and organize them into subfolders.
|
||||
|
||||
1. First, list all files and their metadata
|
||||
2. Preview grouping by file extension
|
||||
3. Create appropriate subfolders (e.g., "images", "documents", "videos")
|
||||
4. Move each file to its appropriate subfolder
|
||||
|
||||
Please confirm before moving any files.
|
||||
"""
|
||||
});
|
||||
|
||||
await done.Task;
|
||||
35
cookbook/copilot-sdk/dotnet/recipe/multiple-sessions.cs
Normal file
35
cookbook/copilot-sdk/dotnet/recipe/multiple-sessions.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
#:property PublishAot=false
|
||||
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
|
||||
// Create multiple independent sessions
|
||||
var session1 = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
||||
var session2 = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
||||
var session3 = await client.CreateSessionAsync(new SessionConfig { Model = "claude-sonnet-4.5" });
|
||||
|
||||
Console.WriteLine("Created 3 independent sessions");
|
||||
|
||||
// Each session maintains its own conversation history
|
||||
await session1.SendAsync(new MessageOptions { Prompt = "You are helping with a Python project" });
|
||||
await session2.SendAsync(new MessageOptions { Prompt = "You are helping with a TypeScript project" });
|
||||
await session3.SendAsync(new MessageOptions { Prompt = "You are helping with a Go project" });
|
||||
|
||||
Console.WriteLine("Sent initial context to all sessions");
|
||||
|
||||
// Follow-up messages stay in their respective contexts
|
||||
await session1.SendAsync(new MessageOptions { Prompt = "How do I create a virtual environment?" });
|
||||
await session2.SendAsync(new MessageOptions { Prompt = "How do I set up tsconfig?" });
|
||||
await session3.SendAsync(new MessageOptions { Prompt = "How do I initialize a module?" });
|
||||
|
||||
Console.WriteLine("Sent follow-up questions to each session");
|
||||
|
||||
// Clean up all sessions
|
||||
await session1.DisposeAsync();
|
||||
await session2.DisposeAsync();
|
||||
await session3.DisposeAsync();
|
||||
|
||||
Console.WriteLine("All sessions destroyed successfully");
|
||||
38
cookbook/copilot-sdk/dotnet/recipe/persisting-sessions.cs
Normal file
38
cookbook/copilot-sdk/dotnet/recipe/persisting-sessions.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
#:property PublishAot=false
|
||||
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
|
||||
// Create session with a memorable ID
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
SessionId = "user-123-conversation",
|
||||
Model = "gpt-5"
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = "Let's discuss TypeScript generics" });
|
||||
Console.WriteLine($"Session created: {session.SessionId}");
|
||||
|
||||
// Destroy session but keep data on disk
|
||||
await session.DisposeAsync();
|
||||
Console.WriteLine("Session destroyed (state persisted)");
|
||||
|
||||
// Resume the previous session
|
||||
var resumed = await client.ResumeSessionAsync("user-123-conversation");
|
||||
Console.WriteLine($"Resumed: {resumed.SessionId}");
|
||||
|
||||
await resumed.SendAsync(new MessageOptions { Prompt = "What were we discussing?" });
|
||||
|
||||
// List sessions
|
||||
var sessions = await client.ListSessionsAsync();
|
||||
Console.WriteLine("Sessions: " + string.Join(", ", sessions.Select(s => s.SessionId)));
|
||||
|
||||
// Delete session permanently
|
||||
await client.DeleteSessionAsync("user-123-conversation");
|
||||
Console.WriteLine("Session deleted");
|
||||
|
||||
await resumed.DisposeAsync();
|
||||
await client.StopAsync();
|
||||
204
cookbook/copilot-sdk/dotnet/recipe/pr-visualization.cs
Normal file
204
cookbook/copilot-sdk/dotnet/recipe/pr-visualization.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
#:package GitHub.Copilot.SDK@*
|
||||
#:property PublishAot=false
|
||||
|
||||
using System.Diagnostics;
|
||||
using GitHub.Copilot.SDK;
|
||||
|
||||
// ============================================================================
|
||||
// Git & GitHub Detection
|
||||
// ============================================================================
|
||||
|
||||
bool IsGitRepo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var proc = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = "rev-parse --git-dir",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
});
|
||||
proc?.WaitForExit();
|
||||
return proc?.ExitCode == 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string? GetGitHubRemote()
|
||||
{
|
||||
try
|
||||
{
|
||||
var proc = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = "remote get-url origin",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
});
|
||||
|
||||
var remoteUrl = proc?.StandardOutput.ReadToEnd().Trim();
|
||||
proc?.WaitForExit();
|
||||
|
||||
if (string.IsNullOrEmpty(remoteUrl)) return null;
|
||||
|
||||
// Handle SSH: git@github.com:owner/repo.git
|
||||
var sshMatch = System.Text.RegularExpressions.Regex.Match(
|
||||
remoteUrl, @"git@github\.com:(.+/.+?)(?:\.git)?$");
|
||||
if (sshMatch.Success) return sshMatch.Groups[1].Value;
|
||||
|
||||
// Handle HTTPS: https://github.com/owner/repo.git
|
||||
var httpsMatch = System.Text.RegularExpressions.Regex.Match(
|
||||
remoteUrl, @"https://github\.com/(.+/.+?)(?:\.git)?$");
|
||||
if (httpsMatch.Success) return httpsMatch.Groups[1].Value;
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
string? ParseRepoArg(string[] args)
|
||||
{
|
||||
var repoIndex = Array.IndexOf(args, "--repo");
|
||||
if (repoIndex != -1 && repoIndex + 1 < args.Length)
|
||||
{
|
||||
return args[repoIndex + 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
string PromptForRepo()
|
||||
{
|
||||
Console.Write("Enter GitHub repo (owner/repo): ");
|
||||
return Console.ReadLine()?.Trim() ?? "";
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main Application
|
||||
// ============================================================================
|
||||
|
||||
Console.WriteLine("🔍 PR Age Chart Generator\n");
|
||||
|
||||
// Determine the repository
|
||||
var repo = ParseRepoArg(args);
|
||||
|
||||
if (!string.IsNullOrEmpty(repo))
|
||||
{
|
||||
Console.WriteLine($"📦 Using specified repo: {repo}");
|
||||
}
|
||||
else if (IsGitRepo())
|
||||
{
|
||||
var detected = GetGitHubRemote();
|
||||
if (detected != null)
|
||||
{
|
||||
repo = detected;
|
||||
Console.WriteLine($"📦 Detected GitHub repo: {repo}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("⚠️ Git repo found but no GitHub remote detected.");
|
||||
repo = PromptForRepo();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("📁 Not in a git repository.");
|
||||
repo = PromptForRepo();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(repo) || !repo.Contains('/'))
|
||||
{
|
||||
Console.WriteLine("❌ Invalid repo format. Expected: owner/repo");
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = repo.Split('/');
|
||||
var owner = parts[0];
|
||||
var repoName = parts[1];
|
||||
|
||||
// Create Copilot client - no custom tools needed!
|
||||
await using var client = new CopilotClient(new CopilotClientOptions { LogLevel = "error" });
|
||||
await client.StartAsync();
|
||||
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5",
|
||||
SystemMessage = new SystemMessageConfig
|
||||
{
|
||||
Content = $"""
|
||||
<context>
|
||||
You are analyzing pull requests for the GitHub repository: {owner}/{repoName}
|
||||
The current working directory is: {Environment.CurrentDirectory}
|
||||
</context>
|
||||
|
||||
<instructions>
|
||||
- Use the GitHub MCP Server tools to fetch PR data
|
||||
- Use your file and code execution tools to generate charts
|
||||
- Save any generated images to the current working directory
|
||||
- Be concise in your responses
|
||||
</instructions>
|
||||
"""
|
||||
}
|
||||
});
|
||||
|
||||
// Set up event handling
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AssistantMessageEvent msg:
|
||||
Console.WriteLine($"\n🤖 {msg.Data.Content}\n");
|
||||
break;
|
||||
case ToolExecutionStartEvent toolStart:
|
||||
Console.WriteLine($" ⚙️ {toolStart.Data.ToolName}");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Initial prompt - let Copilot figure out the details
|
||||
Console.WriteLine("\n📊 Starting analysis...\n");
|
||||
|
||||
await session.SendAsync(new MessageOptions
|
||||
{
|
||||
Prompt = $"""
|
||||
Fetch the open pull requests for {owner}/{repoName} from the last week.
|
||||
Calculate the age of each PR in days.
|
||||
Then generate a bar chart image showing the distribution of PR ages
|
||||
(group them into sensible buckets like <1 day, 1-3 days, etc.).
|
||||
Save the chart as "pr-age-chart.png" in the current directory.
|
||||
Finally, summarize the PR health - average age, oldest PR, and how many might be considered stale.
|
||||
"""
|
||||
});
|
||||
|
||||
// Interactive loop
|
||||
Console.WriteLine("\n💡 Ask follow-up questions or type \"exit\" to quit.\n");
|
||||
Console.WriteLine("Examples:");
|
||||
Console.WriteLine(" - \"Expand to the last month\"");
|
||||
Console.WriteLine(" - \"Show me the 5 oldest PRs\"");
|
||||
Console.WriteLine(" - \"Generate a pie chart instead\"");
|
||||
Console.WriteLine(" - \"Group by author instead of age\"");
|
||||
Console.WriteLine();
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.Write("You: ");
|
||||
var input = Console.ReadLine()?.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(input)) continue;
|
||||
if (input.ToLower() is "exit" or "quit")
|
||||
{
|
||||
Console.WriteLine("👋 Goodbye!");
|
||||
break;
|
||||
}
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = input });
|
||||
}
|
||||
Reference in New Issue
Block a user