From da608b8853cc7282aba84fd1d0618cb6225f8997 Mon Sep 17 00:00:00 2001 From: Lukas Kmoch Date: Sat, 21 Feb 2026 00:24:20 +0100 Subject: [PATCH] Add permission scope toggle (this project / all projects) Add toggleable scope selector to the "always allow" permission button, matching the native Claude Code CLI behavior. When the CLI provides permission suggestions with scope info, users can click to switch between "this project (just you)" and "all projects". Suggestions are filtered by destination before being sent back to the CLI. Co-Authored-By: Claude Opus 4.6 --- src/extension.ts | 51 +++++++++++++++++++++++++++++----- src/script.ts | 72 +++++++++++++++++++++++++++++++++++++----------- src/ui-styles.ts | 24 +++++++++++++++- 3 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 6d62328..91571d6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -351,7 +351,7 @@ class ClaudeChatProvider { this._createImageFile(message.imageData, message.imageType); return; case 'permissionResponse': - this._handlePermissionResponse(message.id, message.approved, message.alwaysAllow); + this._handlePermissionResponse(message.id, message.approved, message.alwaysAllow, message.scope); return; case 'getPermissions': this._sendPermissions(); @@ -1500,6 +1500,7 @@ class ClaudeChatProvider { const toolUseId = request.tool_use_id; console.log(`Permission request for tool: ${toolName}, requestId: ${requestId}`); + console.log('Permission suggestions:', JSON.stringify(suggestions)); // Check if this tool is pre-approved const isPreApproved = await this._isToolPreApproved(toolName, input); @@ -1532,6 +1533,20 @@ class ClaudeChatProvider { pattern = this.getCommandPattern(input.command as string); } + // Compute scope availability from suggestions + let hasProjectScope = false; + let hasUserScope = false; + if (Array.isArray(suggestions)) { + for (const suggestion of suggestions) { + if (suggestion.destination === 'projectSettings' || suggestion.destination === 'localSettings') { + hasProjectScope = true; + } + if (suggestion.destination === 'userSettings') { + hasUserScope = true; + } + } + } + // Send permission request to the UI with pending status this._sendAndSaveMessage({ type: 'permissionRequest', @@ -1543,7 +1558,9 @@ class ClaudeChatProvider { suggestions: suggestions, decisionReason: request.decision_reason, blockedPath: request.blocked_path, - status: 'pending' + status: 'pending', + hasProjectScope: hasProjectScope, + hasUserScope: hasUserScope } }); } @@ -1561,13 +1578,33 @@ class ClaudeChatProvider { suggestions?: any[]; toolUseId: string; }, - alwaysAllow?: boolean + alwaysAllow?: boolean, + scope?: string ): void { if (!this._currentClaudeProcess?.stdin || this._currentClaudeProcess.stdin.destroyed) { console.error('Cannot send permission response: stdin not available'); return; } + // Filter suggestions by scope if specified + let filteredSuggestions = pendingRequest.suggestions; + if (alwaysAllow && Array.isArray(pendingRequest.suggestions) && scope) { + let scopeFiltered: any[]; + if (scope === 'project') { + scopeFiltered = pendingRequest.suggestions.filter( + (s: any) => s.destination === 'projectSettings' || s.destination === 'localSettings' + ); + } else if (scope === 'allProjects') { + scopeFiltered = pendingRequest.suggestions.filter( + (s: any) => s.destination === 'userSettings' + ); + } else { + scopeFiltered = pendingRequest.suggestions; + } + // Fall back to all suggestions if filtering yields empty array + filteredSuggestions = scopeFiltered.length > 0 ? scopeFiltered : pendingRequest.suggestions; + } + let response: any; if (approved) { response = { @@ -1578,8 +1615,8 @@ class ClaudeChatProvider { response: { behavior: 'allow', updatedInput: pendingRequest.input, - // Pass back suggestions if user chose "always allow" - updatedPermissions: alwaysAllow ? pendingRequest.suggestions : undefined, + // Pass back filtered suggestions if user chose "always allow" + updatedPermissions: alwaysAllow ? filteredSuggestions : undefined, toolUseID: pendingRequest.toolUseId } } @@ -1615,7 +1652,7 @@ class ClaudeChatProvider { * Handle permission response from webview UI * Sends control_response back to Claude CLI via stdin */ - private _handlePermissionResponse(id: string, approved: boolean, alwaysAllow?: boolean): void { + private _handlePermissionResponse(id: string, approved: boolean, alwaysAllow?: boolean, scope?: string): void { const pendingRequest = this._pendingPermissionRequests.get(id); if (!pendingRequest) { console.error('No pending permission request found for id:', id); @@ -1626,7 +1663,7 @@ class ClaudeChatProvider { this._pendingPermissionRequests.delete(id); // Send the response to Claude via stdin - this._sendPermissionResponse(id, approved, pendingRequest, alwaysAllow); + this._sendPermissionResponse(id, approved, pendingRequest, alwaysAllow, scope); // Update the permission request status in UI this._postMessage({ diff --git a/src/script.ts b/src/script.ts index 7029415..1264f7e 100644 --- a/src/script.ts +++ b/src/script.ts @@ -17,6 +17,7 @@ const getScript = (isTelemetryEnabled: boolean) => `