Moving the copilot-sdk cookbook content in here

This commit is contained in:
Aaron Powell
2026-01-29 14:29:36 +11:00
parent ccdfd66cc2
commit f59e0b4080
53 changed files with 5385 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
# Runnable Recipe Examples
This folder contains standalone, executable Go examples for each cookbook recipe. Each file is a complete program that can be run directly with `go run`.
## Prerequisites
- Go 1.21 or later
- GitHub Copilot SDK for Go
```bash
go get github.com/github/copilot-sdk/go
```
## Running Examples
Each `.go` file is a complete, runnable program. Simply use:
```bash
go run <filename>.go
```
### Available Recipes
| Recipe | Command | Description |
| -------------------- | -------------------------------- | ------------------------------------------ |
| Error Handling | `go run error-handling.go` | Demonstrates error handling patterns |
| Multiple Sessions | `go run multiple-sessions.go` | Manages multiple independent conversations |
| Managing Local Files | `go run managing-local-files.go` | Organizes files using AI grouping |
| PR Visualization | `go run pr-visualization.go` | Generates PR age charts |
| Persisting Sessions | `go run persisting-sessions.go` | Save and resume sessions across restarts |
### Examples with Arguments
**PR Visualization with specific repo:**
```bash
go run pr-visualization.go -repo github/copilot-sdk
```
**Managing Local Files (edit the file to change target folder):**
```bash
# Edit the targetFolder variable in managing-local-files.go first
go run managing-local-files.go
```
## Go Best Practices
These examples follow Go conventions:
- Proper error handling with explicit checks
- Use of `defer` for cleanup
- Idiomatic naming (camelCase for local variables)
- Standard library usage where appropriate
- Clean separation of concerns
## Learning Resources
- [Go Documentation](https://go.dev/doc/)
- [GitHub Copilot SDK for Go](https://github.com/github/copilot-sdk/blob/main/go/README.md)
- [Parent Cookbook](../README.md)

View File

@@ -0,0 +1,44 @@
package main
import (
"fmt"
"log"
"github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient()
if err := client.Start(); err != nil {
log.Fatalf("Failed to start client: %v", err)
}
defer func() {
if err := client.Stop(); err != nil {
log.Printf("Error stopping client: %v", err)
}
}()
session, err := client.CreateSession(copilot.SessionConfig{
Model: "gpt-5",
})
if err != nil {
log.Fatalf("Failed to create session: %v", err)
}
defer session.Destroy()
responseChan := make(chan string, 1)
session.On(func(event copilot.Event) {
if msg, ok := event.(copilot.AssistantMessageEvent); ok {
responseChan <- msg.Data.Content
}
})
if err := session.Send(copilot.MessageOptions{Prompt: "Hello!"}); err != nil {
log.Printf("Failed to send message: %v", err)
return
}
response := <-responseChan
fmt.Println(response)
}

View File

@@ -0,0 +1,62 @@
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/github/copilot-sdk/go"
)
func main() {
// Create and start client
client := copilot.NewClient()
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
// Create session
session, err := client.CreateSession(copilot.SessionConfig{
Model: "gpt-5",
})
if err != nil {
log.Fatal(err)
}
defer session.Destroy()
// Event handler
session.On(func(event copilot.Event) {
switch e := event.(type) {
case copilot.AssistantMessageEvent:
fmt.Printf("\nCopilot: %s\n", e.Data.Content)
case copilot.ToolExecutionStartEvent:
fmt.Printf(" → Running: %s\n", e.Data.ToolName)
case copilot.ToolExecutionCompleteEvent:
fmt.Printf(" ✓ Completed: %s\n", e.Data.ToolName)
}
})
// Ask Copilot to organize files
// Change this to your target folder
homeDir, _ := os.UserHomeDir()
targetFolder := filepath.Join(homeDir, "Downloads")
prompt := fmt.Sprintf(`
Analyze the files in "%s" 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.
`, targetFolder)
if err := session.Send(copilot.MessageOptions{Prompt: prompt}); err != nil {
log.Fatal(err)
}
session.WaitForIdle()
}

View File

@@ -0,0 +1,53 @@
package main
import (
"fmt"
"log"
"github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient()
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
// Create multiple independent sessions
session1, err := client.CreateSession(copilot.SessionConfig{Model: "gpt-5"})
if err != nil {
log.Fatal(err)
}
defer session1.Destroy()
session2, err := client.CreateSession(copilot.SessionConfig{Model: "gpt-5"})
if err != nil {
log.Fatal(err)
}
defer session2.Destroy()
session3, err := client.CreateSession(copilot.SessionConfig{Model: "claude-sonnet-4.5"})
if err != nil {
log.Fatal(err)
}
defer session3.Destroy()
fmt.Println("Created 3 independent sessions")
// Each session maintains its own conversation history
session1.Send(copilot.MessageOptions{Prompt: "You are helping with a Python project"})
session2.Send(copilot.MessageOptions{Prompt: "You are helping with a TypeScript project"})
session3.Send(copilot.MessageOptions{Prompt: "You are helping with a Go project"})
fmt.Println("Sent initial context to all sessions")
// Follow-up messages stay in their respective contexts
session1.Send(copilot.MessageOptions{Prompt: "How do I create a virtual environment?"})
session2.Send(copilot.MessageOptions{Prompt: "How do I set up tsconfig?"})
session3.Send(copilot.MessageOptions{Prompt: "How do I initialize a module?"})
fmt.Println("Sent follow-up questions to each session")
fmt.Println("All sessions will be destroyed on exit")
}

View File

@@ -0,0 +1,68 @@
package main
import (
"fmt"
"log"
"github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient()
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
// Create session with a memorable ID
session, err := client.CreateSession(copilot.SessionConfig{
SessionID: "user-123-conversation",
Model: "gpt-5",
})
if err != nil {
log.Fatal(err)
}
if err := session.Send(copilot.MessageOptions{Prompt: "Let's discuss TypeScript generics"}); err != nil {
log.Fatal(err)
}
fmt.Printf("Session created: %s\n", session.SessionID)
// Destroy session but keep data on disk
if err := session.Destroy(); err != nil {
log.Fatal(err)
}
fmt.Println("Session destroyed (state persisted)")
// Resume the previous session
resumed, err := client.ResumeSession("user-123-conversation")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Resumed: %s\n", resumed.SessionID)
if err := resumed.Send(copilot.MessageOptions{Prompt: "What were we discussing?"}); err != nil {
log.Fatal(err)
}
// List sessions
sessions, err := client.ListSessions()
if err != nil {
log.Fatal(err)
}
ids := make([]string, 0, len(sessions))
for _, s := range sessions {
ids = append(ids, s.SessionID)
}
fmt.Printf("Sessions: %v\n", ids)
// Delete session permanently
if err := client.DeleteSession("user-123-conversation"); err != nil {
log.Fatal(err)
}
fmt.Println("Session deleted")
if err := resumed.Destroy(); err != nil {
log.Fatal(err)
}
}

View File

@@ -0,0 +1,182 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"os/exec"
"regexp"
"strings"
"github.com/github/copilot-sdk/go"
)
// ============================================================================
// Git & GitHub Detection
// ============================================================================
func isGitRepo() bool {
cmd := exec.Command("git", "rev-parse", "--git-dir")
return cmd.Run() == nil
}
func getGitHubRemote() string {
cmd := exec.Command("git", "remote", "get-url", "origin")
output, err := cmd.Output()
if err != nil {
return ""
}
remoteURL := strings.TrimSpace(string(output))
// Handle SSH: git@github.com:owner/repo.git
sshRe := regexp.MustCompile(`git@github\.com:(.+/.+?)(?:\.git)?$`)
if matches := sshRe.FindStringSubmatch(remoteURL); matches != nil {
return matches[1]
}
// Handle HTTPS: https://github.com/owner/repo.git
httpsRe := regexp.MustCompile(`https://github\.com/(.+/.+?)(?:\.git)?$`)
if matches := httpsRe.FindStringSubmatch(remoteURL); matches != nil {
return matches[1]
}
return ""
}
func promptForRepo() string {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter GitHub repo (owner/repo): ")
repo, _ := reader.ReadString('\n')
return strings.TrimSpace(repo)
}
// ============================================================================
// Main Application
// ============================================================================
func main() {
repoFlag := flag.String("repo", "", "GitHub repository (owner/repo)")
flag.Parse()
fmt.Println("🔍 PR Age Chart Generator\n")
// Determine the repository
var repo string
if *repoFlag != "" {
repo = *repoFlag
fmt.Printf("📦 Using specified repo: %s\n", repo)
} else if isGitRepo() {
detected := getGitHubRemote()
if detected != "" {
repo = detected
fmt.Printf("📦 Detected GitHub repo: %s\n", repo)
} else {
fmt.Println("⚠️ Git repo found but no GitHub remote detected.")
repo = promptForRepo()
}
} else {
fmt.Println("📁 Not in a git repository.")
repo = promptForRepo()
}
if repo == "" || !strings.Contains(repo, "/") {
log.Fatal("❌ Invalid repo format. Expected: owner/repo")
}
parts := strings.SplitN(repo, "/", 2)
owner, repoName := parts[0], parts[1]
// Create Copilot client - no custom tools needed!
client := copilot.NewClient(copilot.ClientConfig{LogLevel: "error"})
if err := client.Start(); err != nil {
log.Fatal(err)
}
defer client.Stop()
cwd, _ := os.Getwd()
session, err := client.CreateSession(copilot.SessionConfig{
Model: "gpt-5",
SystemMessage: copilot.SystemMessage{
Content: fmt.Sprintf(`
<context>
You are analyzing pull requests for the GitHub repository: %s/%s
The current working directory is: %s
</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>
`, owner, repoName, cwd),
},
})
if err != nil {
log.Fatal(err)
}
defer session.Destroy()
// Set up event handling
session.On(func(event copilot.Event) {
switch e := event.(type) {
case copilot.AssistantMessageEvent:
fmt.Printf("\n🤖 %s\n\n", e.Data.Content)
case copilot.ToolExecutionStartEvent:
fmt.Printf(" ⚙️ %s\n", e.Data.ToolName)
}
})
// Initial prompt - let Copilot figure out the details
fmt.Println("\n📊 Starting analysis...\n")
prompt := fmt.Sprintf(`
Fetch the open pull requests for %s/%s 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.
`, owner, repoName)
if err := session.Send(copilot.MessageOptions{Prompt: prompt}); err != nil {
log.Fatal(err)
}
session.WaitForIdle()
// Interactive loop
fmt.Println("\n💡 Ask follow-up questions or type \"exit\" to quit.\n")
fmt.Println("Examples:")
fmt.Println(" - \"Expand to the last month\"")
fmt.Println(" - \"Show me the 5 oldest PRs\"")
fmt.Println(" - \"Generate a pie chart instead\"")
fmt.Println(" - \"Group by author instead of age\"")
fmt.Println()
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("You: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "" {
continue
}
if strings.ToLower(input) == "exit" || strings.ToLower(input) == "quit" {
fmt.Println("👋 Goodbye!")
break
}
if err := session.Send(copilot.MessageOptions{Prompt: input}); err != nil {
log.Printf("Error: %v", err)
}
session.WaitForIdle()
}
}