fix: shell-launch copilot on windows

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot CLI
2026-03-21 10:44:00 +01:00
co-authored by Copilot
parent 2fbb5dc5a5
commit 56c81fe053
4 changed files with 144 additions and 3 deletions
@@ -1,3 +1,4 @@
using System.Collections;
using GitHub.Copilot.SDK;
namespace Kopaya.AgentHost.Services;
@@ -6,6 +7,7 @@ internal static class CopilotCliPathResolver
{
private const string CopilotCommandName = "copilot";
private const string DefaultWindowsPathExtensions = ".COM;.EXE;.BAT;.CMD";
private static readonly string[] BlockedCliEnvironmentPrefixes = ["BUN_", "COPILOT_", "ELECTRON_", "NODE_", "NPM_"];
public static CopilotClientOptions CreateClientOptions()
{
@@ -21,9 +23,16 @@ internal static class CopilotCliPathResolver
"Kopaya requires the system-installed 'copilot' command on PATH. Install the GitHub Copilot CLI and ensure it is available in the current environment.");
}
CopilotCliLaunch launch = ResolveCliLaunch(
cliPath,
OperatingSystem.IsWindows(),
Environment.GetEnvironmentVariable("ComSpec"));
return new CopilotClientOptions
{
CliPath = cliPath,
CliPath = launch.Path,
CliArgs = launch.Args,
Environment = ResolveCliEnvironment(GetCurrentEnvironmentVariables()),
};
}
@@ -37,6 +46,50 @@ internal static class CopilotCliPathResolver
return ResolveCliPath(pathValue, pathExtValue, isWindows, fileExists);
}
internal static IReadOnlyDictionary<string, string> ResolveCliEnvironment(
IEnumerable<KeyValuePair<string, string?>> environmentVariables)
{
ArgumentNullException.ThrowIfNull(environmentVariables);
Dictionary<string, string> sanitizedEnvironment = new(StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<string, string?> entry in environmentVariables)
{
if (string.IsNullOrWhiteSpace(entry.Key) || entry.Value is null)
{
continue;
}
string normalizedKey = entry.Key.ToUpperInvariant();
if (BlockedCliEnvironmentPrefixes.Any(prefix => normalizedKey.StartsWith(prefix, StringComparison.Ordinal)))
{
continue;
}
sanitizedEnvironment[entry.Key] = entry.Value;
}
return sanitizedEnvironment;
}
internal static CopilotCliLaunch ResolveCliLaunch(string cliPath, bool isWindows, string? commandProcessorPath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(cliPath);
if (!isWindows)
{
return new CopilotCliLaunch(cliPath, []);
}
string launchPath = string.IsNullOrWhiteSpace(commandProcessorPath)
? "cmd.exe"
: commandProcessorPath;
return new CopilotCliLaunch(
launchPath,
["/d", "/s", "/c", CopilotCommandName]);
}
private static string? ResolveCliPath(
string? pathValue,
string? pathExtValue,
@@ -98,4 +151,15 @@ internal static class CopilotCliPathResolver
}
}
}
private static IEnumerable<KeyValuePair<string, string?>> GetCurrentEnvironmentVariables()
{
return Environment.GetEnvironmentVariables()
.Cast<DictionaryEntry>()
.Select(entry => new KeyValuePair<string, string?>(
entry.Key?.ToString() ?? string.Empty,
entry.Value?.ToString()));
}
}
internal sealed record CopilotCliLaunch(string Path, string[] Args);