diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 7a332ea..a9e9d3c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -50,7 +50,7 @@ body: id: extension_version attributes: label: Extension version - placeholder: 0.1.8 + placeholder: Copy the installed version from the Extensions view. validations: required: true - type: input diff --git a/.github/social-preview.png b/.github/social-preview.png new file mode 100644 index 0000000..46eedb7 Binary files /dev/null and b/.github/social-preview.png differ diff --git a/.github/social-preview.svg b/.github/social-preview.svg new file mode 100644 index 0000000..a51c8f1 --- /dev/null +++ b/.github/social-preview.svg @@ -0,0 +1,82 @@ + + Codex CLI Launcher social preview + Unofficial VS Code extension that launches Codex CLI in a side terminal. + + + + + + + + + + + + + + + + + + + + + + + + + + + + UNOFFICIAL · OPEN SOURCE + + Codex CLI + Launcher + + One click. Fresh side terminal. Local CLI. + VS Code · Windows · macOS · Linux + + + + Editor → Terminal + + + + + + + + + + + project.ts — Visual Studio Code + + + 1 + + 2 + + 3 + + 4 + + + + + + + + CODEX CLI + + + ❯ codex + + + + + + + THESTREAMCODE / MIKESOFT + MIT LICENSE + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 8855f91..9d23ba1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ ## [Unreleased] +## 0.1.10 - 2026-08-08 + +### Fixed + +- Recognized PowerShell commands that use the `&` call operator with a quoted Codex executable path, while continuing to send the configured command to the shell unchanged. + +### Added + +- Added a deterministic GitHub social preview and regression checks for its dimensions, affiliation wording, and repository-only packaging. + +### Changed + +- Documented shell-specific Windows command examples and explicit signed-tag creation and verification steps for future releases. +- Refined the GitHub and Marketplace presentation with clearer installation paths, a concise trust model, an explicit compatibility matrix, and less prominent cross-promotion. +- Removed unverified Cursor and Windsurf compatibility claims from Marketplace metadata. + +### Security + +- Updated transitive development dependencies to resolve the current `fast-uri`, `js-yaml`, and `undici` advisories in the packaging toolchain. + ## 0.1.9 - 2026-08-01 ### Fixed diff --git a/CITATION.cff b/CITATION.cff index 66c0ca8..f1b80ea 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,5 +5,6 @@ authors: - family-names: Gasperini given-names: Michael url: "https://github.com/TheStreamCode/codex-cli-launcher" -version: "0.1.9" +version: "0.1.10" +date-released: "2026-08-08" license: MIT diff --git a/README.md b/README.md index 3d1b230..fed7fab 100644 --- a/README.md +++ b/README.md @@ -5,43 +5,65 @@ [![Open VSX](https://img.shields.io/open-vsx/v/mikesoft/vscode-codex-cli-launcher?label=Open%20VSX&color=a60ee5)](https://open-vsx.org/extension/mikesoft/vscode-codex-cli-launcher) [![CI](https://github.com/TheStreamCode/codex-cli-launcher/actions/workflows/ci.yml/badge.svg)](https://github.com/TheStreamCode/codex-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) -Codex CLI Launcher is an unofficial VS Code extension that opens OpenAI Codex CLI in a new side terminal directly from the editor toolbar. +Launch OpenAI Codex CLI in a fresh side terminal directly from the VS Code editor toolbar. -Works on Windows, macOS, and Linux where Codex CLI is available to the integrated terminal. +[**Install from Visual Studio Marketplace**](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-codex-cli-launcher) · [**Install from Open VSX**](https://open-vsx.org/extension/mikesoft/vscode-codex-cli-launcher) -Current release: `0.1.9`. +Latest GitHub release: [`v0.1.10`](https://github.com/TheStreamCode/codex-cli-launcher/releases/tag/v0.1.10) -> **Disclaimer** -> This extension is unofficial and is not affiliated with, endorsed by, sponsored by, or approved by OpenAI. "OpenAI", "Codex", and related names are trademarks of their respective owners. +Codex CLI Launcher is a small, unofficial VS Code extension for developers who already use Codex CLI and want a predictable one-click launcher inside the editor. It works on Windows, macOS, and Linux wherever `codex` is available to the integrated terminal. -> **✨ 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 Codex alone, or Super CLI for the whole set. +> **Unofficial project:** This extension is not affiliated with, endorsed by, sponsored by, or approved by OpenAI. "OpenAI", "Codex", and related names are trademarks of their respective owners. ## Features -- Adds a launcher button to the editor title area -- Uses local Marketplace and toolbar artwork packaged with the extension -- Opens a fresh terminal beside the active editor on every launch -- Uses the active editor workspace when available, with a fallback to the first open workspace folder -- Runs a configurable Codex CLI command +- Launch Codex CLI from the editor title with one click +- Open a fresh side terminal for every launch instead of reusing existing sessions +- Start in the active editor's workspace, with a predictable fallback for multi-root windows +- Configure the launch command and terminal name without repository-controlled overrides - Links to the official Codex CLI installation documentation when the default `codex` command is not available -- Supports quoted Windows executable paths +- Supports quoted Windows executable paths, including PowerShell's call operator - Does not collect telemetry, analytics, or personal data -## Requirements +## Quick Start -- VS Code `^1.103.0` -- Codex CLI available in the integrated terminal environment, or a working custom launch command configured in settings +1. Install Codex CLI using the [official Codex CLI documentation](https://learn.chatgpt.com/docs/codex/cli). +2. Install Codex CLI Launcher from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-codex-cli-launcher) or [Open VSX](https://open-vsx.org/extension/mikesoft/vscode-codex-cli-launcher). +3. Open a file in a trusted workspace. +4. Click the launcher button in the editor title. -## Installation +VS Code users can also install the extension from a terminal: -1. Install the extension from the VS Code Marketplace. -2. Install Codex CLI by following the [official Codex CLI installation documentation](https://learn.chatgpt.com/docs/codex/cli). -3. Open any file in VS Code. -4. Click the launcher button in the editor title. +```bash +code --install-extension mikesoft.vscode-codex-cli-launcher +``` -Any equivalent install or launch method that makes `codex` available in your terminal also works. +## Trust and Privacy + +- The launcher is disabled until the workspace is trusted. +- The CLI command is machine-scoped and cannot be supplied by a repository. +- The extension never downloads Codex CLI, generates installer scripts, or runs package-manager installation commands. +- Shell-output capture is bounded and used only to recognize a missing default `codex` command. Output from custom commands is not read. +- No telemetry, analytics, or personal data is collected. +- All extension artwork is packaged in the VSIX; no external images are loaded at runtime. + +See [SECURITY.md](SECURITY.md) and the [architecture documentation](docs/architecture.md) for the maintained trust boundaries. + +## Compatibility + +| Environment | Status | +| --- | --- | +| VS Code `^1.103.0` | Supported and covered by the minimum-version integration test | +| Windows | Supported and validated in CI | +| Linux | Supported and validated in CI | +| macOS | Supported; not currently included in the CI matrix | +| Other VS Code-compatible editors | May work when they provide the required VS Code APIs, but are not part of the supported test matrix | + +## Requirements + +- VS Code `^1.103.0` +- Codex CLI available in the integrated terminal environment, or a working custom launch command configured in settings ## Missing CLI @@ -74,10 +96,16 @@ Default command: "codexCliLauncher.cliCommand": "codex" ``` -Windows executable path with spaces: +Windows PowerShell executable path with spaces: + +```json +"codexCliLauncher.cliCommand": "& \"C:\\Program Files\\OpenAI Codex\\codex.cmd\"" +``` + +Windows Command Prompt executable path with spaces: ```json -"codexCliLauncher.cliCommand": "\"C:\\Users\\You\\AppData\\Roaming\\npm\\codex.cmd\"" +"codexCliLauncher.cliCommand": "\"C:\\Program Files\\OpenAI Codex\\codex.cmd\"" ``` ## Troubleshooting @@ -94,18 +122,12 @@ Check `codexCliLauncher.cliCommand` and verify that the same command works in a ### Custom executable path on Windows -Quote executable paths that contain spaces. This is required for commands such as `"C:\Users\You\AppData\Roaming\npm\codex.cmd"`. +Quote executable paths that contain spaces. In PowerShell, use its call operator before the quoted path, for example `& "C:\Program Files\OpenAI Codex\codex.cmd"`. In Command Prompt, use the quoted path directly, for example `"C:\Program Files\OpenAI Codex\codex.cmd"`. ### Multi-root workspaces The launcher prefers the workspace folder of the active editor. To control where Codex starts in a multi-root window, open a file from the target workspace before clicking the toolbar button. -## Privacy - -Codex CLI Launcher does not collect telemetry, analytics, or personal data. - -All extension artwork is packaged locally in the VSIX. No external image assets are loaded at runtime. - ## Development Local verification and packaging: @@ -131,6 +153,10 @@ Open a [GitHub issue](https://github.com/TheStreamCode/codex-cli-launcher/issues Financial support for the independent maintainer is available through GitHub Sponsors: [github.com/sponsors/TheStreamCode](https://github.com/sponsors/TheStreamCode). +## Related Project + +Need one launcher for multiple coding agents? [Super CLI](https://marketplace.visualstudio.com/items?itemName=mikesoft.vscode-super-cli) provides a single sidebar for Codex, Claude Code, Copilot, Cursor, Grok, Kilo, Antigravity, OpenCode, and other agent CLIs. + ## License Released under the [MIT License](LICENSE). diff --git a/docs/architecture.md b/docs/architecture.md index 08929ba..bed7c93 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -22,6 +22,7 @@ Only `activate` registers on `context.subscriptions`: the two commands and the t - Workspace files are untrusted input. They must not control `cliCommand` or bypass the workspace-trust gate. - The configured CLI command is trusted user intent and is sent to the integrated shell. The launcher does not parse arguments into a child process. +- Executable recognition ignores a leading PowerShell call operator only when deciding whether to offer the missing-Codex documentation. The configured command itself is never rewritten. - Terminal output can contain sensitive data. It is never logged or transmitted, is never read for custom commands, and is bounded while checking for a missing Codex executable. - Network access is limited to opening the official Codex CLI documentation at the user's request. The extension does not make background requests. diff --git a/docs/releasing.md b/docs/releasing.md index 883e115..8cc004e 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -14,7 +14,7 @@ Releases are maintainer-only and should start from a clean, up-to-date `main` br 1. Commit the release metadata and merge it through the protected branch workflow. 2. Update the local `main` branch to the merge commit, then rerun `npm ci`, `npm run check`, `npm run audit`, and `npm run package` from that clean commit. -3. Create a signed `vX.Y.Z` tag at the verified commit. +3. Create a signed annotated tag with `git tag -s vX.Y.Z -m "vX.Y.Z"`, then verify it with `git tag -v vX.Y.Z` before pushing it. 4. Publish that exact verified VSIX through the maintainer's Visual Studio Marketplace and Open VSX release channels, then attach the same artifact to a GitHub release with concise notes. 5. Confirm the Marketplace, Open VSX, README badges, GitHub release, and repository default branch all report the same version. diff --git a/package-lock.json b/package-lock.json index 184d632..371c319 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codex-cli-launcher", - "version": "0.1.9", + "version": "0.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-codex-cli-launcher", - "version": "0.1.9", + "version": "0.1.10", "license": "MIT", "devDependencies": { "@types/node": "^22.20.1", @@ -1989,9 +1989,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": [ { @@ -2569,9 +2569,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": [ { @@ -4400,9 +4400,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/package.json b/package.json index 7b73817..3e2ea78 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "vscode-codex-cli-launcher", "private": true, "displayName": "Codex CLI Launcher — Run OpenAI Codex in a Side Terminal", - "description": "Launch the OpenAI Codex AI coding agent in a side terminal from your editor toolbar. Unofficial; works in VS Code, Cursor & Windsurf on Windows, macOS & Linux.", + "description": "Launch the OpenAI Codex AI coding agent in a side terminal from the VS Code editor toolbar. Unofficial; uses your existing CLI setup on Windows, macOS, and Linux.", "publisher": "mikesoft", - "version": "0.1.9", + "version": "0.1.10", "repository": { "type": "git", "url": "https://github.com/TheStreamCode/codex-cli-launcher.git" @@ -91,7 +91,7 @@ "type": "string", "scope": "machine", "default": "codex", - "markdownDescription": "Command used to start Codex CLI. The default is `codex`. On Windows, quote executable paths that contain spaces, for example `\"C:\\Users\\You\\AppData\\Roaming\\npm\\codex.cmd\"`." + "markdownDescription": "Command used to start Codex CLI. The default is `codex`. In PowerShell, prefix a quoted executable path with the call operator, for example `& \"C:\\Program Files\\OpenAI Codex\\codex.cmd\"`. In Command Prompt, use `\"C:\\Program Files\\OpenAI Codex\\codex.cmd\"`." }, "codexCliLauncher.terminalName": { "type": "string", diff --git a/src/command-utils.ts b/src/command-utils.ts index 9c7c879..7eac955 100644 --- a/src/command-utils.ts +++ b/src/command-utils.ts @@ -20,6 +20,20 @@ function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } +function stripPowerShellCallOperator(command: string): string { + const normalized = command.trim(); + const nextCharacter = normalized[1]; + + if ( + normalized[0] === '&' + && (nextCharacter === '"' || nextCharacter === "'" || /\s/.test(nextCharacter ?? '')) + ) { + return normalized.slice(1).trimStart(); + } + + return normalized; +} + function getExecutableBaseName(command: string): string { const executable = extractExecutable(command); const fileName = executable.split(/[\\/]/).pop() ?? executable; @@ -106,9 +120,9 @@ export function buildExtensionSettingsQuery(extensionId: string): string { return `@ext:${extensionId}`; } -/** Extracts the executable token while preserving quoted Windows paths with spaces. */ +/** Extracts the executable token, including quoted paths invoked with PowerShell's call operator. */ export function extractExecutable(command: string): string { - const normalized = command.trim(); + const normalized = stripPowerShellCallOperator(command); if (!normalized) { return ''; diff --git a/test/command-utils.test.js b/test/command-utils.test.js index 727ab4d..a0143f5 100644 --- a/test/command-utils.test.js +++ b/test/command-utils.test.js @@ -83,10 +83,25 @@ test('extractExecutable preserves quoted Windows paths with spaces', () => { ); }); -test('isCodexCommand recognizes direct and quoted Codex executables', () => { +test('extractExecutable recognizes PowerShell call-operator invocations', () => { + assert.equal( + extractExecutable('& "C:\\Program Files\\OpenAI Codex\\codex.cmd" --login'), + 'C:\\Program Files\\OpenAI Codex\\codex.cmd', + ); + assert.equal( + extractExecutable("& 'C:\\Program Files\\OpenAI Codex\\codex.ps1' --login"), + 'C:\\Program Files\\OpenAI Codex\\codex.ps1', + ); +}); + +test('isCodexCommand recognizes direct, quoted, and PowerShell Codex executables', () => { assert.equal(isCodexCommand('codex --login'), true); assert.equal(isCodexCommand('"C:\\Program Files\\OpenAI Codex\\codex.cmd" --login'), true); + assert.equal(isCodexCommand('& codex --login'), true); + assert.equal(isCodexCommand('& "C:\\Program Files\\OpenAI Codex\\codex.cmd" --login'), true); assert.equal(isCodexCommand('custom-codex'), false); + assert.equal(isCodexCommand('& custom-codex'), false); + assert.equal(isCodexCommand('&& codex'), false); }); test('appendBoundedOutput retains only the latest output window', () => { @@ -100,6 +115,10 @@ test('shouldOfferCodexInstallDocs detects PowerShell command-not-found output', const output = "codex: The term 'codex' is not recognized as a name of a cmdlet, function, script file, or executable program."; assert.equal(shouldOfferCodexInstallDocs('codex', 1, output), true); + assert.equal( + shouldOfferCodexInstallDocs('& "C:\\Program Files\\OpenAI Codex\\codex.cmd"', 1, output), + true, + ); }); test('shouldOfferCodexInstallDocs detects POSIX command-not-found exit codes', () => { diff --git a/test/metadata.test.js b/test/metadata.test.js index d1fb6d6..6493848 100644 --- a/test/metadata.test.js +++ b/test/metadata.test.js @@ -39,9 +39,9 @@ test('package metadata is public-ready and clearly unofficial', () => { assert.equal(packageJson.name, 'vscode-codex-cli-launcher'); assert.equal(packageJson.private, true); assert.equal(packageJson.displayName, 'Codex CLI Launcher — Run OpenAI Codex in a Side Terminal'); - assert.equal(packageJson.description, 'Launch the OpenAI Codex AI coding agent in a side terminal from your editor toolbar. Unofficial; works in VS Code, Cursor & Windsurf on Windows, macOS & Linux.'); + assert.equal(packageJson.description, 'Launch the OpenAI Codex AI coding agent in a side terminal from the VS Code editor toolbar. Unofficial; uses your existing CLI setup on Windows, macOS, and Linux.'); assert.equal(packageJson.publisher, 'mikesoft'); - assert.equal(packageJson.version, '0.1.9'); + assert.equal(packageJson.version, '0.1.10'); assert.equal(packageJson.icon, 'media/icon.png'); assert.equal(packageJson.license, 'MIT'); assert.equal(packageJson.repository.url, 'https://github.com/TheStreamCode/codex-cli-launcher.git'); @@ -88,6 +88,18 @@ test('extension assets are packaged on expected paths', () => { assert.ok(commandIcon.length > 0); }); +test('GitHub social preview is deterministic and repository-only', () => { + const socialPreview = readPngSize('.github/social-preview.png'); + const socialPreviewSource = readText('.github/social-preview.svg'); + + assert.equal(socialPreview.width, 1280); + assert.equal(socialPreview.height, 640); + assert.match(socialPreviewSource, /UNOFFICIAL · OPEN SOURCE/); + assert.match(socialPreviewSource, /One click\. Fresh side terminal\. Local CLI\./); + assert.doesNotMatch(socialPreviewSource, / { const readme = readText('README.md'); @@ -95,10 +107,18 @@ test('README covers setup, official installation docs, privacy, and affiliation assert.match(readme, /unofficial VS Code extension/i); assert.match(readme, /not affiliated with, endorsed by, sponsored by, or approved by OpenAI/i); assert.match(readme, /## Features/); + assert.match(readme, /## Quick Start/); + assert.match(readme, /## Trust and Privacy/); + assert.match(readme, /## Compatibility/); + assert.match(readme, /Install from Visual Studio Marketplace/); + assert.match(readme, /Install from Open VSX/); + assert.match(readme, /machine-scoped and cannot be supplied by a repository/i); + assert.match(readme, /macOS \| Supported; not currently included in the CI matrix/); assert.match(readme, /## Missing CLI/); assert.match(readme, /https:\/\/learn\.chatgpt\.com\/docs\/codex\/cli/); assert.match(readme, /does not download installers, create installation scripts, or run package-manager installation commands/i); assert.match(readme, /does not collect telemetry, analytics, or personal data/i); + assert.match(readme, /PowerShell[\s\S]*call operator/i); assert.match(readme, /npm run check/); }); @@ -181,19 +201,33 @@ test('repository governance files provide structured contribution paths', () => const featureRequest = readText('.github/ISSUE_TEMPLATE/feature_request.yml'); const issueConfig = readText('.github/ISSUE_TEMPLATE/config.yml'); const agents = readText('AGENTS.md'); + const releaseGuide = readText('docs/releasing.md'); assert.match(codeowners, /\* @TheStreamCode/); assert.match(bugReport, /^name: Bug report$/m); + assert.match(bugReport, /placeholder: Copy the installed version from the Extensions view\./); assert.match(featureRequest, /^name: Feature request$/m); assert.match(issueConfig, /security\/advisories\/new/); + assert.doesNotMatch(bugReport, /placeholder: 0\.1\.8/); assert.match(agents, /^# Repository Instructions$/m); assert.match(agents, /## Security invariants/); + assert.match(releaseGuide, /git tag -s vX\.Y\.Z/); + assert.match(releaseGuide, /git tag -v vX\.Y\.Z/); }); test('changelog documents the initial release scope', () => { const changelog = readText('CHANGELOG.md'); + const citation = readText('CITATION.cff'); + const readme = readText('README.md'); + const packageLock = JSON.parse(readText('package-lock.json')); assert.match(changelog, /^# Changelog$/m); + assert.match(changelog, /## 0\.1\.10 - 2026-08-08/); + assert.match(readme, /releases\/tag\/v0\.1\.10/); + assert.match(citation, /version: "0\.1\.10"/); + assert.match(citation, /date-released: "2026-08-08"/); + assert.equal(packageLock.version, '0.1.10'); + assert.equal(packageLock.packages[''].version, '0.1.10'); assert.match(changelog, /## 0\.1\.9/); assert.match(changelog, /launch-scoped listeners/); assert.match(changelog, /## 0\.1\.8/);