From e78c3fe4f0beb60406dead714a2f07f86c1523d1 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Fri, 2 Jan 2026 15:01:31 -0800 Subject: [PATCH] Agent Skills: Status Bar Integration for Skill Counts (#15741) --- .../cli/src/ui/components/Composer.test.tsx | 14 +++++++---- packages/cli/src/ui/components/Composer.tsx | 1 + .../components/ContextSummaryDisplay.test.tsx | 15 +++++++----- .../ui/components/ContextSummaryDisplay.tsx | 23 +++++++++++++------ 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/ui/components/Composer.test.tsx b/packages/cli/src/ui/components/Composer.test.tsx index 181ac31cc6..ef97c56201 100644 --- a/packages/cli/src/ui/components/Composer.test.tsx +++ b/packages/cli/src/ui/components/Composer.test.tsx @@ -144,10 +144,16 @@ const createMockConfig = (overrides = {}) => ({ getDebugMode: vi.fn(() => false), getAccessibility: vi.fn(() => ({})), getMcpServers: vi.fn(() => ({})), - getMcpClientManager: vi.fn().mockImplementation(() => ({ - getBlockedMcpServers: vi.fn(), - getMcpServers: vi.fn(), - })), + getToolRegistry: () => ({ + getTool: vi.fn(), + }), + getSkillManager: () => ({ + getSkills: () => [], + }), + getMcpClientManager: () => ({ + getMcpServers: () => ({}), + getBlockedMcpServers: () => [], + }), ...overrides, }); diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 15a2b45599..11685a4435 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -120,6 +120,7 @@ export const Composer = () => { blockedMcpServers={ config.getMcpClientManager()?.getBlockedMcpServers() ?? [] } + skillCount={config.getSkillManager().getSkills().length} /> ) )} diff --git a/packages/cli/src/ui/components/ContextSummaryDisplay.test.tsx b/packages/cli/src/ui/components/ContextSummaryDisplay.test.tsx index a61f57aa92..50a610f345 100644 --- a/packages/cli/src/ui/components/ContextSummaryDisplay.test.tsx +++ b/packages/cli/src/ui/components/ContextSummaryDisplay.test.tsx @@ -34,14 +34,16 @@ describe('', () => { openFiles: [{ path: '/a/b/c', timestamp: Date.now() }], }, }, + skillCount: 1, }; it('should render on a single line on a wide screen', () => { const { lastFrame, unmount } = renderWithWidth(120, baseProps); const output = lastFrame()!; expect(output).toContain( - 'Using: 1 open file (ctrl+g to view) | 1 GEMINI.md file | 1 MCP server', + '1 open file (ctrl+g to view) | 1 GEMINI.md file | 1 MCP server | 1 skill', ); + expect(output).not.toContain('Using:'); // Check for absence of newlines expect(output.includes('\n')).toBe(false); unmount(); @@ -51,10 +53,10 @@ describe('', () => { const { lastFrame, unmount } = renderWithWidth(60, baseProps); const output = lastFrame()!; const expectedLines = [ - ' Using:', - ' - 1 open file (ctrl+g to view)', - ' - 1 GEMINI.md file', - ' - 1 MCP server', + ' - 1 open file (ctrl+g to view)', + ' - 1 GEMINI.md file', + ' - 1 MCP server', + ' - 1 skill', ]; const actualLines = output.split('\n'); expect(actualLines).toEqual(expectedLines); @@ -86,9 +88,10 @@ describe('', () => { geminiMdFileCount: 0, contextFileNames: [], mcpServers: {}, + skillCount: 0, }; const { lastFrame, unmount } = renderWithWidth(60, props); - const expectedLines = [' Using:', ' - 1 open file (ctrl+g to view)']; + const expectedLines = [' - 1 open file (ctrl+g to view)']; const actualLines = lastFrame()!.split('\n'); expect(actualLines).toEqual(expectedLines); unmount(); diff --git a/packages/cli/src/ui/components/ContextSummaryDisplay.tsx b/packages/cli/src/ui/components/ContextSummaryDisplay.tsx index 5072fa4b84..39476765d4 100644 --- a/packages/cli/src/ui/components/ContextSummaryDisplay.tsx +++ b/packages/cli/src/ui/components/ContextSummaryDisplay.tsx @@ -17,6 +17,7 @@ interface ContextSummaryDisplayProps { mcpServers?: Record; blockedMcpServers?: Array<{ name: string; extensionName: string }>; ideContext?: IdeContext; + skillCount: number; } export const ContextSummaryDisplay: React.FC = ({ @@ -25,6 +26,7 @@ export const ContextSummaryDisplay: React.FC = ({ mcpServers, blockedMcpServers, ideContext, + skillCount, }) => { const { columns: terminalWidth } = useTerminalSize(); const isNarrow = isNarrowWidth(terminalWidth); @@ -36,7 +38,8 @@ export const ContextSummaryDisplay: React.FC = ({ geminiMdFileCount === 0 && mcpServerCount === 0 && blockedMcpServerCount === 0 && - openFileCount === 0 + openFileCount === 0 && + skillCount === 0 ) { return ; // Render an empty space to reserve height } @@ -83,15 +86,23 @@ export const ContextSummaryDisplay: React.FC = ({ return parts.join(', '); })(); - const summaryParts = [openFilesText, geminiMdText, mcpText].filter(Boolean); + const skillText = (() => { + if (skillCount === 0) { + return ''; + } + return `${skillCount} skill${skillCount > 1 ? 's' : ''}`; + })(); + + const summaryParts = [openFilesText, geminiMdText, mcpText, skillText].filter( + Boolean, + ); if (isNarrow) { return ( - Using: {summaryParts.map((part, index) => ( - {' '}- {part} + - {part} ))} @@ -100,9 +111,7 @@ export const ContextSummaryDisplay: React.FC = ({ return ( - - Using: {summaryParts.join(' | ')} - + {summaryParts.join(' | ')} ); };