Summary
The Copilot hook configuration in copilot-hooks.json uses ${PLUGIN_ROOT} in its bash commands:
"bash": "node \"${PLUGIN_ROOT}/hooks/ponytail-activate.js\"",
"bash": "node \"${PLUGIN_ROOT}/hooks/ponytail-mode-tracker.js\"",
When VS Code runs on Windows and connects to a Linux remote environment (WSL), the host expands ${PLUGIN_ROOT} before invoking Bash. The resulting command can contain a Windows-style path, which is invalid in the remote Linux shell and causes Node.js to fail to locate the hook script.
Using $PLUGIN_ROOT in the Bash-specific command defers expansion to Bash, which receives the correct remote environment variable.
Environment
- Ponytail: 4.9.0 / current main
- Client: GitHub Copilot in VS Code
- Local host: Windows
- Remote extension host: Linux
- Hook configuration:
hooks/copilot-hooks.json
Steps to reproduce
- Install the Ponytail Copilot plugin.
- Open a Linux workspace using VS Code Remote from Windows.
- Start a Copilot agent session.
- Submit a prompt so both
sessionStart and userPromptSubmitted hooks run.
- Inspect the hook command and error output.
Actual behavior
${PLUGIN_ROOT} is substituted before Bash runs. The generated command contains a host-style path or separators that are invalid in the Linux environment. Node.js cannot find ponytail-activate.js or ponytail-mode-tracker.js.
Expected behavior
The Bash hook should resolve PLUGIN_ROOT using the environment of the remote Bash process and execute both scripts successfully.
Proposed fix
Use shell-native expansion in each platform-specific field:
- "bash": "node \"${PLUGIN_ROOT}/hooks/ponytail-activate.js\"",
+ "bash": "node \"$PLUGIN_ROOT/hooks/ponytail-activate.js\"",
"powershell": "node \"${PLUGIN_ROOT}\\hooks\\ponytail-activate.js\""
Apply the same change to ponytail-mode-tracker.js.
The PowerShell form should remain unchanged because ${PLUGIN_ROOT} is valid PowerShell syntax. The Bash form should use $PLUGIN_ROOT.
This preserves the existing schema and platform behavior. An args field is not proposed because it is not part of the currently supported copilot-hooks.json command schema.
Regression tests
Add coverage to tests/copilot-plugin.test.js:
- Load
hooks/copilot-hooks.json.
- Assert every Bash command uses
"$PLUGIN_ROOT/..." and does not contain "${PLUGIN_ROOT}/...".
- Assert PowerShell commands retain
"${PLUGIN_ROOT}\\...".
- Execute both Bash commands with
PLUGIN_ROOT pointing to a temporary path containing spaces.
- Assert both processes exit with status 0.
Example runtime setup:
const env = {
...process.env,
PLUGIN_ROOT: pluginRootWithSpaces,
HOME: temporaryHome,
USERPROFILE: temporaryHome,
PONYTAIL_DEFAULT_MODE: 'full',
};
const result = spawnSync('bash', ['-c', hook.bash], {
env,
input: '',
encoding: 'utf8',
});
assert.equal(result.status, 0, result.stderr);
Run the complete suite with:
Summary
The Copilot hook configuration in
copilot-hooks.jsonuses${PLUGIN_ROOT}in itsbashcommands:When VS Code runs on Windows and connects to a Linux remote environment (WSL), the host expands
${PLUGIN_ROOT}before invoking Bash. The resulting command can contain a Windows-style path, which is invalid in the remote Linux shell and causes Node.js to fail to locate the hook script.Using
$PLUGIN_ROOTin the Bash-specific command defers expansion to Bash, which receives the correct remote environment variable.Environment
hooks/copilot-hooks.jsonSteps to reproduce
sessionStartanduserPromptSubmittedhooks run.Actual behavior
${PLUGIN_ROOT}is substituted before Bash runs. The generated command contains a host-style path or separators that are invalid in the Linux environment. Node.js cannot findponytail-activate.jsorponytail-mode-tracker.js.Expected behavior
The Bash hook should resolve
PLUGIN_ROOTusing the environment of the remote Bash process and execute both scripts successfully.Proposed fix
Use shell-native expansion in each platform-specific field:
Apply the same change to
ponytail-mode-tracker.js.The PowerShell form should remain unchanged because
${PLUGIN_ROOT}is valid PowerShell syntax. The Bash form should use$PLUGIN_ROOT.This preserves the existing schema and platform behavior. An
argsfield is not proposed because it is not part of the currently supportedcopilot-hooks.jsoncommand schema.Regression tests
Add coverage to
tests/copilot-plugin.test.js:hooks/copilot-hooks.json."$PLUGIN_ROOT/..."and does not contain"${PLUGIN_ROOT}/..."."${PLUGIN_ROOT}\\...".PLUGIN_ROOTpointing to a temporary path containing spaces.Example runtime setup:
Run the complete suite with:
npm test