Skip to content

Conversation

roomote[bot]
Copy link

@roomote roomote bot commented Oct 17, 2025

Description

This PR implements the ability to provide custom denial messages when commands are auto-denied, allowing users to provide helpful alternatives and guidance. For example, users can suggest "Use 'uv run python' instead" when denying the system-wide 'python' command.

Related Issue

Fixes #8703

Changes

  • Type definitions: Updated in global settings to support both string arrays (backward compatible) and object arrays with optional custom messages
  • UI Enhancement: Added a second input field in AutoApproveSettings for custom messages alongside denied commands
  • Chat Interface: Modified ChatView to display custom messages when available, falling back to default localized messages
  • Backend: Updated ClineProvider and webviewMessageHandler to handle the new format while maintaining backward compatibility
  • Localization: Added translation keys for the new UI elements

Features

  • ✅ Backward compatible - existing string arrays continue to work
  • ✅ Intuitive UI with card-based layout for denied commands
  • ✅ Custom messages displayed in chat when commands are denied
  • ✅ Falls back to default messages when no custom message provided
  • ✅ Proper input validation and sanitization
  • ✅ TypeScript type safety throughout

Testing

  • All existing tests pass
  • Manually tested with various scenarios:
    • Adding commands with custom messages
    • Adding commands without custom messages
    • Removing commands
    • Backward compatibility with existing string arrays

Screenshots

The new UI allows users to optionally add custom messages:

  • Command input field (required)
  • Custom message input field (optional)
  • Card-based display showing command and custom message if present

Important

Add custom denial messages for auto-denied commands, enhancing user guidance while maintaining backward compatibility.

  • Behavior:
    • Custom denial messages for auto-denied commands added, allowing suggestions like "Use 'uv run python' instead".
    • Backward compatibility maintained with existing string arrays.
  • UI Enhancements:
    • Added input fields for custom messages in AutoApproveSettings.
    • ChatView displays custom messages when commands are denied.
  • Backend:
    • Updated ClineProvider and webviewMessageHandler to handle new command format.
  • Localization:
    • Added translation keys for new UI elements in settings.json.

This description was created by Ellipsis for 9ade5a3. You can customize this summary. It will automatically update as commits are pushed.

- Update type definitions to support both string and object format for denied commands
- Add UI for entering custom messages when denying commands
- Use custom messages in ChatView when auto-denying commands
- Add translations for new UI elements

This allows users to provide specific guidance when denying commands,
such as suggesting 'uv run python' when denying direct 'python' commands.

Fixes #8703
@roomote roomote bot requested review from cte, jr and mrubens as code owners October 17, 2025 16:30
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Oct 17, 2025
Copy link
Author

roomote bot commented Oct 17, 2025

Review Summary

I've reviewed the PR and found the following issues that should be addressed:

  • Critical: Lost workspace configuration merging - The mergeDeniedCommands method no longer merges workspace-level denied commands with global state, breaking documented behavior where both sources should be combined
  • Code duplication - The getDeniedCommand function reimplements longest prefix matching logic that already exists in findLongestPrefixMatch utility
  • Type safety concern - The type checking in mergeDeniedCommands assumes if the first array element is an object, all elements are objects, which is unsafe for mixed-type arrays

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Oct 17, 2025
className="grow"
data-testid="denied-command-input"
placeholder={t("settings:autoApprove.execute.customMessagePlaceholder", {
defaultValue: "Custom message (optional, e.g., 'Use uv run python instead')",
Copy link

Choose a reason for hiding this comment

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

Remove the inline fallback English string (defaultValue) from the translation call for customMessagePlaceholder. The system automatically falls back to the English JSON if a translation is missing.

Suggested change
defaultValue: "Custom message (optional, e.g., 'Use uv run python instead')",

This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.

<Input
value={deniedCommandInput}
onChange={(e: any) => setDeniedCommandInput(e.target.value)}
onKeyDown={(e: any) => {
Copy link

Choose a reason for hiding this comment

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

Consider using proper event types instead of any in onKeyDown handlers for better type safety.

Suggested change
onKeyDown={(e: any) => {
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {

Comment on lines +1679 to 1694
private mergeDeniedCommands(
globalStateCommands?: string[] | { command: string; message?: string }[],
): { command: string; message?: string }[] {
// Handle both string[] and object[] formats
if (!globalStateCommands) {
return []
}

// If it's already in the new format, return as-is
if (globalStateCommands.length > 0 && typeof globalStateCommands[0] === "object") {
return globalStateCommands as { command: string; message?: string }[]
}

// If it's in the old string format, convert to new format for compatibility
return (globalStateCommands as string[]).map((cmd) => ({ command: cmd }))
}
Copy link
Author

Choose a reason for hiding this comment

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

The new mergeDeniedCommands implementation no longer merges workspace-level denied commands with global-level ones. The original implementation delegated to mergeCommandLists which retrieved workspace configuration via vscode.workspace.getConfiguration(Package.name).get<string[]>(configKey) and merged it with global state. Now it only returns the converted global state commands without fetching workspace config. This breaks the documented merging behavior where workspace and global configurations are combined. The mergeAllowedCommands method on line 1671 still uses mergeCommandLists and correctly merges both sources. The fix should either call mergeCommandLists first or replicate its logic to fetch and merge workspace config.

Comment on lines +1064 to +1070

for (const denied of normalizedDenied) {
if (cmd.toLowerCase().startsWith(denied.command.toLowerCase())) {
if (denied.command.length > longestLength) {
longestMatch = denied
longestLength = denied.command.length
}
Copy link
Author

Choose a reason for hiding this comment

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

The getDeniedCommand function reimplements the longest prefix matching logic that already exists in findLongestPrefixMatch from the command-validation utility (which was removed from imports on line 29). This creates code duplication and maintenance burden. The manual loop on lines 1064-1070 duplicates the exact logic of findLongestPrefixMatch. Instead, this function should re-add findLongestPrefixMatch to imports and use it to find the matching command prefix, then look up the corresponding object from deniedCommands to get the custom message. This would reduce duplication and ensure consistent matching behavior.

Comment on lines +1687 to +1689
// If it's already in the new format, return as-is
if (globalStateCommands.length > 0 && typeof globalStateCommands[0] === "object") {
return globalStateCommands as { command: string; message?: string }[]
Copy link
Author

Choose a reason for hiding this comment

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

The type checking logic assumes that if the first element is an object, all elements are objects (line 1688). This is unsafe for arrays with mixed types. If globalStateCommands contains a mix like ["cmd1", {command: "cmd2"}], this code would incorrectly cast the entire array as objects, causing runtime errors when accessing .command on string elements. While the zod schema should prevent mixed arrays, defensive coding would handle this gracefully. Consider checking each element individually or using Array.every() to verify all elements match the expected type before casting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

[ENHANCEMENT] Custom command denied rejection prompts (or at least one)

2 participants