|
43 | 43 |
|
44 | 44 | $ErrorActionPreference = "Stop" |
45 | 45 |
|
46 | | -$Script:InstallerVersion = "1.9.6" |
| 46 | +$Script:InstallerVersion = "1.9.7" |
47 | 47 |
|
48 | 48 | try { |
49 | 49 | [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 |
@@ -610,12 +610,23 @@ function Get-LatestXTesterVersionFromFeed([string]$VenvPython) { |
610 | 610 | # Available versions: 0.0.23, 0.0.22, ... |
611 | 611 | # Feed auth works here because the surrounding Invoke-WithCleanPythonPath sets |
612 | 612 | # PIP_KEYRING_PROVIDER=import (see that function for why). |
613 | | - $raw = & $VenvPython @( |
614 | | - "-m", "pip", "index", "versions", "xtester", |
615 | | - "--no-cache-dir", |
616 | | - "--index-url", $FeedUrl, |
617 | | - "--extra-index-url", $ExtraIndexUrl |
618 | | - ) 2>&1 |
| 613 | + # |
| 614 | + # The Azure Artifacts credential provider writes benign informational lines |
| 615 | + # (e.g. "[Information] [CredentialProvider]... Acquired bearer token using |
| 616 | + # 'MSAL Silent'") to *stderr*. Under the script-wide $ErrorActionPreference = |
| 617 | + # "Stop", Windows PowerShell 5.1 turns native-command stderr captured via |
| 618 | + # `2>&1` into a terminating NativeCommandError, aborting the installer even |
| 619 | + # though the call succeeded. Relax the preference locally so those stderr |
| 620 | + # lines are captured as plain text and we can rely on $LASTEXITCODE instead. |
| 621 | + $raw = & { |
| 622 | + $ErrorActionPreference = "Continue" |
| 623 | + & $VenvPython @( |
| 624 | + "-m", "pip", "index", "versions", "xtester", |
| 625 | + "--no-cache-dir", |
| 626 | + "--index-url", $FeedUrl, |
| 627 | + "--extra-index-url", $ExtraIndexUrl |
| 628 | + ) 2>&1 |
| 629 | + } |
619 | 630 | if ($LASTEXITCODE -ne 0) { return $null } |
620 | 631 | $text = ($raw | Out-String) |
621 | 632 | $match = [regex]::Match($text, 'xtester\s*\(([^)]+)\)') |
@@ -792,16 +803,32 @@ function Register-ClaudeCode([string]$CommandPath) { |
792 | 803 | } |
793 | 804 | } |
794 | 805 |
|
| 806 | +function Get-VSCodeUserMcpConfigPath { |
| 807 | + # VS Code reads user-global (profile-wide) MCP servers from mcp.json in the |
| 808 | + # User config folder, not from a workspace .vscode/mcp.json. Registering there |
| 809 | + # makes X-Tester available in every workspace instead of only the directory |
| 810 | + # the installer happened to run in. |
| 811 | + if ($IsWindows -or $env:OS -eq "Windows_NT") { |
| 812 | + $base = if ($env:APPDATA) { $env:APPDATA } else { Join-Path $env:USERPROFILE "AppData\Roaming" } |
| 813 | + return Join-Path $base "Code\User\mcp.json" |
| 814 | + } |
| 815 | + if ($IsMacOS) { |
| 816 | + return Join-Path $HOME "Library/Application Support/Code/User/mcp.json" |
| 817 | + } |
| 818 | + $base = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { Join-Path $HOME ".config" } |
| 819 | + return Join-Path $base "Code/User/mcp.json" |
| 820 | +} |
| 821 | + |
795 | 822 | function Register-VSCode([string]$CommandPath) { |
796 | | - $configPath = Join-Path (Get-Location).Path ".vscode\mcp.json" |
| 823 | + $configPath = Get-VSCodeUserMcpConfigPath |
797 | 824 | $config = Read-JsonObject $configPath ([ordered]@{ servers = [ordered]@{} }) |
798 | 825 | if (-not $config.Contains("servers")) { $config["servers"] = [ordered]@{} } |
799 | 826 | $config["servers"][$ServerName] = [ordered]@{ |
800 | 827 | command = $CommandPath |
801 | 828 | env = [ordered]@{} |
802 | 829 | } |
803 | 830 | Write-JsonObject $configPath $config |
804 | | - Success "Wrote VS Code MCP config: $configPath" |
| 831 | + Success "Wrote VS Code user MCP config: $configPath" |
805 | 832 | } |
806 | 833 |
|
807 | 834 | function Test-XTesterMcpSpawn([string]$CommandPath) { |
@@ -914,16 +941,16 @@ if (-not $SkipClientInstall ` |
914 | 941 | } |
915 | 942 |
|
916 | 943 | # Opportunistic: if the user installed for copilot only and VS Code is on PATH, |
917 | | -# offer to register the MCP server in the workspace .vscode/mcp.json too. |
918 | | -# Skipped non-interactively (no host UI) and when the user already asked for |
919 | | -# vscode or -SkipClientInstall. |
| 944 | +# offer to register the MCP server in the VS Code user profile (mcp.json) too, |
| 945 | +# so it is available across all workspaces. Skipped non-interactively (no host |
| 946 | +# UI) and when the user already asked for vscode or -SkipClientInstall. |
920 | 947 | if (-not $SkipClientInstall ` |
921 | 948 | -and ($selectedClients -contains 'copilot') ` |
922 | 949 | -and (-not ($selectedClients -contains 'vscode')) ` |
923 | 950 | -and ($Host.UI.RawUI -ne $null) ` |
924 | 951 | -and (Get-Command 'code' -ErrorAction SilentlyContinue)) { |
925 | 952 | Write-Host "" |
926 | | - $answer = Read-Host "VS Code detected. Also register X-Tester MCP with VS Code (writes .vscode/mcp.json)? [Y/n]" |
| 953 | + $answer = Read-Host "VS Code detected. Also register X-Tester MCP with VS Code (user profile)? [Y/n]" |
927 | 954 | if ([string]::IsNullOrWhiteSpace($answer) -or $answer -match '^(y|yes)$') { |
928 | 955 | Register-VSCode $xtesterMcp |
929 | 956 | } else { |
|
0 commit comments