Skip to content

Allow users to manage terminal/shell command permissions directly from the chat interface #5480

@hannesrudolph

Description

@hannesrudolph

Terminal/Shell Command Permissions UI Feature

Overview

This feature allows users to manage terminal/shell command permissions directly from the chat interface when Roo executes terminal commands.

Feature Description

The command permissions feature adds an interactive UI component to command execution blocks that allows users to:

  • View suggested command patterns for the allow list
  • Add patterns to the allowed commands list (for auto-execution)
  • Add patterns to the denied commands list (to block execution)
  • Toggle patterns between allowed/denied/none states
  • View pattern descriptions that explain what each pattern will allow

UI Components

1. CommandExecution Component

Location: webview-ui/src/components/chat/CommandExecution.tsx

This is the main component that displays command execution blocks in the chat interface.

Key Features:

  • Displays the command being executed
  • Shows execution status (running/exited with exit code)
  • Collapsible output section
  • Integrated command pattern management section

Component Structure:

<div className="w-full">
  {/* Header section */}
  <div className="flex flex-row items-center justify-between gap-2 px-3 py-2 bg-vscode-editor-background border border-vscode-border rounded-t-md">
    {/* Icon, title, status display, output toggle */}
  </div>

  {/* Command execution box */}
  <div className="bg-vscode-editor-background border-x border-b border-vscode-border rounded-b-md">
    {/* Command display */}
    <div className="p-3">
      <CodeBlock source={command} language="shell" />
    </div>

    {/* Command management section */}
    {showSuggestions && (
      <CommandPatternSelector
        patterns={commandPatterns}
        allowedCommands={allowedCommands}
        deniedCommands={deniedCommands}
        onAllowPatternChange={handleAllowPatternChange}
        onDenyPatternChange={handleDenyPatternChange}
      />
    )}

    {/* Output section */}
    {output.length > 0 && (
      <div className={cn("border-t border-vscode-panel-border", { hidden: !isOutputExpanded })}>
        <div className="p-3">
          <CodeBlock source={output} language="log" />
        </div>
      </div>
    )}
  </div>
</div>

2. CommandPatternSelector Component

Location: webview-ui/src/components/chat/CommandPatternSelector.tsx

This component handles the command pattern management UI.

Key Features:

  • Collapsible section with chevron indicator
  • Lists command patterns with descriptions
  • Allow/Deny toggle buttons for each pattern
  • Visual indicators for pattern status
  • Tooltip with help text and link to settings

Component Structure:

<div className="border-t border-vscode-panel-border bg-vscode-sideBar-background/30">
  <button
    onClick={() => setIsExpanded(!isExpanded)}
    className="flex items-center gap-2 w-full px-3 py-2 text-xs text-vscode-descriptionForeground hover:text-vscode-foreground hover:bg-vscode-list-hoverBackground transition-all"
  >
    <ChevronDown className={cn("size-3 transition-transform duration-200", {
      "rotate-0": isExpanded,
      "-rotate-90": !isExpanded,
    })} />
    <span className="font-medium">{t("chat:commandExecution.manageCommands")}</span>
    {/* Info tooltip when expanded */}
  </button>

  {isExpanded && (
    <div className="px-3 pb-3 space-y-2">
      {patterns.map((item, index) => (
        <div key={`${item.pattern}-${index}`} className="ml-5 flex items-center gap-2">
          <div className="flex-1">
            <span className="font-mono text-xs text-vscode-foreground">{item.pattern}</span>
            {item.description && (
              <span className="text-xs text-vscode-descriptionForeground ml-2">
                - {item.description}
              </span>
            )}
          </div>
          <div className="flex items-center gap-1">
            {/* Allow button */}
            <button className={cn("p-1 rounded transition-all", /* status-based styles */)}>
              <Check className="size-3.5" />
            </button>
            {/* Deny button */}
            <button className={cn("p-1 rounded transition-all", /* status-based styles */)}>
              <X className="size-3.5" />
            </button>
          </div>
        </div>
      ))}
    </div>
  )}
</div>

Styling

Color Scheme

The UI uses VSCode theme variables for consistent appearance:

  • Background Colors:

    • Main container: bg-vscode-editor-background
    • Pattern selector section: bg-vscode-sideBar-background/30
    • Hover states: hover:bg-vscode-list-hoverBackground, hover:bg-vscode-toolbar-hoverBackground
  • Text Colors:

    • Primary text: text-vscode-foreground
    • Secondary text: text-vscode-descriptionForeground
    • Muted text: text-vscode-descriptionForeground/70
  • Border Colors:

    • Main borders: border-vscode-border
    • Section dividers: border-vscode-panel-border
  • Status Indicators:

    • Running: bg-lime-400 (green dot)
    • Success (exit 0): bg-lime-400
    • Error (non-zero exit): bg-red-400
    • Allowed pattern: bg-green-500/20 text-green-500
    • Denied pattern: bg-red-500/20 text-red-500

Button States

  • Default: text-vscode-descriptionForeground
  • Hover (Allow): hover:text-green-500 hover:bg-green-500/10
  • Hover (Deny): hover:text-red-500 hover:bg-red-500/10
  • Active (Allow): bg-green-500/20 text-green-500 hover:bg-green-500/30
  • Active (Deny): bg-red-500/20 text-red-500 hover:bg-red-500/30

Pattern Extraction Logic

Command Pattern Extraction

Location: webview-ui/src/utils/commandPatterns.ts

The system extracts command patterns using two methods:

  1. LLM Suggestions: If the AI provides suggestions in the command text

    • Format: <suggestions>["pattern1", "pattern2"]</suggestions>
    • Alternative: <suggest>pattern1</suggest><suggest>pattern2</suggest>
  2. Programmatic Extraction: If no suggestions provided, patterns are extracted automatically

    • Base command (e.g., "git" from "git push origin main")
    • Command + subcommand (e.g., "git push")
    • Special handling for package managers (npm, yarn, pnpm, bun)
    • Stops at flags, paths, or complex arguments

Pattern Descriptions

The system generates human-readable descriptions for patterns:

  • npm run → "all npm run scripts"
  • git push → "git push commands"
  • ./script.sh → "this specific script"
  • python → "python scripts"
  • cd → "directory navigation"

State Management

Extension State Integration

The component integrates with the extension state to:

  • Read current allowedCommands and deniedCommands arrays
  • Update lists via setAllowedCommands and setDeniedCommands
  • Send updates to VSCode via postMessage

Pattern Status Logic

const getPatternStatus = (pattern: string): "allowed" | "denied" | "none" => {
  if (allowedCommands.includes(pattern)) return "allowed"
  if (deniedCommands.includes(pattern)) return "denied"
  return "none"
}

Mutual Exclusivity

When a pattern is added to one list, it's automatically removed from the other:

  • Adding to allowed → removes from denied
  • Adding to denied → removes from allowed

Internationalization

Translation Keys

Location: webview-ui/src/i18n/locales/en/chat.json

{
  "commandExecution": {
    "running": "Running",
    "pid": "PID: {{pid}}",
    "exited": "Exited ({{exitCode}})",
    "manageCommands": "Manage Command Permissions",
    "commandManagementDescription": "Manage command permissions: Click ✓ to allow auto-execution, ✗ to deny execution. Patterns can be toggled on/off or removed from lists. <settingsLink>View all settings</settingsLink>",
    "addToAllowed": "Add to allowed list",
    "removeFromAllowed": "Remove from allowed list",
    "addToDenied": "Add to denied list",
    "removeFromDenied": "Remove from denied list",
    "abortCommand": "Abort command execution",
    "expandOutput": "Expand output",
    "collapseOutput": "Collapse output",
    "expandManagement": "Expand command management section",
    "collapseManagement": "Collapse command management section"
  }
}

Implementation Details

Command Parsing

Location: webview-ui/src/utils/commandUtils.ts

The parseCommandAndOutput function extracts:

  1. The command string
  2. Output (if present after "Output:" marker)
  3. Suggestions from <suggestions> or <suggest> tags

Event Handling

  • Pattern allow/deny changes trigger state updates and VSCode messages
  • Collapsible sections use local state for expand/collapse
  • Status updates received via message events from the extension

Message Format

When updating command lists:

vscode.postMessage({
  type: "allowedCommands", // or "deniedCommands"
  commands: updatedCommandArray
})

Usage Example

When Roo executes a command like npm install express, the UI will:

  1. Display the command in a code block
  2. Show "Manage Command Permissions" section (collapsed by default)
  3. When expanded, show patterns:
    • npm - npm commands
    • npm install - npm install commands
  4. User can click ✓ to allow or ✗ to deny each pattern
  5. Changes are immediately reflected in the UI and saved to extension state

Accessibility

  • All interactive elements have proper aria-label attributes
  • Expand/collapse states use aria-expanded
  • Tooltips provide additional context
  • Keyboard navigation supported
  • Visual indicators complement color coding

Testing Considerations

  1. Pattern Extraction: Test with various command formats
  2. State Synchronization: Verify updates persist across sessions
  3. UI Interactions: Test expand/collapse, button clicks, tooltips
  4. Edge Cases: Empty patterns, malformed suggestions, special characters
  5. Theme Compatibility: Verify appearance in different VSCode themes

Parsing Mechanism Implementation

Overview

The new implementation uses the shell-quote library to properly parse shell commands and extract command patterns. This approach handles complex shell syntax including pipes, command chaining, and special operators.

Parser Implementation

Location: webview-ui/src/utils/commandPatterns.ts (proposed)

import { parse } from 'shell-quote';

function extractPatterns(cmdStr) {
    const patterns = new Set();
    
    const parsed = parse(cmdStr);
    
    const commandSeparators = new Set(['|', '&&', '||', ';']);
    let current = [];
    for (const token of parsed) {
        if (typeof token === 'object' && token.op && commandSeparators.has(token.op)) {
            if (current.length) processCommand(current, patterns);
            current = [];
        } else {
            current.push(token);
        }
    }

    if (current.length) processCommand(current, patterns);
        
    return patterns;
}

Filter/ProcessCommand Function

The processCommand function extracts patterns from individual commands by progressively building patterns from the command and its arguments:

function processCommand(cmd, patterns) {
    if (!cmd.length || typeof cmd[0] !== 'string') return;
    
    const mainCmd = cmd[0];
    patterns.add(mainCmd);
    
    const breakingExps = [ /^-/, /[\\/:.~ ]/, ];
    
    for (let i = 1; i < cmd.length; i++) {
        const arg = cmd[i];

        if (typeof arg !== 'string' || breakingExps.some(re => re.test(arg))) break;
        
        const pattern = cmd.slice(0, i + 1).join(' ');
        patterns.add(pattern);
    }
}

Breaking Rules

The parser stops building patterns when it encounters:

  • Flags (arguments starting with -)
  • Path separators (/, \)
  • Special characters (:, ., ~, space)

Display Logic

To display sorted command patterns from shell history:

const exampleCommands = [ 'git ... ' , ... ]
const allPatterns = new Set();
exampleCommands.forEach(cmd => {
    extractPatterns(cmd).forEach(pattern => allPatterns.add(pattern));
});

const sorted = Array.from(allPatterns).sort((a, b) => {
    return a.localeCompare(b);
});

console.log('All suggested command patterns for auto-allowing (sorted and unique):', sorted);

Example Outputs

When processing shell history, the parser generates a sorted list of unique command patterns:

[
  '../repackage.sh',
  '../roo-main/build.sh',
  '../roo-main/clean.sh',
  'cd',
  'code',
  'code-insiders',
  'fg',
  'git',
  'git add',
  'git checkout',
  'git cherry-pick',
  'git diff',
  'git diff HEAD',
  'git fetch',
  'git fetch github',
  'git fetch origin',
  'git log',
  'git push',
  'git push github',
  'git push github refactor-use-files-for-history',
  'git rebase',
  'git reset',
  'git restore',
  'git show',
  'git stash',
  'git stash pop',
  'git status',
  'grep',
  'grep map',
  'head',
  'npm',
  'npm run',
  'npm run start',
  'npx',
  'npx jest',
  'pnpm',
  'pnpm test',
  'top',
  'unzip',
  'vi',
  'xclip'
]

Example Inputs from Shell History

The following examples demonstrate the parser's ability to handle complex real-world commands from a developer's shell history:

# Complex piped commands
unzip -l builds/roo-cline-3.21.5-error-boundary-component.vsix|grep map
unzip -l builds/roo-cline-3.21.5-error-boundary-component.vsix|grep map|head

# Git commands with various arguments
git checkout -b use-safe-write-json-for-all-files
git checkout v3.16.5 .roomodes
git cherry-pick --continue
git diff HEAD
git diff pnpm-lock.yaml
git fetch github
git push github refactor-use-files-for-history -f
git rebase origin/main
git restore .roomodes
git restore --staged .roomodes
git show f65fb69fd~1 f65fb69fdfb60abb0968b658c320ae5adb1e3d8b

# Package manager commands
npx jest core/prompts/sections/__tests__/custom-instructions.test.ts
pnpm test
npm run start

# VSCode commands
code --install-extension builds/roo-cline-3.21.5-error-boundary-component.vsix
code-insiders .

# Shell built-ins and utilities
cd src/
cd webview-ui/
cd ..
cd -
fg
top
vi ../repackage.sh

# Script executions
../roo-main/clean.sh
../roo-main/build.sh
../repackage.sh

# Complex chained commands
../roo-main/build.sh && code --install-extension builds/roo-cline-3.21.5-error-boundary-component.vsix && code

Pattern Extraction Benefits

  1. Granular Control: Users can allow broad patterns (e.g., git) or specific subcommands (e.g., git push)
  2. Safety: The parser stops at flags and paths, preventing overly specific patterns
  3. Flexibility: Handles complex shell syntax including pipes, redirects, and command chaining
  4. Deduplication: Automatically removes duplicate patterns from history
  5. Sorting: Presents patterns in alphabetical order for easy scanning

Integration with UI

The extracted patterns are passed to the CommandPatternSelector component as:

interface CommandPattern {
  pattern: string;
  description?: string;
}

The UI then displays these patterns with appropriate descriptions and allow/deny toggle buttons, enabling users to quickly build their command permission lists based on their actual command usage history.

Metadata

Metadata

Assignees

Labels

Issue - In ProgressSomeone is actively working on this. Should link to a PR soon.enhancementNew feature or requestproposal

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions