Skip to content

Conversation

@zamalali
Copy link

@zamalali zamalali commented Oct 29, 2025

Problem

@workspace is limited to opened folders. Full-stack and multiple repo developers cannot reference external code (e.g. backend, shared utils) without workspace reloads or manual #file drags.


Solution

  • Command: github.copilot.chat.addExternalContext QuickPick + folder browse
  • Hard cap: 3 external folders
  • Injection: As ChatPromptReference (vscode.prompt.file.external)
  • Tools: listDir, edit, etc. access external paths via isExternalPath
  • Exclusions: node_modules, .git, dist, build, .log, .tmp + files.exclude
  • UX:
    • Status bar badge: $(folder) 2/3
    • github.copilot.chat.manageExternalContexts remove via QuickPick
    • Warnings on cap/exclusion
  • Env: EXTERNAL_CONTEXT_PATHS (fallback: SYSTEM_CONTEXT_PATHS)

No impact on existing @workspace or open files.


Implementation

  • ExternalContextService: path management, dedup, cap, events
  • ExternalContextContribution: commands, UI, badge, filtering
  • Integrated into chatParticipantRequestHandler, listDirTool, toolUtils

Testing

  • External paths appear in prompt references
  • Cap enforced at 3 with warning
  • Excluded paths skipped + toast
  • Tools read/write external files
  • vitest suite: add/cap/remove/events

Limitations

  • No context chips chat input UI is closed-source in workbench.
    Status bar + manage command provide full visibility and control.
  • Max 3 is intentional; future configurability possible.

Ready for review and merge.
All functionality delivered within open-source constraints.

Implement github.copilot.chat.addSystemContext and systemContextService. Add related contributions and i18n updates (package.json, package.nls.json). Add helper tools/utilities used by the feature (listDirTool, toolUtils) and wire into prompt/request handler. Note: To surface this choice in the Copilot Chat 'Add Context' QuickPick, a follow-up UI change is required in the chat/workbench UI.
…rvice

Implements github.copilot.chat.addExternalContext command, refactors associated contributions and services, enforces 3-folder cap, and adds dedicated test coverage. Safe sync commit — local testing pending.
…ogic

Aligns naming, command wiring, and prompt/tool references with ExternalContext design. Enforces 3-folder cap via IExternalContextService, adds exclusion guardrails and status-bar badge. Includes package and i18n updates.
Copilot AI review requested due to automatic review settings October 29, 2025 12:52
@zamalali
Copy link
Author

@microsoft-github-policy-service agree

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds external context support to GitHub Copilot Chat, allowing users to add folders outside their workspace that Copilot can access. Users can manage up to 3 external folders via commands and a status bar indicator, with these paths automatically included as chat references.

Key changes:

  • Implements ExternalContextService to manage external folder paths with events and path comparison logic
  • Adds UI contribution with commands for adding/managing external folders and a status bar item
  • Integrates external paths into tool validation (toolUtils, listDirTool) and chat references (chatParticipantRequestHandler)

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/extension/context/node/externalContextService.ts Core service managing external paths with events, max limit enforcement, and path comparison logic
src/extension/context/vscode-node/externalContextContribution.ts UI contribution providing commands, status bar item, and folder picker with exclusion rules
src/extension/context/node/test/externalContextService.spec.ts Unit tests for external context service covering max paths, events, and removal
src/extension/tools/node/toolUtils.ts Updates file validation to allow external paths in tools
src/extension/tools/node/listDirTool.tsx Updates directory listing tool to allow external paths
src/extension/prompt/node/chatParticipantRequestHandler.ts Injects external paths as chat references for each request
src/extension/extension/vscode-node/services.ts Registers external context service in DI container
src/extension/extension/vscode-node/contributions.ts Registers external context contribution
package.nls.json Adds localization strings for new commands
package.json Adds command definitions for external context management

replaceExternalPaths(paths: readonly URI[]): void {
this._paths.clear();
for (const path of paths) {
this._paths.set(path.toString(), path);
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The replaceExternalPaths method does not enforce the MAX_EXTERNAL_PATHS limit, allowing more paths to be set than the maximum. This is inconsistent with addExternalPaths which respects the limit. Add a check to only add paths up to MAX_EXTERNAL_PATHS.

Suggested change
this._paths.set(path.toString(), path);
if (this._paths.size >= MAX_EXTERNAL_PATHS) {
break;
}
const key = path.toString();
if (!this._paths.has(key)) {
this._paths.set(key, path);
}

Copilot uses AI. Check for mistakes.
Comment on lines 115 to 118
if (potentialParent === child) {
return true;
}

Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The equality check at lines 115-117 is redundant since the same comparison is already performed by the caller at line 98 (candidateComparable === storedComparable). Remove this check to avoid duplicated logic.

Suggested change
if (potentialParent === child) {
return true;
}

Copilot uses AI. Check for mistakes.
this.request = await this.sanitizeVariables();
this.turn.request.message = this.request.prompt;


Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Extra blank line at line 228 should be removed to maintain consistent spacing according to the project's coding style.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines 293 to 314
private appendExternalContextReferences(): void {
const externalUris = this._externalContextService.getExternalPaths();
if (!externalUris.length) {
return;
}

const existingRefs = this.request.references ?? [];

const newRefs: ChatPromptReference[] = [];
let counter = 0;
for (const uri of externalUris) {
const alreadyPresent = existingRefs.some(ref => this.matchesReference(ref, uri)) || newRefs.some(ref => this.matchesReference(ref, uri));
if (!alreadyPresent) {
const id = `${EXTERNAL_CONTEXT_REFERENCE_PREFIX}.${counter++}`;
newRefs.push({
id,
name: uri.fsPath,
value: uri,
modelDescription: `External context path ${uri.fsPath}`,
});
}
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The nested loop at line 304 creates O(n*m) complexity checking if URIs are already present. The second check newRefs.some(ref => this.matchesReference(ref, uri)) is unnecessary since newRefs is being built in this loop and cannot contain duplicates from externalUris. Consider using a Set-based approach or remove the redundant newRefs.some() check.

Copilot uses AI. Check for mistakes.
@zamalali zamalali requested a review from Copilot October 29, 2025 13:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comment on lines +99 to +113
isExternalPath(uri: URI): boolean {
const candidateComparable = this.toComparablePath(uri);
for (const stored of this._paths.values()) {
const storedComparable = this.toComparablePath(stored);

if (candidateComparable === storedComparable) {
return true;
}

if (this.isSubPath(candidateComparable, storedComparable) || this.isSubPath(storedComparable, candidateComparable)) {
return true;
}
}
return false;
}
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The toComparablePath method is called repeatedly inside the loop, converting the same stored paths on every iteration. Consider caching the comparable paths in a Map alongside the URIs to avoid redundant path normalization operations.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants