From 1fa94b9c54bce8345623eb0301cecdbc31e98423 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Wed, 9 Jul 2025 00:19:14 +0100 Subject: [PATCH] Ability to add permission --- src/extension.ts | 68 +++++++++++++++++++++++++ src/ui-styles.ts | 128 +++++++++++++++++++++++++++++++++++++++++++++++ src/ui.ts | 102 +++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index 2be1ea1..618db91 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -280,6 +280,9 @@ class ClaudeChatProvider { case 'removePermission': this._removePermission(message.toolName, message.command); return; + case 'addPermission': + this._addPermission(message.toolName, message.command); + return; } } @@ -1376,6 +1379,71 @@ class ClaudeChatProvider { } } + private async _addPermission(toolName: string, command: string | null): Promise { + try { + const storagePath = this._context.storageUri?.fsPath; + if (!storagePath) return; + + const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json')); + let permissions: any = { alwaysAllow: {} }; + + try { + const content = await vscode.workspace.fs.readFile(permissionsUri); + permissions = JSON.parse(new TextDecoder().decode(content)); + } catch { + // File doesn't exist, use default permissions + } + + // Add the new permission + if (command === null || command === '') { + // Allow all commands for this tool + permissions.alwaysAllow[toolName] = true; + } else { + // Add specific command pattern + if (!permissions.alwaysAllow[toolName]) { + permissions.alwaysAllow[toolName] = []; + } + + // Convert to array if it's currently set to true + if (permissions.alwaysAllow[toolName] === true) { + permissions.alwaysAllow[toolName] = []; + } + + if (Array.isArray(permissions.alwaysAllow[toolName])) { + // For Bash commands, convert to pattern using existing logic + let commandToAdd = command; + if (toolName === 'Bash') { + commandToAdd = this.getCommandPattern(command); + } + + // Add if not already present + if (!permissions.alwaysAllow[toolName].includes(commandToAdd)) { + permissions.alwaysAllow[toolName].push(commandToAdd); + } + } + } + + // Ensure permissions directory exists + const permissionsDir = vscode.Uri.file(path.dirname(permissionsUri.fsPath)); + try { + await vscode.workspace.fs.stat(permissionsDir); + } catch { + await vscode.workspace.fs.createDirectory(permissionsDir); + } + + // Save updated permissions + const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2)); + await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent); + + // Send updated permissions to UI + this._sendPermissions(); + + console.log(`Added permission for ${toolName}${command ? ` command: ${command}` : ' (all commands)'}`); + } catch (error) { + console.error('Error adding permission:', error); + } + } + public getMCPConfigPath(): string | undefined { const storagePath = this._context.storageUri?.fsPath; if (!storagePath) {return undefined;} diff --git a/src/ui-styles.ts b/src/ui-styles.ts index 0339012..2108512 100644 --- a/src/ui-styles.ts +++ b/src/ui-styles.ts @@ -360,6 +360,134 @@ const styles = ` opacity: 0.5; } + /* Add Permission Form */ + .permissions-add-section { + margin-top: 6px; + } + + .permissions-show-add-btn { + background-color: transparent; + color: var(--vscode-descriptionForeground); + border: none; + border-radius: 3px; + padding: 6px 8px; + font-size: 11px; + cursor: pointer; + transition: all 0.2s ease; + font-weight: 400; + opacity: 0.7; + } + + .permissions-show-add-btn:hover { + background-color: var(--vscode-list-hoverBackground); + opacity: 1; + } + + .permissions-add-form { + margin-top: 8px; + padding: 12px; + border: 1px solid var(--vscode-panel-border); + border-radius: 6px; + background-color: var(--vscode-input-background); + animation: slideDown 0.2s ease; + } + + @keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + .permissions-form-row { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 8px; + } + + .permissions-tool-select { + background-color: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-panel-border); + border-radius: 3px; + padding: 4px 8px; + font-size: 12px; + min-width: 100px; + height: 24px; + flex-shrink: 0; + } + + .permissions-command-input { + background-color: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-panel-border); + border-radius: 3px; + padding: 4px 8px; + font-size: 12px; + flex-grow: 1; + height: 24px; + font-family: var(--vscode-editor-font-family); + } + + .permissions-command-input::placeholder { + color: var(--vscode-input-placeholderForeground); + } + + .permissions-add-btn { + background-color: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + border-radius: 3px; + padding: 4px 12px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s ease; + height: 24px; + font-weight: 500; + flex-shrink: 0; + } + + .permissions-add-btn:hover { + background-color: var(--vscode-button-hoverBackground); + } + + .permissions-add-btn:disabled { + background-color: var(--vscode-button-secondaryBackground); + color: var(--vscode-button-secondaryForeground); + cursor: not-allowed; + opacity: 0.5; + } + + .permissions-cancel-btn { + background-color: transparent; + color: var(--vscode-foreground); + border: 1px solid var(--vscode-panel-border); + border-radius: 3px; + padding: 4px 12px; + font-size: 12px; + cursor: pointer; + transition: all 0.2s ease; + height: 24px; + font-weight: 500; + } + + .permissions-cancel-btn:hover { + background-color: var(--vscode-list-hoverBackground); + border-color: var(--vscode-focusBorder); + } + + .permissions-form-hint { + font-size: 11px; + color: var(--vscode-descriptionForeground); + font-style: italic; + line-height: 1.3; + } + /* WSL Alert */ .wsl-alert { margin: 8px 12px; diff --git a/src/ui.ts b/src/ui.ts index aed512c..b8603be 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -260,6 +260,35 @@ const html = ` Loading permissions... +
+ + +

MCP Configuration (coming soon)

@@ -2539,6 +2568,79 @@ const html = ` command: command }); } + + function showAddPermissionForm() { + document.getElementById('showAddPermissionBtn').style.display = 'none'; + document.getElementById('addPermissionForm').style.display = 'block'; + + // Focus on the tool select dropdown + setTimeout(() => { + document.getElementById('addPermissionTool').focus(); + }, 100); + } + + function hideAddPermissionForm() { + document.getElementById('showAddPermissionBtn').style.display = 'flex'; + document.getElementById('addPermissionForm').style.display = 'none'; + + // Clear form inputs + document.getElementById('addPermissionTool').value = ''; + document.getElementById('addPermissionCommand').value = ''; + document.getElementById('addPermissionCommand').style.display = 'none'; + } + + function toggleCommandInput() { + const toolSelect = document.getElementById('addPermissionTool'); + const commandInput = document.getElementById('addPermissionCommand'); + const hintDiv = document.getElementById('permissionsFormHint'); + + if (toolSelect.value === 'Bash') { + commandInput.style.display = 'block'; + hintDiv.textContent = 'Use patterns like "npm i *" or "git add *" for specific commands.'; + } else if (toolSelect.value === '') { + commandInput.style.display = 'none'; + commandInput.value = ''; + hintDiv.textContent = 'Select a tool to add always-allow permission.'; + } else { + commandInput.style.display = 'none'; + commandInput.value = ''; + hintDiv.textContent = 'This will allow all ' + toolSelect.value + ' commands without asking for permission.'; + } + } + + function addPermission() { + const toolSelect = document.getElementById('addPermissionTool'); + const commandInput = document.getElementById('addPermissionCommand'); + const addBtn = document.getElementById('addPermissionBtn'); + + const toolName = toolSelect.value.trim(); + const command = commandInput.value.trim(); + + if (!toolName) { + return; + } + + // Disable button during processing + addBtn.disabled = true; + addBtn.textContent = 'Adding...'; + + vscode.postMessage({ + type: 'addPermission', + toolName: toolName, + command: command || null + }); + + // Clear form and hide it + toolSelect.value = ''; + commandInput.value = ''; + hideAddPermissionForm(); + + // Re-enable button + setTimeout(() => { + addBtn.disabled = false; + addBtn.textContent = 'Add'; + }, 500); + } // Close settings modal when clicking outside document.getElementById('settingsModal').addEventListener('click', (e) => {