/**
* @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 (
);
};