/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { Box, Text } from 'ink'; import { theme } from '../../semantic-colors.js'; interface HooksListProps { hooks: ReadonlyArray<{ config: { command?: string; type: string; name?: string; description?: string; timeout?: number; }; source: string; eventName: string; matcher?: string; sequential?: boolean; enabled: boolean; }>; } export const HooksList: React.FC = ({ hooks }) => { if (hooks.length === 0) { return ( No hooks configured. ); } // Group hooks by event name for better organization const hooksByEvent = hooks.reduce( (acc, hook) => { if (!acc[hook.eventName]) { acc[hook.eventName] = []; } acc[hook.eventName].push(hook); return acc; }, {} as Record>, ); return ( ⚠️ Security Warning: Hooks can execute arbitrary commands on your system. Only use hooks from sources you trust. Review hook scripts carefully. Learn more:{' '} https://geminicli.com/docs/hooks Configured Hooks: {Object.entries(hooksByEvent).map(([eventName, eventHooks]) => ( {eventName}: {eventHooks.map((hook, index) => { const hookName = hook.config.name || hook.config.command || 'unknown'; const statusColor = hook.enabled ? theme.status.success : theme.text.secondary; const statusText = hook.enabled ? 'enabled' : 'disabled'; return ( {hookName} {` [${statusText}]`} {hook.config.description && ( {hook.config.description} )} Source: {hook.source} {hook.config.name && hook.config.command && ` | Command: ${hook.config.command}`} {hook.matcher && ` | Matcher: ${hook.matcher}`} {hook.sequential && ` | Sequential`} {hook.config.timeout && ` | Timeout: ${hook.config.timeout}s`} ); })} ))} Tip: Use /hooks enable {''} or{' '} /hooks disable {''} to toggle individual hooks. Use /hooks enable-all or{' '} /hooks disable-all to toggle all hooks at once. ); };