Skip to content

Copilot Bash hooks resolve ${PLUGIN_ROOT} incorrectly in VS Code Remote #759

Description

@carmona-valencia

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

  1. Install the Ponytail Copilot plugin.
  2. Open a Linux workspace using VS Code Remote from Windows.
  3. Start a Copilot agent session.
  4. Submit a prompt so both sessionStart and userPromptSubmitted hooks run.
  5. 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:

  1. Load hooks/copilot-hooks.json.
  2. Assert every Bash command uses "$PLUGIN_ROOT/..." and does not contain "${PLUGIN_ROOT}/...".
  3. Assert PowerShell commands retain "${PLUGIN_ROOT}\\...".
  4. Execute both Bash commands with PLUGIN_ROOT pointing to a temporary path containing spaces.
  5. 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:

npm test

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions