Public/Start-CopilotWork.ps1
# Helper: Assign @copilot to a GitHub issue using gh CLI function Set-CopilotIssueAssignee { param( [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$IssueNumber ) if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { throw "The GitHub CLI 'gh' is not installed or not in PATH. Please install it to assign @copilot." } gh issue edit $IssueNumber --repo $Repo --add-assignee "@copilot" | Out-Null } # Helper: Check if a GitHub repo exists function Test-RepoExists { param( [Parameter(Mandatory)] [string]$Name # owner/repo ) if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { throw "The GitHub CLI 'gh' is not installed or not in PATH. Please install it." } gh repo view $Name 2>$null | Out-Null if ($LASTEXITCODE -eq 0) { return $true } else { return $false } } # Helper: Create a new GitHub issue function New-Issue { param( [Parameter(Mandatory)] [string]$RepoName, # owner/repo [Parameter(Mandatory)] [string]$Title, [Parameter(Mandatory)] [string]$Body ) if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { throw "The GitHub CLI 'gh' is not installed or not in PATH. Please install it." } $output = gh issue create --repo $RepoName --title $Title --body $Body 2>&1 if ($LASTEXITCODE -ne 0 -or -not $output) { throw "Failed to create issue using gh CLI. Output: $output" } # Try to extract the URL from the output (gh usually prints the URL on the last line) $url = $output | Select-String -Pattern 'https://github.com/.*/issues/\d+' -AllMatches | ForEach-Object { $_.Matches.Value } | Select-Object -Last 1 if ($url) { return $url } else { throw "Issue created but URL not found in output. Raw output: $output" } } function Start-CopilotWork { <# .SYNOPSIS Creates Copilot-assigned issues in a specified GitHub repo. .DESCRIPTION Checks if the repo exists, creates one or more issues with the given prompt(s), assigns @copilot, and outputs the issue URLs. If -Path is provided, reads a markdown file and creates an issue for each section separated by '---'. .PARAMETER Repo The GitHub repository in the format owner/repo. .PARAMETER Work The issue body/prompt for Copilot. .PARAMETER Path Path to a markdown file containing one or more issues separated by '---'. .EXAMPLE Start-CopilotWork -Repo dfinke/trystuff 'need a greet fn in Rust' Start-CopilotWork -Repo dfinke/trystuff -Path .\issues.md #> [CmdletBinding()] param( [Parameter(Position = 0)] [string]$Repo, [Parameter(Position = 1)] [string]$Work, [Parameter()] [string]$Path, [Parameter()] [switch]$AssignCopilot, [Parameter()] [switch]$Show ) function Test-RepoFormat($str) { return $str -match '^[^/]+/[^/]+$' } # Allow Repo and Work to be provided in any order (positional or named) if ($Repo -and $Work) { if (Test-RepoFormat $Repo -and -not (Test-RepoFormat $Work)) { # $Repo and $Work are correct } elseif (Test-RepoFormat $Work -and -not (Test-RepoFormat $Repo)) { # Swap if user provided in reverse order $temp = $Repo $Repo = $Work $Work = $temp } elseif (-not (Test-RepoFormat $Repo) -and -not (Test-RepoFormat $Work)) { throw "One argument must be a repo in the format owner/repo." } # else: both look like repos, ambiguous, you can throw or pick one } elseif ($Repo -and -not $Work) { if (-not (Test-RepoFormat $Repo)) { # Only one arg, treat as Work, try to get repo from current dir $Work = $Repo $Repo = $null } } elseif ($Work -and -not $Repo) { if (-not (Test-RepoFormat $Work)) { # Only one arg, treat as Work, try to get repo from current dir # $Work is already set, $Repo is null } else { # Only one arg, and it's a repo, so need Work throw "You must provide a work string if you specify a repo." } } # If Repo is not provided, try to get it from the current directory using gh CLI if (-not $Repo) { if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { Write-Host "The GitHub CLI 'gh' is not installed or not in PATH. Please install it." -ForegroundColor Yellow return } $repoInfo = gh repo view --json 'owner,name' 2>$null | ConvertFrom-Json if (-not $repoInfo) { Write-Host "Not in a GitHub repository directory. Please specify -Repo (owner/repo) or run in a git repo directory." -ForegroundColor Yellow return } $Repo = "$($repoInfo.owner.login)/$($repoInfo.name)" } if (-not (Test-RepoExists -Name $Repo)) { throw "Repository '$Repo' does not exist." } $results = @() if ($Path) { if (-not (Test-Path $Path)) { throw "File '$Path' does not exist." } $content = Get-Content -Path $Path -Raw $sections = $content -split '(?m)^---\s*$' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } foreach ($section in $sections) { $result = New-Issue -RepoName $Repo -Title "Copilot Request" -Body $section if ($AssignCopilot -and $result) { # Extract issue number from URL (assumes .../issues/<number>) if ($result -match '/issues/(\d+)$') { $issueNumber = $matches[1] Set-CopilotIssueAssignee -Repo $Repo -IssueNumber $issueNumber } } $results += $result } if ($Show -and $results.Count -gt 0) { foreach ($url in $results) { Start-Process $url } } return $results } elseif ($Work) { $result = New-Issue -RepoName $Repo -Title "Copilot Request" -Body $Work if ($AssignCopilot -and $result) { if ($result -match '/issues/(\d+)$') { $issueNumber = $matches[1] Set-CopilotIssueAssignee -Repo $Repo -IssueNumber $issueNumber } } if ($Show -and $result) { Start-Process $result } return $result } else { throw "You must provide either -Work or -Path with content." } } |