workflows/kickstart-via-jira/systems/mcp/tools/repo-clone/script.ps1

function Invoke-RepoClone {
    param([hashtable]$Arguments)

    $project = $Arguments['project']
    $repo    = $Arguments['repo']

    if (-not $project) { throw "project is required" }
    if (-not $repo)    { throw "repo is required" }

    # ---------------------------------------------------------------------------
    # Load .env.local for credentials
    # ---------------------------------------------------------------------------
    $envLocal = Join-Path $global:DotbotProjectRoot ".env.local"
    if (Test-Path $envLocal) {
        Get-Content $envLocal | ForEach-Object {
            if ($_ -match '^\s*([^#][^=]+)=(.*)$') {
                [Environment]::SetEnvironmentVariable($matches[1].Trim(), $matches[2].Trim(), "Process")
            }
        }
    }

    $adoOrgUrl = $env:AZURE_DEVOPS_ORG_URL
    $adoPat    = $env:AZURE_DEVOPS_PAT
    if (-not $adoOrgUrl) { throw "AZURE_DEVOPS_ORG_URL not set in .env.local" }
    if (-not $adoPat)    { throw "AZURE_DEVOPS_PAT not set in .env.local" }

    # ---------------------------------------------------------------------------
    # Determine paths and branch name
    # ---------------------------------------------------------------------------
    $reposDir  = Join-Path $global:DotbotProjectRoot "repos"
    $clonePath = Join-Path $reposDir $repo

    # Read jira-context.md to get Jira key for branch name
    $initiativePath = Join-Path $global:DotbotProjectRoot ".bot\workspace\product\briefing\jira-context.md"
    $jiraKey = $null
    if (Test-Path $initiativePath) {
        $content = Get-Content $initiativePath -Raw
        if ($content -match '\|\s*Jira Key\s*\|\s*([A-Z]{2,10}-\d+)') {
            $jiraKey = $matches[1]
        }
    }

    # Read branch prefix from settings
    $branchPrefix = "initiative"
    $settingsPath = Join-Path $global:DotbotProjectRoot ".bot\settings\settings.default.json"
    if (Test-Path $settingsPath) {
        $settings = Get-Content $settingsPath -Raw | ConvertFrom-Json
        if ($settings.azure_devops -and $settings.azure_devops.branch_prefix) {
            $branchPrefix = $settings.azure_devops.branch_prefix
        }
    }

    if (-not $jiraKey) {
        throw "Cannot determine Jira key from jira-context.md. Run Phase 0 (kickstart) first."
    }

    $workingBranch = "$branchPrefix/$jiraKey"

    # ---------------------------------------------------------------------------
    # Clone the repository
    # ---------------------------------------------------------------------------
    if (-not (Test-Path $reposDir)) {
        New-Item -Path $reposDir -ItemType Directory -Force | Out-Null
    }

    if (Test-Path $clonePath) {
        return @{
            success        = $true
            path           = $clonePath
            default_branch = (git -C $clonePath symbolic-ref refs/remotes/origin/HEAD 2>$null) -replace 'refs/remotes/origin/', ''
            working_branch = $workingBranch
            message        = "Repository already cloned at $clonePath"
            already_cloned = $true
        }
    }

    # Build clone URL with PAT authentication
    $orgHost = ($adoOrgUrl -replace 'https?://', '')
    $cloneUrl = "https://$($adoPat)@$orgHost/$project/_git/$repo"

    try {
        $cloneOutput = & git clone $cloneUrl $clonePath 2>&1
        if ($LASTEXITCODE -ne 0) {
            $errorMsg = ($cloneOutput | Out-String).Trim()
            $errorMsg = $errorMsg -replace [regex]::Escape($adoPat), '***'

            $errorType = if ($errorMsg -match 'Authentication failed|401|403') { "authentication_failed" }
                         elseif ($errorMsg -match 'not found|does not exist|404') { "repo_not_found" }
                         elseif ($errorMsg -match 'timeout|Could not resolve host') { "network_error" }
                         else { "clone_failed" }

            return @{
                success    = $false
                error_type = $errorType
                message    = "Clone failed for $repo from $project`: $errorMsg"
                path       = $null
            }
        }
    } catch {
        return @{
            success    = $false
            error_type = "exception"
            message    = "Failed to clone $repo from $project`: $_"
            path       = $null
        }
    }

    # Detect default branch
    $defaultBranch = & git -C $clonePath symbolic-ref refs/remotes/origin/HEAD 2>$null
    $defaultBranch = $defaultBranch -replace 'refs/remotes/origin/', ''
    if (-not $defaultBranch) { $defaultBranch = "main" }

    # Create initiative branch
    & git -C $clonePath checkout -b $workingBranch 2>&1 | Out-Null

    # ---------------------------------------------------------------------------
    # Configure NuGet authentication (for .NET repos)
    # ---------------------------------------------------------------------------
    $nugetConfig = Join-Path $clonePath "src\NuGet.config"
    if (-not (Test-Path $nugetConfig)) {
        $nugetConfig = Join-Path $clonePath "NuGet.config"
    }

    if (Test-Path $nugetConfig) {
        $nugetVarName = $env:NUGET_FEED_VAR
        if ($nugetVarName) {
            # Try Machine-level var first (corporate workstation setup)
            $nugetPat = [System.Environment]::GetEnvironmentVariable($nugetVarName, "Machine")

            # Fall back to .env.local value
            if (-not $nugetPat) {
                $nugetPat = $env:NUGET_FEED_PAT
            }

            # Fall back to ADO PAT
            if (-not $nugetPat) {
                $nugetPat = $adoPat
            }

            if ($nugetPat) {
                [System.Environment]::SetEnvironmentVariable($nugetVarName, $nugetPat, "Process")
            }
        }
    }

    # ---------------------------------------------------------------------------
    # Return result
    # ---------------------------------------------------------------------------
    return @{
        success        = $true
        path           = $clonePath
        default_branch = $defaultBranch
        working_branch = $workingBranch
        message        = "Cloned $repo from $project, branch: $workingBranch"
        already_cloned = $false
    }
}