/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { Box, Text } from 'ink'; import { Colors } from '../colors.js'; import { formatDuration } from '../utils/formatters.js'; import { getStatusColor, TOOL_SUCCESS_RATE_HIGH, TOOL_SUCCESS_RATE_MEDIUM, USER_AGREEMENT_RATE_HIGH, USER_AGREEMENT_RATE_MEDIUM, } from '../utils/displayUtils.js'; import { useSessionStats } from '../contexts/SessionContext.js'; import type { ToolCallStats } from '@vybestack/llxprt-code-telemetry'; import { getBorderStyle } from '../contexts/UnicodeRenderingContext.js'; const TOOL_NAME_COL_WIDTH = 25; const CALLS_COL_WIDTH = 8; const SUCCESS_RATE_COL_WIDTH = 15; const AVG_DURATION_COL_WIDTH = 15; const StatRow: React.FC<{ name: string; stats: ToolCallStats; }> = ({ name, stats }) => { const successRate = stats.count > 0 ? (stats.success / stats.count) * 100 : 0; const avgDuration = stats.count > 0 ? stats.durationMs / stats.count : 0; const successColor = getStatusColor(successRate, { green: TOOL_SUCCESS_RATE_HIGH, yellow: TOOL_SUCCESS_RATE_MEDIUM, }); return ( {name} {stats.count} {successRate.toFixed(1)}% {formatDuration(avgDuration)} ); }; const ToolTableHeader: React.FC = () => ( Tool Name Calls Success Rate Avg Duration ); const TableDivider: React.FC = () => ( ); interface DecisionRowProps { label: string; value: number; color: string; } const DecisionRow: React.FC = ({ label, value, color }) => ( {label} {value} ); interface UserDecisionSummaryProps { totalReviewed: number; totalDecisions: { accept: number; reject: number; modify: number; }; agreementRate: number; agreementColor: string; } const UserDecisionSummary: React.FC = ({ totalReviewed, totalDecisions, agreementRate, agreementColor, }) => ( <> User Decision Summary Total Reviewed Suggestions: {totalReviewed} Overall Agreement Rate: 0 ? agreementColor : Colors.Foreground} > {totalReviewed > 0 ? `${agreementRate.toFixed(1)}%` : '--'} ); const EmptyToolStats: React.FC = () => ( No tool calls have been made in this session. ); interface ToolStatsContentProps { activeTools: Array<[string, ToolCallStats]>; totalDecisions: { accept: number; reject: number; modify: number }; } const ToolStatsContent: React.FC = ({ activeTools, totalDecisions, }) => { const totalReviewed = totalDecisions.accept + totalDecisions.reject + totalDecisions.modify; const agreementRate = totalReviewed > 0 ? (totalDecisions.accept / totalReviewed) * 100 : 0; const agreementColor = getStatusColor(agreementRate, { green: USER_AGREEMENT_RATE_HIGH, yellow: USER_AGREEMENT_RATE_MEDIUM, }); return ( Tool Stats {activeTools.map(([name, stats]) => ( ))} ); }; export const ToolStatsDisplay: React.FC = () => { const { stats } = useSessionStats(); const { tools } = stats.metrics; const activeTools = Object.entries(tools.byName).filter( ([, metrics]: [string, ToolCallStats]) => metrics.count > 0, ); if (activeTools.length === 0) { return ; } const totalDecisions = Object.values(tools.byName).reduce( ( acc: { accept: number; reject: number; modify: number }, tool: ToolCallStats, ) => { acc.accept += tool.decisions.accept; acc.reject += tool.decisions.reject; acc.modify += tool.decisions.modify; return acc; }, { accept: 0, reject: 0, modify: 0 }, ); return ( ); };