Skip to content

Add native PowerShell support for Copilot CLI - #206

Open
Ken Kuang (sykuang) wants to merge 3 commits into
microsoft:mainfrom
sykuang:fix/copilot-powershell-launchers
Open

Ken Kuang (sykuang) wants to merge 3 commits into
microsoft:mainfrom
sykuang:fix/copilot-powershell-launchers

Conversation

@sykuang

Copy link
Copy Markdown

Summary

  • Add native PowerShell hook and terminal launchers that reuse the existing Python bootstrap, honor TE_PYTHON, preserve UTF-8 input, and prefer interpreters outside WindowsApps.
  • Route Copilot's Windows hooks and all seven slash commands through PowerShell while keeping the existing Unix commands.
  • Maintain both stable bin/te and bin/te.ps1 launchers on Windows.

Bootstrap fix

Creating the stable launcher only at SessionStart left an already-open session without te.ps1. Slash commands then failed with CommandNotFoundException. Prompt hooks now create or repair the launchers before the command runs, and launcher-write errors are recorded in errors.log.

The regression executes the actual PowerShell blocks from the slash-command files after a prompt hook without a preceding session-start event. It failed with the reported missing-launcher error in both Windows PowerShell 5.1 and PowerShell 7 before the fix, and passes afterward.

Validation

  • python -m pytest tests\test_plugin_package.py tests\test_live_core.py tests\test_live_safety.py tests\test_hook_queue.py -q --tb=short
  • Native checks cover paths containing spaces, apostrophes, and non-ASCII characters; redirected and piped hook input; command arguments and exit codes; missing Python; and WindowsApps discovery.
  • Exercised Copilot's native hook routing against a localhost model stand-in, without an external model call.
  • Repaired the missing live launcher using the existing helper and successfully reran the original stable-path toolprune refresh-catalog command.

Ken Kuang and others added 2 commits November 11, 2026 18:06
Reuse the Python bootstrap for native Windows hooks and CLI launchers, and select PowerShell in Copilot slash commands. Repair stable launchers on prompt submission as well as session start so already-open sessions do not fail with a missing te.ps1. Cover native shells, argument forwarding, interpreter discovery, and the missing-launcher regression.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e7520c60-7772-47ec-8bcf-90f5ec0785dc
Set the console input encoding before reading redirected hook JSON. Assert that the session preserves the exact non-ASCII working directory, and expose errors.log when fail-open hook behavior hides a startup error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e7520c60-7772-47ec-8bcf-90f5ec0785dc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I compared te.ps1 with the te and te-hook shell launchers at the PR head. They pick an interpreter in the same order: TE_PYTHON, python3, python, py, preferring one outside WindowsApps and probing the alias last. Both use py -3 and PYTHONUTF8=1, and both fail open with {} and an errors.log line when there is no Python. $OutputEncoding is a BOM-less UTF8Encoding, so the payload piped to Python doesn't start with a BOM. In ensure_launcher, the .ps1 body escapes ' by doubling it. The read-back comparison is stable on Windows because both the write and the read are in text mode.

Two points:

  1. The two Copilot hook forms use different variables. command uses ${PLUGIN_ROOT}, and the new windows form uses $env:CLAUDE_PLUGIN_ROOT. hosts.detect (hosts.py:1243) already treats COPILOT_PLUGIN_ROOT as the variable Copilot sets for its hook processes. $env:COPILOT_PLUGIN_ROOT would avoid depending on Copilot also exporting the Claude-compatible name. If Copilot does export all three, a line saying so in the README would help the next reader.
  2. #194 (open) adds a minimum-version check to the shell launchers. It skips a python3 that is older than the bootstrap needs. te.ps1 has no such check, so if #194 lands, the two launchers will choose interpreters differently. te_bootstrap.py still refuses an old Python by itself (MIN_VERSION), so the result is a refusal, not a crash. Whichever PR lands second should probably copy the check.

I didn't run the PowerShell tests.

Reviewed with help from Claude Code.

Remove the dependency on CLAUDE_PLUGIN_ROOT in all six Windows hook commands. Exercise hook and slash-command execution with only COPILOT_PLUGIN_ROOT defined so the native root contract is covered.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e7520c60-7772-47ec-8bcf-90f5ec0785dc
@sykuang

Copy link
Copy Markdown
Author

Thanks Muhammad Abdullah Butt (@1AbdullahButt). Addressed the root-variable point in 8466f75: all six Windows hooks now use $env:COPILOT_PLUGIN_ROOT. The native hook/slash-command test supplies only that variable, with neither CLAUDE_PLUGIN_ROOT nor PLUGIN_ROOT set. It failed before the change and passes afterward in Windows PowerShell 5.1 and PowerShell 7.

For #194, I checked its current scope: it is still open and explicitly changes the POSIX branches only, leaving Windows untouched. I am keeping Windows version-aware interpreter selection out of this PR rather than pulling that separate change into it. The existing bootstrap minimum-version check still refuses an unsupported interpreter safely; Windows candidate-skipping can be handled in a separate follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants