feat(cli): refine dense output for ListDirectory and ReadManyFiles results

This commit is contained in:
Jarrod Whelan
2026-02-11 10:58:13 -08:00
parent 03de28960f
commit f03cfb075d
2 changed files with 46 additions and 34 deletions
@@ -229,33 +229,35 @@ describe('DenseToolMessage', () => {
it('renders correctly for ls results', () => { it('renders correctly for ls results', () => {
const lsResult = { const lsResult = {
summary: 'Listed 2 files', summary: 'Listed 2 files. (1 ignored)',
files: ['file1.ts', 'dir1'], files: ['file1.ts', 'dir1'],
}; };
const { lastFrame } = renderWithProviders( const { lastFrame } = renderWithProviders(
<DenseToolMessage {...defaultProps} resultDisplay={lsResult} />, <DenseToolMessage {...defaultProps} resultDisplay={lsResult} />,
); );
const output = lastFrame(); const output = lastFrame();
expect(output).toContain('→ Listed 2 files'); expect(output).toContain('→ Listed 2 files. (1 ignored)');
expect(output).toContain('file1.ts'); // Directory listings should not have a payload in dense mode
expect(output).toContain('dir1'); expect(output).not.toContain('file1.ts');
expect(output).not.toContain('dir1');
}); });
it('renders correctly for ReadManyFiles results', () => { it('renders correctly for ReadManyFiles results', () => {
const rmfResult = { const rmfResult = {
summary: 'Read 3 file(s)', summary: 'Read 3 file(s)',
files: ['file1.ts', 'file2.ts', 'file3.ts'], files: ['file1.ts', 'file2.ts', 'file3.ts'],
include: ['**/*.ts'],
skipped: [{ path: 'skipped.bin', reason: 'binary' }], skipped: [{ path: 'skipped.bin', reason: 'binary' }],
}; };
const { lastFrame } = renderWithProviders( const { lastFrame } = renderWithProviders(
<DenseToolMessage {...defaultProps} resultDisplay={rmfResult} />, <DenseToolMessage {...defaultProps} resultDisplay={rmfResult} />,
); );
const output = lastFrame(); const output = lastFrame();
expect(output).toContain('→ Read 3 file(s)'); expect(output).toContain('Attempting to read files from **/*.ts');
expect(output).toContain('→ Read 3 file(s) (1 ignored)');
expect(output).toContain('file1.ts'); expect(output).toContain('file1.ts');
expect(output).toContain('file2.ts'); expect(output).toContain('file2.ts');
expect(output).toContain('file3.ts'); expect(output).toContain('file3.ts');
expect(output).toContain('(1 skipped)');
}); });
it('renders correctly for todo updates', () => { it('renders correctly for todo updates', () => {
@@ -144,44 +144,30 @@ function getFileOpData(
return { description, summary, payload }; return { description, summary, payload };
} }
function getListResultData( function getReadManyFilesData(result: ReadManyFilesResult): ViewParts {
result: ListDirectoryResult | ReadManyFilesResult, const items = result.files ?? [];
toolName: string,
originalDescription?: string,
): ViewParts {
let description = originalDescription;
const items: string[] = result.files ?? [];
const maxVisible = 10; const maxVisible = 10;
const includePatterns = result.include?.join(', ') ?? '';
const description = `Attempting to read files from ${includePatterns}`;
// Enhance with ReadManyFiles specific data if present const skippedCount = result.skipped?.length ?? 0;
const rmf = result as ReadManyFilesResult; const summaryStr = `Read ${items.length} file(s)${
if (toolName === 'ReadManyFiles' && rmf.include) { skippedCount > 0 ? ` (${skippedCount} ignored)` : ''
const includePatterns = rmf.include.join(', '); }`;
description = `Attempting to read files from ${includePatterns}`; const summary = <Text color={theme.text.accent}> {summaryStr}</Text>;
result.summary = `Read ${items.length} file(s)`;
}
const summary = <Text color={theme.text.accent}> {result.summary}</Text>;
const skippedCount = rmf.skipped?.length ?? 0;
const skippedText =
skippedCount > 0 ? `(${skippedCount} skipped)` : undefined;
const excludedText = const excludedText =
rmf.excludes && rmf.excludes.length > 0 result.excludes && result.excludes.length > 0
? `Excluded patterns: ${rmf.excludes.slice(0, 3).join(', ')}${rmf.excludes.length > 3 ? '...' : ''}` ? `Excluded patterns: ${result.excludes.slice(0, 3).join(', ')}${
result.excludes.length > 3 ? '...' : ''
}`
: undefined; : undefined;
const hasItems = items.length > 0; const hasItems = items.length > 0;
const payload = const payload =
hasItems || skippedText || excludedText ? ( hasItems || excludedText ? (
<Box flexDirection="column" marginLeft={2}> <Box flexDirection="column" marginLeft={2}>
{hasItems && <RenderItemsList items={items} maxVisible={maxVisible} />} {hasItems && <RenderItemsList items={items} maxVisible={maxVisible} />}
{skippedText && (
<Text color={theme.text.secondary} dimColor>
{skippedText}
</Text>
)}
{excludedText && ( {excludedText && (
<Text color={theme.text.secondary} dimColor> <Text color={theme.text.secondary} dimColor>
{excludedText} {excludedText}
@@ -193,6 +179,30 @@ function getListResultData(
return { description, summary, payload }; return { description, summary, payload };
} }
function getListDirectoryData(
result: ListDirectoryResult,
originalDescription?: string,
): ViewParts {
const summary = <Text color={theme.text.accent}> {result.summary}</Text>;
// For directory listings, we want NO payload in dense mode as per request
return { description: originalDescription, summary, payload: undefined };
}
function getListResultData(
result: ListDirectoryResult | ReadManyFilesResult,
toolName: string,
originalDescription?: string,
): ViewParts {
// Use 'include' to determine if this is a ReadManyFilesResult
if ('include' in result) {
return getReadManyFilesData(result);
}
return getListDirectoryData(
result as ListDirectoryResult,
originalDescription,
);
}
function getGenericSuccessData( function getGenericSuccessData(
resultDisplay: unknown, resultDisplay: unknown,
originalDescription?: string, originalDescription?: string,