Skip to content

Commit c23a8aa

Browse files
authored
Merge branch 'breakingChangeDocTool' into breakingChangeDocTool-breakstuff
2 parents 2cc5b9d + 1498523 commit c23a8aa

File tree

3 files changed

+153
-53
lines changed

3 files changed

+153
-53
lines changed

.github/workflows/breaking-change-doc.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ jobs:
3434
env:
3535
GH_TOKEN: ${{ github.token }}
3636

37+
- name: Install GitHub Copilot CLI
38+
run: |
39+
npm install -g @github/copilot
40+
copilot --version
41+
3742
- name: Fetch latest tags
3843
run: |
3944
git fetch --tags --force

eng/breakingChanges/breaking-change-doc.ps1

Lines changed: 144 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ SETUP:
5757
1. Install GitHub CLI and authenticate: gh auth login
5858
2. Choose LLM provider:
5959
- For GitHub Models: gh extension install github/gh-models
60+
- For GitHub Copilot: Install GitHub Copilot CLI from https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli
6061
- For OpenAI: `$env:OPENAI_API_KEY = "your-key"
62+
- For Azure OpenAI: `$env:AZURE_OPENAI_API_KEY = "your-key" and set LlmBaseUrl in config.ps1
6163
- For others: Set appropriate API key
6264
3. Edit config.ps1 to customize settings
6365
"@
@@ -96,21 +98,22 @@ try {
9698
exit 1
9799
}
98100

99-
# Check LLM API key or GitHub CLI for GitHub Models
101+
# Check LLM API key or GitHub CLI for GitHub Models/Copilot
100102
$llmProvider = $Config.LlmProvider
101103
$apiKey = switch ($llmProvider) {
102104
"openai" { $env:OPENAI_API_KEY }
103105
"anthropic" { $env:ANTHROPIC_API_KEY }
104106
"azure-openai" { $env:AZURE_OPENAI_API_KEY }
105107
"github-models" { $null } # No API key needed for GitHub Models
108+
"github-copilot" { $null } # No API key needed for GitHub Copilot CLI
106109
default { $env:OPENAI_API_KEY }
107110
}
108111

