| File: Agents\Hooks\HookCommandFormatter.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Aspire.Cli.Agents.Hooks; /// <summary> /// Builds the shell-form command strings used in the GitHub Copilot CLI hook configuration, with correct /// quoting for paths that may contain spaces or apostrophes. Claude Code hooks instead use exec form /// (<c>command</c> + <c>args</c>), which passes the script path as a discrete argument and needs no quoting. /// </summary> internal static class HookCommandFormatter { /// <summary> /// Quotes a path as a single bash argument. Bash single quotes are literal, so an embedded /// apostrophe is closed, escaped, and reopened: <c>'</c> becomes <c>'\''</c>. /// Example: <c>/home/o'brien/x.sh</c> → <c>'/home/o'\''brien/x.sh'</c>. /// </summary> public static string QuoteForBash(string path) => $"'{path.Replace("'", "'\\''")}'"; /// <summary> /// Quotes a path as a single PowerShell literal string. PowerShell single quotes are literal and /// an embedded apostrophe is doubled: <c>'</c> becomes <c>''</c>. /// Example: <c>C:\Users\o'brien\x.ps1</c> → <c>'C:\Users\o''brien\x.ps1'</c>. /// </summary> public static string QuoteForPowerShell(string path) => $"'{path.Replace("'", "''")}'"; /// <summary> /// Builds the Unix command that runs the shell hook script via bash. Running through <c>bash</c> /// (rather than executing the path directly) avoids depending on the executable bit surviving. /// </summary> public static string BuildBashCommand(string shellScriptPath) => $"bash {QuoteForBash(shellScriptPath)}"; /// <summary> /// Builds the Windows command that runs the PowerShell hook script with PowerShell 7+ (<c>pwsh</c>). /// The GitHub Copilot CLI hooks reference makes <c>pwsh</c> a hard Windows prerequisite, so the Copilot /// hook must invoke it rather than Windows PowerShell; see /// https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-hooks. It is spawned /// with an explicit bypass policy so the script runs regardless of the ambient execution policy, and /// <c>-NoProfile</c> avoids profile side effects and startup cost. /// </summary> public static string BuildPwshCommand(string powerShellScriptPath) => $"pwsh -NoProfile -ExecutionPolicy Bypass -File {QuoteForPowerShell(powerShellScriptPath)}"; }