Skip to content

Add external URI opener for Claude Code VSCode extension support#3

Draft
Copilot wants to merge 5 commits intomainfrom
copilot/ensure-extension-works-vscode
Draft

Add external URI opener for Claude Code VSCode extension support#3
Copilot wants to merge 5 commits intomainfrom
copilot/ensure-extension-works-vscode

Conversation

Copy link

Copilot AI commented Feb 12, 2026

Extension only worked when Claude Code ran in integrated terminal via PLANNOTATOR_BROWSER env var injection. Failed to intercept URLs when Claude Code ran as a VSCode extension using vscode.env.openExternal().

Changes

External URI opener registration (src/extension.ts)

  • Registers window.registerExternalUriOpener for http/https schemes
  • canOpenExternalUri() returns priority 2 for localhost URLs (Plannotator runs locally on dynamic ports)
  • openExternalUri() routes to existing panel manager
  • Type casting handles API missing from @types/vscode@1.85 (available since VSCode 1.54)
  • Optional chaining provides graceful degradation
const windowWithOpener = vscode.window as typeof vscode.window & {
  registerExternalUriOpener?: (
    id: string,
    opener: { canOpenExternalUri(uri: Uri): number | undefined; openExternalUri(uri: Uri): void },
    metadata: { schemes: string[]; label: string }
  ) => Disposable;
};

windowWithOpener.registerExternalUriOpener?.(
  "plannotator-webview.opener",
  {
    canOpenExternalUri: (uri) => {
      const urlString = uri.toString();
      if (
        urlString.startsWith("http://localhost:") ||
        urlString.startsWith("https://localhost:") ||
        urlString.startsWith("http://127.0.0.1:") ||
        urlString.startsWith("https://127.0.0.1:")
      ) {
        return 2;
      }
      return undefined;
    },
    openExternalUri: (uri) => openInPanel(uri.toString())
  },
  { schemes: ["http", "https"], label: "Open Plannotator in VS Code" }
);

URL pattern matching rationale

  • Matches localhost URLs specifically: Plannotator runs on localhost with dynamic ports (localhost:3000, localhost:8080, etc.)
  • Matches both localhost and 127.0.0.1 hostnames
  • Real Plannotator URLs don't contain "plannotator" in the path - they're just localhost:PORT URLs

Mock types (mocks/vscode.ts)

  • Added ExternalUriOpener and ExternalUriOpenerMetadata interfaces
  • Added registerExternalUriOpener to window mock
  • Added Uri.toString() for URI handling in tests

Test coverage (src/extension.test.ts)

  • Registration verification
  • Priority 2 returned for http://localhost:3000/ and http://127.0.0.1:8080/review
  • Undefined returned for http://example.com (pass-through)

Architecture

Both modes now supported:

  1. Terminal mode (existing): PLANNOTATOR_BROWSER env var → router script → IPC server → webview
  2. Extension mode (new): vscode.env.openExternal() → external URI opener (matches localhost URLs) → webview

Cookie proxy and panel management shared across both paths.

Original prompt

This section details on the original issue you should resolve

<issue_title>Ensure extension works with Claude Code VSCode extension, not just terminal</issue_title>
<issue_description>## Summary

The extension should work seamlessly when Claude Code is used as a VSCode extension, not only when invoked from the terminal.

Problem

Currently, the extension may only be tested/working when Claude Code is run from the terminal. Users who use the Claude Code VSCode extension integration need the same functionality available within their IDE workflow.

Expected Behavior

  • Extension features should be fully functional when Claude Code is running as a VSCode extension
  • All hooks, commands, and interactions should work identically whether Claude Code is launched from the terminal or within VSCode

Acceptance Criteria

  • Verify extension activates correctly when Claude Code runs inside VSCode
  • Ensure all hook integrations work in the VSCode extension context
  • Test plan annotation workflows end-to-end in VSCode extension mode</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 12, 2026 08:15
- Implement registerExternalUriOpener to intercept vscode.env.openExternal() calls
- Add pattern matching for Plannotator URLs (URLs containing "plannotator")
- Update mock types to include ExternalUriOpener interfaces
- Add comprehensive tests for external URI opener functionality
- Update README with documentation for both terminal and extension modes
- Add package-lock.json to .gitignore

Co-authored-by: 7tg <20913374+7tg@users.noreply.github.com>
- Add detailed comments explaining URL matching strategy and priority value
- Restore globalThis.URL in mock for explicit scope reference
- Add security note about URL pattern matching scope

Co-authored-by: 7tg <20913374+7tg@users.noreply.github.com>
Copilot AI changed the title [WIP] Ensure extension works with Claude Code VSCode integration Add external URI opener for Claude Code VSCode extension support Feb 12, 2026
Copilot AI requested a review from 7tg February 12, 2026 08:19
src/extension.ts Outdated
// More specific matching (like hostname) isn't feasible since Plannotator can run
// on any localhost port, and the pattern must match both development and production URLs
// Note: While this could theoretically match unintended URLs (e.g., http://example.com?q=plannotator),
// in practice this is only triggered by vscode.env.openExternal() calls from other extensions
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot url is just localhost there is not plannator

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in 863de5b. Changed URL matching from checking for "plannotator" substring to matching localhost URLs specifically:

  • http://localhost:* and https://localhost:*
  • http://127.0.0.1:* and https://127.0.0.1:*

This correctly matches actual Plannotator URLs which run on localhost with dynamic ports.

Copilot AI and others added 2 commits February 12, 2026 09:14
Plannotator URLs don't contain "plannotator" in the path - they're just
localhost:PORT URLs. Changed matching logic to:
- Match http://localhost:* and https://localhost:*
- Match http://127.0.0.1:* and https://127.0.0.1:*
- Updated tests to verify localhost matching behavior

Co-authored-by: 7tg <20913374+7tg@users.noreply.github.com>
Co-authored-by: 7tg <20913374+7tg@users.noreply.github.com>
Copilot AI requested a review from 7tg February 12, 2026 09:17
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.

Ensure extension works with Claude Code VSCode extension, not just terminal

2 participants