109112
if ($llmProvider -eq "github-models") {
110113
# Check if gh-models extension is installed
111114
try {
112-
$extensions = gh extension list 2>$null
113-
if ($extensions -notmatch "github/gh-models") {
115+
$modelsExtension = gh extension list 2>$null | Select-String "gh models"
116+
if (-not $modelsExtension) {
114117
Write-Error "❌ GitHub Models extension not found. Install with: gh extension install github/gh-models"
115118
exit 1
116119
}
@@ -119,12 +122,27 @@ if ($llmProvider -eq "github-models") {
119122
Write-Error "❌ Could not check GitHub Models extension: $($_.Exception.Message)"
120123
exit 1
121124
}
125+
} elseif ($llmProvider -eq "github-copilot") {
126+
# Check if standalone GitHub Copilot CLI is installed
127+
try {
128+
$copilotVersion = copilot --version 2>$null
129+
if (-not $copilotVersion -or $LASTEXITCODE -ne 0) {
130+
Write-Error "❌ GitHub Copilot CLI not found. Install from: https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"
131+
exit 1
132+
}
133+
Write-Host "✅ GitHub Copilot CLI found (version: $($copilotVersion.Split("`n")[0]))" -ForegroundColor Green
134+
} catch {
135+
Write-Error "❌ Could not check GitHub Copilot CLI: $($_.Exception.Message)"
136+
Write-Error " Install from: https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli"
137+
exit 1
138+
}
122139
} elseif (-not $apiKey) {
123140
Write-Error "❌ No LLM API key found. Set environment variable:"
124141
Write-Host " For OpenAI: `$env:OPENAI_API_KEY = 'your-key'"
125142
Write-Host " For Anthropic: `$env:ANTHROPIC_API_KEY = 'your-key'"
126143
Write-Host " For Azure OpenAI: `$env:AZURE_OPENAI_API_KEY = 'your-key'"
127144
Write-Host " For GitHub Models: Use 'github-models' provider (no key needed)"
145+
Write-Host " For GitHub Copilot: Install GitHub Copilot CLI from https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli (no key needed)"
128146
exit 1
129147
} else {
130148
Write-Host "✅ LLM API key found ($llmProvider)" -ForegroundColor Green
@@ -216,15 +234,7 @@ function Limit-Text {
216234
function Get-UrlEncodedText {
217235
param([string]$text)
218236

219-
# Basic URL encoding for GitHub issue URLs
220-
$encoded = $text -replace '\r\n', '%0A' -replace '\n', '%0A' -replace '\r', '%0A'
221-
$encoded = $encoded -replace ' ', '%20' -replace '#', '%23' -replace '&', '%26'
222-
$encoded = $encoded -replace '\[', '%5B' -replace '\]', '%5D' -replace '\(', '%28' -replace '\)', '%29'
223-
$encoded = $encoded -replace ':', '%3A' -replace ';', '%3B' -replace '\?', '%3F' -replace '=', '%3D'
224-
$encoded = $encoded -replace '@', '%40' -replace '\+', '%2B' -replace '\$', '%24'
225-
$encoded = $encoded -replace '"', '%22' -replace "'", '%27' -replace '<', '%3C' -replace '>', '%3E'
226-
227-
return $encoded
237+
return [System.Web.HttpUtility]::UrlEncode($text)
228238
}
229239

230240
# Function to fetch issue template from GitHub
@@ -452,66 +462,149 @@ function Invoke-LlmApi {
452462
$ghArgs += @("--system-prompt", $SystemPrompt)
453463
}
454464

455-
$response = & gh @ghArgs
465+
$response = gh @ghArgs
456466
return $response -join "`n"
457467
}
458468
catch {
459469
Write-Error "GitHub Models API call failed: $($_.Exception.Message)"
460470
return $null
461471
}
462472
}
463-
default {
464-
# Existing API-based providers
465-
$headers = @{ 'Content-Type' = 'application/json' }
466-
$body = @{}
467-
$endpoint = ""
468-
469-
switch ($Config.LlmProvider) {
470-
"openai" {
471-
$endpoint = if ($Config.LlmBaseUrl) { "$($Config.LlmBaseUrl)/chat/completions" } else { "https://api.openai.com/v1/chat/completions" }
472-
$headers['Authorization'] = "Bearer $apiKey"
473-
474-
$messages = @()
475-
if ($SystemPrompt) { $messages += @{ role = "system"; content = $SystemPrompt } }
476-
$messages += @{ role = "user"; content = $Prompt }
477-
478-
$body = @{
479-
model = $Config.LlmModel
480-
messages = $messages
481-
max_tokens = $MaxTokens
482-
temperature = 0.1
483-
}
473+
"github-copilot" {
474+
# Use GitHub Copilot CLI in programmatic mode
475+
try {
476+
# Combine system prompt and user prompt, emphasizing text-only response
477+
$fullPrompt = if ($SystemPrompt) {
478+
"$SystemPrompt`n`nIMPORTANT: Please respond with only the requested text content. Do not create, modify, or execute any files. Just return the text response.`n`n$Prompt"
479+
} else {
480+
"IMPORTANT: Please respond with only the requested text content. Do not create, modify, or execute any files. Just return the text response.`n`n$Prompt"
484481
}
485-
"anthropic" {
486-
$endpoint = if ($Config.LlmBaseUrl) { "$($Config.LlmBaseUrl)/messages" } else { "https://api.anthropic.com/v1/messages" }
487-
$headers['x-api-key'] = $apiKey
488-
$headers['anthropic-version'] = "2023-06-01"
489-
490-
$fullPrompt = if ($SystemPrompt) { "$SystemPrompt`n`nHuman: $Prompt`n`nAssistant:" } else { "Human: $Prompt`n`nAssistant:" }
491-
492-
$body = @{
493-
model = $Config.LlmModel
494-
max_tokens = $MaxTokens
495-
messages = @(@{ role = "user"; content = $fullPrompt })
496-
temperature = 0.1
482+
483+
# Use copilot with -p flag for programmatic mode and suppress logs
484+
$rawResponse = copilot -p $fullPrompt --log-level none --allow-all-tools
485+
486+
# Parse the response to extract just the content, removing usage statistics
487+
# The response format typically includes usage stats at the end starting with "Total usage est:"
488+
$lines = $rawResponse -split "`n"
489+
$contentLines = @()
490+
$foundUsageStats = $false
491+
492+
foreach ($line in $lines) {
493+
if ($line -match "^Total usage est:" -or $line -match "^Total duration") {
494+
$foundUsageStats = $true
495+
break
496+
}
497+
if (-not $foundUsageStats) {
498+
$contentLines += $line
497499
}
498500
}
501+
502+
# Join the content lines and trim whitespace
503+
$response = ($contentLines -join "`n").Trim()
504+
return $response
505+
}
506+
catch {
507+
Write-Error "GitHub Copilot CLI call failed: $($_.Exception.Message)"
508+
return $null
509+
}
510+
}
511+
"openai" {
512+
# OpenAI API
513+
$endpoint = if ($Config.LlmBaseUrl) { "$($Config.LlmBaseUrl)/chat/completions" } else { "https://api.openai.com/v1/chat/completions" }
514+
$headers = @{
515+
'Content-Type' = 'application/json'
516+
'Authorization' = "Bearer $apiKey"
517+
}
518+
519+
$messages = @()
520+
if ($SystemPrompt) { $messages += @{ role = "system"; content = $SystemPrompt } }
521+
$messages += @{ role = "user"; content = $Prompt }
522+
523+
$body = @{
524+
model = $Config.LlmModel
525+
messages = $messages
526+
max_tokens = $MaxTokens
527+
temperature = 0.1
528+
}
529+
530+
try {
531+
$requestJson = $body | ConvertTo-Json -Depth 10
532+
$response = Invoke-RestMethod -Uri $endpoint -Method POST -Headers $headers -Body $requestJson
533+
return $response.choices[0].message.content
534+
}
535+
catch {
536+
Write-Error "OpenAI API call failed: $($_.Exception.Message)"
537+
return $null
538+
}
539+
}
540+
"anthropic" {
541+
# Anthropic API
542+
$endpoint = if ($Config.LlmBaseUrl) { "$($Config.LlmBaseUrl)/messages" } else { "https://api.anthropic.com/v1/messages" }
543+
$headers = @{
544+
'Content-Type' = 'application/json'
545+
'x-api-key' = $apiKey
546+
'anthropic-version' = "2023-06-01"
547+
}
548+
549+
$fullPrompt = if ($SystemPrompt) { "$SystemPrompt`n`nHuman: $Prompt`n`nAssistant:" } else { "Human: $Prompt`n`nAssistant:" }
550+
551+
$body = @{
552+
model = $Config.LlmModel
553+
max_tokens = $MaxTokens
554+
messages = @(@{ role = "user"; content = $fullPrompt })
555+
temperature = 0.1
499556
}
500557

501558
try {
502559
$requestJson = $body | ConvertTo-Json -Depth 10
503560
$response = Invoke-RestMethod -Uri $endpoint -Method POST -Headers $headers -Body $requestJson
561+
return $response.content[0].text
562+
}
563+
catch {
564+
Write-Error "Anthropic API call failed: $($_.Exception.Message)"
565+
return $null
566+
}
567+
}
568+
"azure-openai" {
569+
# Azure OpenAI API
570+
# Endpoint format: https://{resource}.openai.azure.com/openai/deployments/{deployment}/chat/completions?api-version={api-version}
571+
if (-not $Config.LlmBaseUrl) {
572+
Write-Error "Azure OpenAI requires LlmBaseUrl to be set in config (e.g., 'https://your-resource.openai.azure.com')"
573+
return $null
574+
}
575+
576+
$apiVersion = if ($Config.AzureApiVersion) { $Config.AzureApiVersion } else { "2024-02-15-preview" }
577+
$endpoint = "$($Config.LlmBaseUrl)/openai/deployments/$($Config.LlmModel)/chat/completions?api-version=$apiVersion"
578+
579+
$headers = @{
580+
'Content-Type' = 'application/json'
581+
'api-key' = $apiKey
582+
}
504583

505-
switch ($Config.LlmProvider) {
506-
"openai" { return $response.choices[0].message.content }
507-
"anthropic" { return $response.content[0].text }
508-
}
584+
$messages = @()
585+
if ($SystemPrompt) { $messages += @{ role = "system"; content = $SystemPrompt } }
586+
$messages += @{ role = "user"; content = $Prompt }
587+
588+
$body = @{
589+
messages = $messages
590+
max_tokens = $MaxTokens
591+
temperature = 0.1
592+
}
593+
594+
try {
595+
$requestJson = $body | ConvertTo-Json -Depth 10
596+
$response = Invoke-RestMethod -Uri $endpoint -Method POST -Headers $headers -Body $requestJson
597+
return $response.choices[0].message.content
509598
}
510599
catch {
511-
Write-Error "LLM API call failed: $($_.Exception.Message)"
600+
Write-Error "Azure OpenAI API call failed: $($_.Exception.Message)"
512601
return $null
513602
}
514603
}
604+
default {
605+
Write-Error "Unknown LLM provider: $($Config.LlmProvider)"
606+
return $null
607+
}
515608
}
516609
}
517610

eng/breakingChanges/config.ps1

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
$Config = @{
44
# LLM Settings
5-
LlmProvider = "github-models" # openai, anthropic, azure-openai, github-models
5+
LlmProvider = "github-copilot" # openai, anthropic, azure-openai, github-models, github-copilot
66
LlmModel = "openai/gpt-4o" # For GitHub Models: openai/gpt-4o, openai/gpt-4o-mini, microsoft/phi-4, etc.
7-
LlmApiKey = $null # Uses environment variables by default (not needed for github-models)
7+
# For Azure OpenAI: deployment name (e.g., "gpt-4o", "gpt-35-turbo")
8+
LlmApiKey = $null # Uses environment variables by default (not needed for github-models or github-copilot)
89
LlmBaseUrl = $null # For Azure OpenAI: https://your-resource.openai.azure.com
10+
AzureApiVersion = "2024-02-15-preview" # Azure OpenAI API version (optional, defaults to 2024-02-15-preview)
911

1012
# GitHub Settings
1113
SourceRepo = "dotnet/runtime"

0 commit comments

Comments
 (0)