From 1ba3807df5c7e04a330bf97f11296c9a750dbb56 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 22 Jul 2026 16:40:42 +0300 Subject: [PATCH] fix: hide duplicated bash permission pattern Removes permission patterns already shown as the rendered bash command Keeps distinct patterns visible in the permission card Adds tests for the pattern filtering behavior --- .../src/components/chat/PermissionCard.test.ts | 17 +++++++++++++++++ .../ui/src/components/chat/PermissionCard.tsx | 17 +++++++++++------ .../components/chat/permissionCardPatterns.ts | 4 ++++ 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 packages/ui/src/components/chat/PermissionCard.test.ts create mode 100644 packages/ui/src/components/chat/permissionCardPatterns.ts diff --git a/packages/ui/src/components/chat/PermissionCard.test.ts b/packages/ui/src/components/chat/PermissionCard.test.ts new file mode 100644 index 00000000..1db671f7 --- /dev/null +++ b/packages/ui/src/components/chat/PermissionCard.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, test } from 'bun:test'; + +import { getVisiblePermissionPatterns } from './permissionCardPatterns'; + +describe('getVisiblePermissionPatterns', () => { + test('omits a pattern already rendered as the bash command', () => { + const command = 'bunx eslint "src/components/session/SessionSidebar.tsx"'; + + expect(getVisiblePermissionPatterns([command], command)).toEqual([]); + }); + + test('preserves distinct permission patterns', () => { + const command = 'bunx eslint "src/components/session/SessionSidebar.tsx"'; + + expect(getVisiblePermissionPatterns(['bunx eslint *', command], command)).toEqual(['bunx eslint *']); + }); +}); diff --git a/packages/ui/src/components/chat/PermissionCard.tsx b/packages/ui/src/components/chat/PermissionCard.tsx index 77061a3d..ae243290 100644 --- a/packages/ui/src/components/chat/PermissionCard.tsx +++ b/packages/ui/src/components/chat/PermissionCard.tsx @@ -9,6 +9,7 @@ import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { Icon } from "@/components/icon/Icon"; import { DiffPreview, WritePreview } from './DiffPreview'; import { useI18n } from '@/lib/i18n'; +import { getVisiblePermissionPatterns } from './permissionCardPatterns'; const PERMISSION_BASH_CUSTOM_STYLE: React.CSSProperties = { margin: 0, @@ -123,6 +124,7 @@ export const PermissionCard: React.FC = ({ const toolName = permission.permission || 'unknown'; const tool = toolName.toLowerCase(); + const isBashTool = tool === 'bash' || tool === 'shell' || tool === 'shell_command'; const getMeta = (key: string, fallback: string = ''): string => { const val = permission.metadata[key]; @@ -137,11 +139,14 @@ export const PermissionCard: React.FC = ({ return Boolean(val); }; const displayToolName = getToolDisplayName(toolName); + const bashCommand = isBashTool + ? getMeta('command') || getMeta('cmd') || getMeta('script') + : ''; + const visiblePatterns = getVisiblePermissionPatterns(permission.patterns, bashCommand); const renderToolContent = () => { - if (tool === 'bash' || tool === 'shell' || tool === 'shell_command') { - const command = getMeta('command') || getMeta('cmd') || getMeta('script'); + if (isBashTool) { const description = getMeta('description'); const workingDir = getMeta('cwd') || getMeta('working_directory') || getMeta('directory') || getMeta('path'); const timeout = getMetaNum('timeout'); @@ -162,11 +167,11 @@ export const PermissionCard: React.FC = ({ )} {} - {command && ( + {bashCommand && (
= ({ {}
- {permission.patterns.length > 0 && ( + {visiblePatterns.length > 0 && (
{t('chat.permissionCard.patterns')}
- {permission.patterns.join(", ")} + {visiblePatterns.join(", ")}
)} diff --git a/packages/ui/src/components/chat/permissionCardPatterns.ts b/packages/ui/src/components/chat/permissionCardPatterns.ts new file mode 100644 index 00000000..4bd22af1 --- /dev/null +++ b/packages/ui/src/components/chat/permissionCardPatterns.ts @@ -0,0 +1,4 @@ +export const getVisiblePermissionPatterns = (patterns: string[], renderedCommand: string): string[] => { + if (!renderedCommand) return patterns; + return patterns.filter((pattern) => pattern !== renderedCommand); +};