diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cc0d24..a52aee8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,8 +35,11 @@ jobs: node-version: '22' cache: npm + - name: Set up npm + run: npm install --global npm@11.16.0 --ignore-scripts + - name: Install dependencies - run: npm ci + run: npm ci --strict-allow-scripts - name: Audit dependencies run: npm run audit diff --git a/.github/workflows/publish-open-vsx.yml b/.github/workflows/publish-open-vsx.yml index 693e945..2e37737 100644 --- a/.github/workflows/publish-open-vsx.yml +++ b/.github/workflows/publish-open-vsx.yml @@ -27,17 +27,50 @@ jobs: with: node-version: '22' - - name: Download verified GitHub Release asset + - name: Download and verify GitHub Release asset env: GH_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ inputs.tag }} run: | + if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then + echo "Release tag must use the stable vMAJOR.MINOR.PATCH format." >&2 + exit 1 + fi + + expected="${RELEASE_TAG#v}" + asset_name="vscode-copilot-cli-launcher-${expected}.vsix" + asset_path=".vsce/${asset_name}" mkdir -p .vsce + + release_digest="$(gh release view "$RELEASE_TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --json assets \ + --jq '.assets[] | select(.name == "'"$asset_name"'") | .digest')" + if [[ ! "$release_digest" =~ ^sha256:[0-9a-f]{64}$ ]]; then + echo "GitHub Release asset is missing an exact SHA-256 digest." >&2 + exit 1 + fi + gh release download "$RELEASE_TAG" \ --repo "$GITHUB_REPOSITORY" \ - --pattern 'vscode-copilot-cli-launcher-*.vsix' \ + --pattern "$asset_name" \ --dir .vsce - test "$(find .vsce -maxdepth 1 -name '*.vsix' | wc -l)" -eq 1 + + actual_digest="sha256:$(sha256sum "$asset_path" | cut -d ' ' -f 1)" + if [ "$actual_digest" != "$release_digest" ]; then + echo "Downloaded VSIX does not match the GitHub Release digest." >&2 + exit 1 + fi + + package_name="$(unzip -p "$asset_path" extension/package.json | jq -r '.name')" + package_publisher="$(unzip -p "$asset_path" extension/package.json | jq -r '.publisher')" + package_version="$(unzip -p "$asset_path" extension/package.json | jq -r '.version')" + test "$package_name" = 'vscode-copilot-cli-launcher' + test "$package_publisher" = 'mikesoft' + test "$package_version" = "$expected" + + echo "VSIX_PATH=$asset_path" >> "$GITHUB_ENV" + echo "EXPECTED_VERSION=$expected" >> "$GITHUB_ENV" - name: Verify Open VSX credential env: @@ -49,22 +82,19 @@ jobs: - name: Publish release asset env: OVSX_PAT: ${{ secrets.OVSX_PAT }} - run: npx --yes ovsx@1.0.2 publish --packagePath "$(find .vsce -maxdepth 1 -name '*.vsix' -print -quit)" + run: npx --yes ovsx@1.0.2 publish --packagePath "$VSIX_PATH" - name: Verify live version - env: - EXPECTED_VERSION: ${{ inputs.tag }} run: | - expected="${EXPECTED_VERSION#v}" for attempt in {1..12}; do current="$(curl --fail --silent --show-error \ https://open-vsx.org/api/mikesoft/vscode-copilot-cli-launcher \ | jq -r '.version')" - if [ "$current" = "$expected" ]; then + if [ "$current" = "$EXPECTED_VERSION" ]; then echo "Open VSX is live at version $current." exit 0 fi sleep 5 done - echo "Expected Open VSX version $expected, but found $current." >&2 + echo "Expected Open VSX version $EXPECTED_VERSION, but found $current." >&2 exit 1 diff --git a/AGENTS.md b/AGENTS.md index 3fb65c5..d68e446 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,9 +19,10 @@ no internal endpoints, customer data, credentials, or unreleased claims. `target: ES2022`, `strict: true`. - Runtime: the VS Code extension host. Declared engine floor is `^1.103.0`; `@types/vscode` must stay aligned with it. -- Tooling runtime: **Node.js 22** (the version CI uses). -- Package manager: **npm**, with the committed `package-lock.json`. Do not introduce a second - package manager, lockfile, or a `packageManager` field — a metadata test asserts its absence. +- Tooling runtime: **Node.js 22** with **npm 11.16.0** (the versions CI uses). +- Package manager: **npm 11.16.0**, with the committed `package-lock.json`. Do not introduce a + second package manager, lockfile, or a `packageManager` field — a metadata test asserts its + absence. - npm install scripts are denied unless their exact package version appears in `allowScripts`. Review the script before approving it with `npm approve-scripts`; never broaden an existing approval or carry it to a new dependency version without re-reviewing that version. @@ -46,7 +47,7 @@ no internal endpoints, customer data, credentials, or unreleased claims. ## Required Commands ```powershell -npm ci +npm ci --strict-allow-scripts npm run check npm run audit ``` @@ -58,7 +59,7 @@ npm run audit - Build a release candidate only after those pass: ```powershell -npm run package -- --out .vsce/vscode-copilot-cli-launcher-.vsix +npm run package -- -- --out .vsce/vscode-copilot-cli-launcher-.vsix ``` ## Generated and Ignored Files @@ -140,15 +141,15 @@ target registry after publication: ## Mandatory Validation Before Handoff -1. `npm ci` succeeds from the committed lockfile. +1. `npm ci --strict-allow-scripts` succeeds from the committed lockfile. 2. `npm run check` passes end to end (compile, unit, metadata, integration, `vsce ls`). 3. `npm run audit` reports no high-severity advisories. 4. `npm run package` produces a VSIX whose file list contains only `out/*.js`, `media/icon.png`, `package.json`, and the root documentation and licence files — no sources, tests, maps, lockfile, or local tooling. 5. Version references are synchronized and `CHANGELOG.md` has a dated entry with real changes only. -6. `npm ci` emits no pending `allowScripts` warning; any newly requested install script is reviewed - and pinned to its exact package version before approval. +6. `npm ci --strict-allow-scripts` emits no pending `allowScripts` warning; any newly requested + install script is reviewed and pinned to its exact package version before approval. ## Instructions for AI Agents diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dee018..8d3e775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Changed + +- Reorganized the README around a concise product overview, direct installation links, verified compatibility boundaries, and a compact security and privacy summary; moved detailed diagnostics into a dedicated troubleshooting guide. +- Corrected npm 11 argument forwarding in the documented release-candidate packaging command so `vsce` receives the requested output path. +- Pinned CI and contributor tooling to npm 11.16.0 and made clean installs fail when a dependency install script is not covered by the exact-version `allowScripts` policy. +- Hardened the Open VSX workflow to verify the release tag, exact asset name, GitHub SHA-256 digest, and packaged extension identity before publishing. + +### Security + +- Updated the lockfile to resolve high-severity development-tooling advisories in `fast-uri`, `js-yaml`, and `undici` without adding runtime dependencies. + ## [0.2.10] - 2026-08-01 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d7f4ece..5b952d7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,10 @@ Thanks for your interest in improving Copilot CLI Launcher. ## Development -Use Node.js 22 and the npm version bundled with it. Install the exact dependency graph from the lockfile, then run the complete validation suite: +Use Node.js 22 with npm 11.16.0. Install the exact dependency graph from the lockfile while enforcing the reviewed install-script allowlist, then run the complete validation suite: ```bash -npm ci +npm ci --strict-allow-scripts npm run check npm run audit ``` diff --git a/README.md b/README.md index 7a3c890..80a0218 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,69 @@ # Copilot CLI Launcher [![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/mikesoft.vscode-copilot-cli-launcher?label=Marketplace&color=6366F1)](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-copilot-cli-launcher) -[![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/mikesoft.vscode-copilot-cli-launcher?color=0EA5E9)](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-copilot-cli-launcher) [![Open VSX](https://img.shields.io/open-vsx/v/mikesoft/vscode-copilot-cli-launcher?label=Open%20VSX&color=a60ee5)](https://open-vsx.org/extension/mikesoft/vscode-copilot-cli-launcher) [![CI](https://github.com/TheStreamCode/github-copilot-cli-launcher/actions/workflows/ci.yml/badge.svg)](https://github.com/TheStreamCode/github-copilot-cli-launcher/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -[![Sponsor](https://img.shields.io/badge/Sponsor-TheStreamCode-ea4aaa?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/TheStreamCode) -A VS Code extension that opens the standalone GitHub Copilot CLI coding agent in a new side terminal directly from the editor toolbar. One click, fresh terminal, ready to go. +Launch the standalone GitHub Copilot CLI coding agent from your editor toolbar in a fresh side terminal. No terminal reuse, telemetry, installers, or background services. + +[Install from Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-copilot-cli-launcher) · [Install from Open VSX](https://open-vsx.org/extension/mikesoft/vscode-copilot-cli-launcher) · [Download the latest VSIX](https://github.com/TheStreamCode/github-copilot-cli-launcher/releases/latest) > **Disclaimer** > This independent extension is unofficial and is not affiliated with, endorsed by, or sponsored by GitHub or Microsoft. See [TRADEMARKS.md](TRADEMARKS.md) for trademark information. -> **✨ Want one launcher for every agent?** Try **[Super CLI](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-super-cli)** — a single sidebar that launches Claude Code, Codex, Copilot, Cursor, Grok, Kilo, Antigravity, OpenCode, and more. Install this launcher for Copilot alone, or Super CLI for the whole set. - -## Features - -- **Toolbar launcher** — one-click button in the editor title area to start Copilot CLI -- **Side-by-side terminal** — opens beside the active editor, never reusing existing terminals -- **Smart working directory** — uses the active editor's workspace folder, with fallback to the first workspace -- **Official installation guidance** — when shell integration detects a missing CLI command, opens GitHub's official installation documentation on request -- **Configurable** — customize the CLI command and terminal label via VS Code settings -- **Optimized bundled visuals** — the same product artwork powers the Marketplace and toolbar while keeping the VSIX compact -- **Windows-ready** — supports quoted executable paths with spaces -- **Privacy-first** — no telemetry, analytics, or personal data collection +## Why Copilot CLI Launcher -## Requirements +- **One click, one fresh terminal** — launch Copilot CLI beside the active editor without replacing or reusing an existing terminal. +- **The right working directory** — start in the active editor's workspace folder, with a predictable fallback for multi-root projects. +- **Local and focused** — no telemetry, analytics, installers, filesystem scanning, or background network behavior. -- VS Code `^1.103.0` -- GitHub Copilot CLI installed and available as `copilot`; see GitHub's [official installation documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli) +## Quick Start -## Installation +1. Install the extension from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-copilot-cli-launcher) or [Open VSX](https://open-vsx.org/extension/mikesoft/vscode-copilot-cli-launcher). +2. Install GitHub Copilot CLI by following GitHub's [official installation documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli). +3. Open a file in a trusted workspace and click the launcher button in the editor title. -1. **Install the extension** from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-copilot-cli-launcher). +Requires VS Code `^1.103.0` or a compatible editor and a working `copilot` command. -2. **Install GitHub Copilot CLI** using one of the methods in GitHub's [official installation documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli). - -3. Open any file in VS Code and click the launcher button in the editor title. +## Features -## Missing CLI Guidance +- Toolbar command in the editor title area +- A new side terminal for every launch +- Active-editor workspace resolution with multi-root fallback +- Configurable CLI command and terminal label +- Quoted executable-path support on Windows +- Missing-command guidance through official GitHub documentation +- Workspace Trust enforcement and machine-scoped command configuration -Missing-command guidance depends on terminal shell integration, which lets the extension observe command completion and output. When a configured CLI command is not found, the extension can open GitHub's official Copilot CLI installation documentation in your default browser. The extension does not download software, create installer files, or run installation commands. +## Compatibility -Without shell integration, the launcher sends the configured command through its fallback path and cannot detect that the command is missing. +| Environment | Support | Validation | +| --- | --- | --- | +| VS Code `^1.103.0` | Supported | Integration smoke test in CI | +| Windows | Supported | Tested in CI; quoted executable paths are covered | +| Linux | Supported | Tested in CI | +| macOS | Supported | Platform-neutral implementation; not in the CI matrix | +| Cursor and Windsurf | Compatible | Uses the VS Code extension API; not in the CI matrix | ## How It Works -Each launch creates a new terminal beside the current editor and sends the configured command immediately. Existing terminals are never reused. +Each launch creates a new terminal beside the current editor and sends the configured command exactly once. Existing terminals are never reused. -The launcher uses the workspace folder of the active editor for the terminal's working directory. If the active editor is outside the workspace, it falls back to the first workspace folder. With no workspace folder, the launcher does not set a working directory. +The launcher uses the active editor's workspace folder as the terminal's working directory. If the active editor is outside the workspace, it falls back to the first workspace folder. With no workspace folder, it leaves the working directory unset. -## Workspace Trust and Command Safety +## Security and Privacy -The launcher does not start Copilot CLI until the current workspace is trusted because it runs a terminal command in that workspace. Review workspace trust before launching Copilot CLI in a repository you do not trust. +| Area | Behavior | +| --- | --- | +| Workspace Trust | The launcher will not run the command until the current workspace is trusted. | +| Command source | `copilotCliLauncher.cliCommand` uses only its default or user/machine value; workspace-controlled values are ignored. | +| Terminal output | Diagnostic output is bounded, kept in memory, and never persisted. | +| Data collection | Copilot CLI Launcher does not collect telemetry, analytics, or personal data. | +| Downloads and installation | The extension does not download software, create installer files, or run installation commands. | +| Background activity | No filesystem scanning, automatic downloads, background services, or extension-initiated network checks. | -`copilotCliLauncher.cliCommand` is a machine-scoped setting. The launcher uses only its default or user/machine value and ignores workspace-controlled values, so a workspace cannot set the command it launches. +The configured command runs inside the selected workspace, so review Workspace Trust before launching Copilot CLI in an unfamiliar repository. The separate Copilot CLI process remains subject to GitHub's own behavior and terms. ## Configuration @@ -63,9 +72,7 @@ The launcher does not start Copilot CLI until the current workspace is trusted b | `copilotCliLauncher.cliCommand` | `copilot` | Machine-scoped command executed when the launcher button is clicked; workspace values are ignored. | | `copilotCliLauncher.terminalName` | `Copilot CLI` | Base label used for the created terminal. | -Open settings via the Command Palette: **Copilot CLI Launcher: Open Settings** - -**Examples:** +Open settings from the Command Palette with **Copilot CLI Launcher: Open Settings**. Default command: @@ -79,71 +86,38 @@ Windows executable path with spaces: "copilotCliLauncher.cliCommand": "\"C:\\Program Files\\GitHub Copilot\\copilot.exe\"" ``` -## Known Issue: GitHub Copilot Chat Bootstrapper Hang - -On Windows, if the GitHub Copilot Chat extension for VS Code is or was ever installed, it can leave a `copilot`/`copilot.bat`/`copilot.ps1` bootstrapper script under its extension global storage (`...\Code\User\globalStorage\github.copilot-chat\copilotCli\`). If that directory appears earlier on your system `PATH` than the real Copilot CLI installation, the plain `copilot` command — this launcher's default — can resolve to that bootstrapper instead of the real binary. - -We have observed that bootstrapper hang instead of exiting when launched from a terminal, leaving an orphaned `powershell.exe` process running indefinitely and consuming CPU. Because each launch can leave behind a new stuck process, repeated use compounds the effect on system responsiveness. - -This launcher cannot see which binary your shell will actually resolve `copilot` to — it only starts a terminal and sends the configured command, the same as typing it yourself. It therefore cannot silently work around the hang. Instead, it blocks the launch and asks for confirmation: - -- If `copilotCliLauncher.cliCommand` is explicitly set to a path inside `github.copilot-chat\copilotCli\`, every launch is blocked until you confirm. -- If you are using the plain default `copilot` command on Windows, the first launch each VS Code session is blocked with a warning; choosing **Launch Anyway** proceeds for the rest of that session. Dismissing the warning any other way (Escape, clicking outside, "Open Settings", "Learn More") blocks that launch and asks again next time, since it does not mean you have accepted the risk. - -**Mitigation:** set `copilotCliLauncher.cliCommand` to the full, quoted path of your real Copilot CLI executable (for example the WinGet or npm install location) so it never depends on `PATH` order: - -```json -"copilotCliLauncher.cliCommand": "\"C:\\Users\\\\AppData\\Local\\Microsoft\\WinGet\\Packages\\GitHub.Copilot_Microsoft.Winget.Source_8wekyb3d8bbwe\\copilot.exe\"" -``` - -Find your real binary's path with `Get-Command copilot -All` in PowerShell, or `where copilot` — it lists every match on `PATH` in resolution order. +## Missing CLI Guidance -This is not a bug in this launcher, the Copilot CLI binary, or your BYOK/model-switching setup; it is a hang in GitHub's own bootstrapper script when launched as a non-interactive terminal child process. If you can reproduce it outside this launcher, please report it to GitHub through the official [Copilot CLI documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli). +When terminal shell integration is available, the extension can detect a missing configured command and offer to open GitHub's official installation documentation. Without shell integration, it sends the command through its fallback path and cannot determine whether the command exists. ## Troubleshooting -### Copilot CLI is not found - -Follow GitHub's [official installation documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli), then verify that `copilot` is available in a regular terminal. - -If your setup relies on shell initialization, restart VS Code after installation so new terminals inherit the updated environment. - -### Nothing happens after clicking the button +On Windows, an older GitHub Copilot Chat bootstrapper can sometimes take precedence over the real `copilot` executable and hang. The launcher warns before a potentially affected launch because it cannot inspect your `PATH` without weakening its local-only design. -Check the value of `copilotCliLauncher.cliCommand` in settings and verify that the same command works in a regular terminal. +The most reliable mitigation is to set `copilotCliLauncher.cliCommand` to the full, quoted path of the real executable. Use `Get-Command copilot -All` in PowerShell or `where copilot` to inspect command resolution. -### Custom executable path on Windows - -Quote executable paths that contain spaces, for example: `"C:\Program Files\GitHub Copilot\copilot.exe"`. - -### Multi-root workspaces - -The launcher uses the workspace folder of the active editor. To control where Copilot CLI starts in a multi-root window, open a file from the target workspace before clicking the toolbar button. - -## Privacy - -Copilot CLI Launcher does not collect telemetry, analytics, or personal data. - -The extension ships its bundled visual assets inside the VSIX package and does not fetch remote images at runtime. +For symptoms, mitigation examples, missing-command help, and multi-root behavior, see the [complete troubleshooting guide](docs/TROUBLESHOOTING.md). ## Development ```bash -npm ci +npm ci --strict-allow-scripts npm run check npm run audit npm run package ``` -`npm run package` creates the `.vsix` file in the workspace root. +`npm run check` performs a clean compile, unit and metadata tests, a VS Code integration smoke test, and package-content validation. CI repeats the gate on Windows and Linux and rejects high-severity dependency advisories. `npm run package` creates the `.vsix` release candidate. + +## Related Project -`npm run check` starts from a clean build, then runs unit tests, metadata checks, a VS Code integration smoke test, and a package-content validation. CI repeats those checks on Windows and Linux and rejects high-severity dependency advisories. +Need one launcher for several coding agents? [Super CLI](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-super-cli) provides a unified sidebar for Copilot, Claude Code, Codex, Cursor, Grok, Kilo, Antigravity, OpenCode, and more. ## Support -Open a [GitHub issue](https://github.com/TheStreamCode/github-copilot-cli-launcher/issues) for bugs and feature requests. For support details, see [SUPPORT.md](SUPPORT.md). +Open a [GitHub issue](https://github.com/TheStreamCode/github-copilot-cli-launcher/issues) for bugs and feature requests. See [SUPPORT.md](SUPPORT.md) for reporting guidance and contact options. -Financial support for the independent maintainer is available through GitHub Sponsors: [github.com/sponsors/TheStreamCode](https://github.com/sponsors/TheStreamCode). +You can support the independent maintainer through [GitHub Sponsors](https://github.com/sponsors/TheStreamCode). ## License diff --git a/docs/README.md b/docs/README.md index 21a5ff8..357315a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,12 +1,13 @@ # Documentation -This directory contains engineering documents for the repository. End-user installation, usage, and troubleshooting guidance lives in the root `README.md`. +This directory contains durable product and engineering documentation. The root `README.md` provides the concise product overview, quick start, configuration, and first-line troubleshooting. ## Engineering Notes Add durable engineering notes here when a change needs more context than the code, tests, changelog, or pull request can provide. Use descriptive Markdown filenames and include the decision, alternatives considered, and validation evidence. - [`RELEASING.md`](RELEASING.md): versioning, validation, GitHub release, and registry publication checklist +- [`TROUBLESHOOTING.md`](TROUBLESHOOTING.md): detailed launcher diagnostics, Windows bootstrapper mitigation, missing-command guidance, and workspace behavior The current release focuses on one-click GitHub Copilot CLI launch from the editor toolbar and safe missing-command guidance through GitHub's official installation documentation. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index a51556a..66b36aa 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -17,12 +17,12 @@ Move completed changelog entries from `Unreleased` into a dated release section ## 2. Validate from a Clean Install ```powershell -npm ci +npm ci --strict-allow-scripts npm run check npm run audit $version = node -p "require('./package.json').version" $vsixPath = ".vsce/vscode-copilot-cli-launcher-$version.vsix" -npm run package -- --out $vsixPath +npm run package -- -- --out $vsixPath ``` Confirm that the VSIX contains only the runtime JavaScript, required metadata, licenses, documentation, and bundled product artwork. @@ -39,6 +39,8 @@ Provide `OVSX_PAT` through an approved environment or CI secret store. Never pas For the standard repository flow, save it as the GitHub Actions secret `OVSX_PAT`, then manually dispatch **Publish Open VSX** with the existing GitHub Release tag. The workflow downloads and publishes that release's verified VSIX asset and confirms the registry version. +Before using the publishing credential, the workflow requires a stable semantic-version tag, downloads the exact versioned asset, verifies its GitHub SHA-256 digest, and checks the packaged name, publisher, and version. + For an authorized local fallback: ```powershell diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 0000000..5e5d837 --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,83 @@ +# Troubleshooting + +This guide covers launcher-specific diagnostics. For installing or troubleshooting GitHub Copilot CLI itself, start with GitHub's [official installation documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli). + +## GitHub Copilot Chat Bootstrapper Hang on Windows + +### Symptoms + +If the GitHub Copilot Chat extension for VS Code is or was installed, it can leave a `copilot`, `copilot.bat`, or `copilot.ps1` bootstrapper under its extension global storage directory: + +```text +...\Code\User\globalStorage\github.copilot-chat\copilotCli\ +``` + +If that directory appears earlier on `PATH` than the real Copilot CLI installation, the plain `copilot` command can resolve to the bootstrapper instead of the actual executable. The bootstrapper may hang in a terminal and leave an orphaned `powershell.exe` process consuming CPU. Repeated launches can leave multiple stuck processes. + +You can reproduce the resolution outside this extension by running the same command in a regular terminal. + +### Why the Launcher Warns + +Copilot CLI Launcher starts a terminal and sends the configured command, just as a user would type it. It deliberately does not scan the filesystem or resolve `PATH`, so it cannot silently choose a different binary. + +- An explicit command path inside `github.copilot-chat\copilotCli\` requires confirmation on every launch. +- The plain `copilot` command on Windows requires confirmation on the first launch of each VS Code session. +- Only **Launch Anyway** accepts the risk. Dismissing the prompt or choosing another action blocks that launch. + +### Recommended Mitigation + +Find every matching command in resolution order: + +```powershell +Get-Command copilot -All +where copilot +``` + +Then set `copilotCliLauncher.cliCommand` to the full, quoted path of the real Copilot CLI executable. For example: + +```json +"copilotCliLauncher.cliCommand": "\"C:\\Users\\\\AppData\\Local\\Microsoft\\WinGet\\Packages\\GitHub.Copilot_Microsoft.Winget.Source_8wekyb3d8bbwe\\copilot.exe\"" +``` + +The exact location depends on how Copilot CLI was installed. Verify the path on your machine instead of copying the example unchanged. + +If the same hang occurs when you run the resolved bootstrapper directly in a regular terminal, report it to GitHub through the official Copilot CLI documentation. The launcher does not modify or replace GitHub's bootstrapper. + +## Copilot CLI Is Not Found + +Follow GitHub's [official installation documentation](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli), then verify the command in a regular terminal: + +```powershell +copilot --version +``` + +Restart VS Code after installing the CLI so newly created terminals inherit the updated environment. + +Missing-command guidance depends on VS Code terminal shell integration. When integration is unavailable, the launcher can send the command but cannot observe its completion or determine that it is missing. The extension does not download software, create installer files, or run installation commands. + +## Nothing Happens After Clicking the Button + +1. Confirm the workspace is trusted. +2. Open **Copilot CLI Launcher: Open Settings** from the Command Palette. +3. Verify that `copilotCliLauncher.cliCommand` works in a regular terminal. +4. If the path contains spaces, quote the executable path in the setting. + +## Custom Executable Paths on Windows + +Use escaped double quotes around executable paths that contain spaces: + +```json +"copilotCliLauncher.cliCommand": "\"C:\\Program Files\\GitHub Copilot\\copilot.exe\"" +``` + +The setting is machine-scoped. Workspace configuration cannot replace the command that is launched. + +## Multi-root Workspaces + +The launcher uses the workspace folder containing the active editor. To select a starting folder in a multi-root window, open a file from the target workspace before clicking the launcher button. + +If the active editor is outside every workspace folder, the launcher uses the first workspace folder. If no workspace is open, it leaves the terminal working directory unset. + +## Reporting a Launcher Issue + +If the configured command works in a regular VS Code terminal but not through the launcher, open a [GitHub issue](https://github.com/TheStreamCode/github-copilot-cli-launcher/issues) and include the non-sensitive diagnostics listed in [SUPPORT.md](../SUPPORT.md). Never include credentials, tokens, customer data, or private repository content. diff --git a/package-lock.json b/package-lock.json index f7a6af6..8bbad3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1999,9 +1999,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, "funding": [ { @@ -2579,9 +2579,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -4411,9 +4411,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "dev": true, "license": "MIT", "engines": { diff --git a/test/metadata.test.js b/test/metadata.test.js index 5ed1c29..4df913a 100644 --- a/test/metadata.test.js +++ b/test/metadata.test.js @@ -127,15 +127,23 @@ test('release metadata versions stay synchronized', () => { assert.match(openVsxWorkflow, new RegExp(`^\\s*default: v${version}\\s*$`, 'm')); }); -test('README is organized around user-facing setup, configuration, and troubleshooting', () => { +test('README presents the product, trust boundaries, setup, and support clearly', () => { const readme = readText('README.md'); assert.match(readme, /^# Copilot CLI Launcher$/m); - assert.match(readme, /opens the standalone GitHub Copilot CLI coding agent in a new side terminal/i); + assert.match(readme, /Launch the standalone GitHub Copilot CLI coding agent from your editor toolbar/i); + assert.match(readme, /Install from Visual Studio Marketplace/); + assert.match(readme, /Install from Open VSX/); + assert.match(readme, /Download the latest VSIX/); + assert.match(readme, /## Why Copilot CLI Launcher/); + assert.match(readme, /## Quick Start/); assert.match(readme, /## Features/); + assert.match(readme, /## Compatibility/); + assert.match(readme, /## Security and Privacy/); assert.match(readme, /## Missing CLI Guidance/); assert.match(readme, /## Configuration/); assert.match(readme, /## Troubleshooting/); + assert.match(readme, /docs\/TROUBLESHOOTING\.md/); assert.match(readme, /Copilot CLI Launcher: Open Settings/); assert.match(readme, /official installation documentation/i); assert.match(readme, /does not download software, create installer files, or run installation commands/i); @@ -158,11 +166,23 @@ test('SUPPORT explains when to use issues and when to contact the maintainer dir test('docs directory includes an accurate engineering documentation index', () => { const docsReadme = readText('docs/README.md'); + const agentGuide = readText('AGENTS.md'); + const releasing = readText('docs/RELEASING.md'); + const troubleshooting = readText('docs/TROUBLESHOOTING.md'); assert.match(docsReadme, /^# Documentation$/m); assert.match(docsReadme, /root `README\.md`/); assert.match(docsReadme, /Add durable engineering notes here/i); + assert.match(docsReadme, /TROUBLESHOOTING\.md/); assert.doesNotMatch(docsReadme, /`(?:specs|plans)\/`/); + + assert.match(troubleshooting, /^# Troubleshooting$/m); + assert.match(troubleshooting, /GitHub Copilot Chat Bootstrapper Hang on Windows/); + assert.match(troubleshooting, /## Copilot CLI Is Not Found/); + assert.match(troubleshooting, /## Multi-root Workspaces/); + assert.match(troubleshooting, /does not download software, create installer files, or run installation commands/i); + assert.match(agentGuide, /npm run package -- -- --out \.vsce\/vscode-copilot-cli-launcher-\.vsix/); + assert.match(releasing, /npm run package -- -- --out \$vsixPath/); }); test('README documents key features and privacy notes', () => { @@ -220,12 +240,31 @@ test('CI workflow validates the extension with npm on Windows and Linux', () => assert.match(workflow, /windows-latest/); assert.match(workflow, /ubuntu-latest/); assert.match(workflow, /cache: npm/); - assert.match(workflow, /npm ci/); + assert.match(workflow, /run: npm install --global npm@11\.16\.0 --ignore-scripts/); + assert.match(workflow, /run: npm ci --strict-allow-scripts/); assert.match(workflow, /npm run audit/); assert.match(workflow, /npm run check/); assert.match(workflow, /persist-credentials: false/); }); +test('Open VSX workflow verifies the release asset before publishing it', () => { + const workflow = readText('.github/workflows/publish-open-vsx.yml'); + const verificationStep = workflow.indexOf('Download and verify GitHub Release asset'); + const publishStep = workflow.indexOf('Publish release asset'); + + assert.ok(verificationStep >= 0); + assert.ok(publishStep > verificationStep); + assert.match(workflow, /Release tag must use the stable vMAJOR\.MINOR\.PATCH format/); + assert.match(workflow, /asset_name="vscode-copilot-cli-launcher-\$\{expected\}\.vsix"/); + assert.match(workflow, /\.assets\[\].*\.digest/); + assert.match(workflow, /sha256sum "\$asset_path"/); + assert.match(workflow, /unzip -p "\$asset_path" extension\/package\.json/); + assert.match(workflow, /test "\$package_name" = 'vscode-copilot-cli-launcher'/); + assert.match(workflow, /test "\$package_publisher" = 'mikesoft'/); + assert.match(workflow, /test "\$package_version" = "\$expected"/); + assert.match(workflow, /publish --packagePath "\$VSIX_PATH"/); +}); + test('per-launch listeners stay out of the never-drained extension subscriptions array', () => { const extensionSource = readText('src/extension.ts'); const subscriptionPushes = extensionSource.match(/context\.subscriptions\.push\(/g) ?? [];