Skip to content

Commit 6ab9bc7

Browse files
wip
1 parent 92fd313 commit 6ab9bc7

25 files changed

+433
-607
lines changed

packages/web/src/features/chat/agent.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ const createAgentStream = async ({
224224
onWriteSource({
225225
type: 'file',
226226
language: output.metadata.language,
227-
repo: output.metadata.repository,
227+
repo: output.metadata.repo,
228228
path: output.metadata.path,
229229
revision: output.metadata.revision,
230230
name: output.metadata.path.split('/').pop() ?? output.metadata.path,
@@ -234,7 +234,7 @@ const createAgentStream = async ({
234234
onWriteSource({
235235
type: 'file',
236236
language: file.language,
237-
repo: file.repository,
237+
repo: file.repo,
238238
path: file.fileName,
239239
revision: file.revision,
240240
name: file.fileName.split('/').pop() ?? file.fileName,
@@ -245,7 +245,7 @@ const createAgentStream = async ({
245245
onWriteSource({
246246
type: 'file',
247247
language: file.language,
248-
repo: file.repository,
248+
repo: file.repo,
249249
path: file.fileName,
250250
revision: file.revision,
251251
name: file.fileName.split('/').pop() ?? file.fileName,
@@ -308,7 +308,8 @@ const createPrompt = ({
308308
The user has explicitly selected the following repositories for analysis:
309309
${repos.map(repo => `- ${repo}`).join('\n')}
310310
311-
When calling the searchCode tool, always pass these repositories as \`filterByRepos\` to scope results to the selected repositories.
311+
When calling tools that accept a \`repo\` parameter (e.g. \`read_file\`, \`list_commits\`, \`list_tree\`), use these repository names directly.
312+
When calling the \`search_code\` tool, pass these repositories as \`filterByRepos\` to scope results to the selected repositories.
312313
</selected_repositories>
313314
` : ''}
314315

packages/web/src/features/chat/components/chatThread/detailsCard.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ReadFileToolComponent } from './tools/readFileToolComponent';
1616
import { SearchCodeToolComponent } from './tools/searchCodeToolComponent';
1717
import { ListReposToolComponent } from './tools/listReposToolComponent';
1818
import { ListCommitsToolComponent } from './tools/listCommitsToolComponent';
19+
import { ListTreeToolComponent } from './tools/listTreeToolComponent';
1920
import { SBChatMessageMetadata, SBChatMessagePart } from '../../types';
2021
import { SearchScopeIcon } from '../searchScopeIcon';
2122
import isEqual from "fast-deep-equal/react";
@@ -166,48 +167,55 @@ const DetailsCardComponent = ({
166167
className="text-sm"
167168
/>
168169
)
169-
case 'tool-readFile':
170+
case 'tool-read_file':
170171
return (
171172
<ReadFileToolComponent
172173
key={index}
173174
part={part}
174175
/>
175176
)
176-
case 'tool-searchCode':
177+
case 'tool-search_code':
177178
return (
178179
<SearchCodeToolComponent
179180
key={index}
180181
part={part}
181182
/>
182183
)
183-
case 'tool-findSymbolDefinitions':
184+
case 'tool-find_symbol_definitions':
184185
return (
185186
<FindSymbolDefinitionsToolComponent
186187
key={index}
187188
part={part}
188189
/>
189190
)
190-
case 'tool-findSymbolReferences':
191+
case 'tool-find_symbol_references':
191192
return (
192193
<FindSymbolReferencesToolComponent
193194
key={index}
194195
part={part}
195196
/>
196197
)
197-
case 'tool-listRepos':
198+
case 'tool-list_repos':
198199
return (
199200
<ListReposToolComponent
200201
key={index}
201202
part={part}
202203
/>
203204
)
204-
case 'tool-listCommits':
205+
case 'tool-list_commits':
205206
return (
206207
<ListCommitsToolComponent
207208
key={index}
208209
part={part}
209210
/>
210211
)
212+
case 'tool-list_tree':
213+
return (
214+
<ListTreeToolComponent
215+
key={index}
216+
part={part}
217+
/>
218+
)
211219
case 'data-source':
212220
case 'dynamic-tool':
213221
case 'file':

packages/web/src/features/chat/components/chatThread/tools/findSymbolDefinitionsToolComponent.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export const FindSymbolDefinitionsToolComponent = ({ part }: { part: FindSymbolD
2525
}
2626
}, [part]);
2727

28+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
29+
? () => { navigator.clipboard.writeText(part.output.output); return true; }
30+
: undefined;
31+
2832
return (
2933
<div className="my-4">
3034
<ToolHeader
@@ -34,6 +38,7 @@ export const FindSymbolDefinitionsToolComponent = ({ part }: { part: FindSymbolD
3438
label={label}
3539
Icon={BookOpenIcon}
3640
onExpand={setIsExpanded}
41+
onCopy={onCopy}
3742
/>
3843
{part.state === 'output-available' && isExpanded && (
3944
<>
@@ -52,7 +57,7 @@ export const FindSymbolDefinitionsToolComponent = ({ part }: { part: FindSymbolD
5257
<FileListItem
5358
key={file.fileName}
5459
path={file.fileName}
55-
repoName={file.repository}
60+
repoName={file.repo}
5661
/>
5762
)
5863
})}

packages/web/src/features/chat/components/chatThread/tools/findSymbolReferencesToolComponent.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export const FindSymbolReferencesToolComponent = ({ part }: { part: FindSymbolRe
2525
}
2626
}, [part]);
2727

28+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
29+
? () => { navigator.clipboard.writeText(part.output.output); return true; }
30+
: undefined;
31+
2832
return (
2933
<div className="my-4">
3034
<ToolHeader
@@ -34,6 +38,7 @@ export const FindSymbolReferencesToolComponent = ({ part }: { part: FindSymbolRe
3438
label={label}
3539
Icon={BookOpenIcon}
3640
onExpand={setIsExpanded}
41+
onCopy={onCopy}
3742
/>
3843
{part.state === 'output-available' && isExpanded && (
3944
<>
@@ -52,7 +57,7 @@ export const FindSymbolReferencesToolComponent = ({ part }: { part: FindSymbolRe
5257
<FileListItem
5358
key={file.fileName}
5459
path={file.fileName}
55-
repoName={file.repository}
60+
repoName={file.repo}
5661
/>
5762
)
5863
})}

packages/web/src/features/chat/components/chatThread/tools/listCommitsToolComponent.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export const ListCommitsToolComponent = ({ part }: { part: ListCommitsToolUIPart
2323
}
2424
}, [part]);
2525

26+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
27+
? () => { navigator.clipboard.writeText(part.output.output); return true; }
28+
: undefined;
29+
2630
return (
2731
<div className="my-4">
2832
<ToolHeader
@@ -32,6 +36,7 @@ export const ListCommitsToolComponent = ({ part }: { part: ListCommitsToolUIPart
3236
label={label}
3337
Icon={GitCommitVerticalIcon}
3438
onExpand={setIsExpanded}
39+
onCopy={onCopy}
3540
/>
3641
{part.state === 'output-available' && isExpanded && (
3742
<>

packages/web/src/features/chat/components/chatThread/tools/listReposToolComponent.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export const ListReposToolComponent = ({ part }: { part: ListReposToolUIPart })
2323
}
2424
}, [part]);
2525

26+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
27+
? () => { navigator.clipboard.writeText(part.output.output); return true; }
28+
: undefined;
29+
2630
return (
2731
<div className="my-4">
2832
<ToolHeader
@@ -32,6 +36,7 @@ export const ListReposToolComponent = ({ part }: { part: ListReposToolUIPart })
3236
label={label}
3337
Icon={FolderOpenIcon}
3438
onExpand={setIsExpanded}
39+
onCopy={onCopy}
3540
/>
3641
{part.state === 'output-available' && isExpanded && (
3742
<>
@@ -46,12 +51,12 @@ export const ListReposToolComponent = ({ part }: { part: ListReposToolUIPart })
4651
) : (
4752
<TreeList>
4853
<div className="text-sm text-muted-foreground mb-2">
49-
Found {part.output.metadata.repos.length} repositories:
54+
Found {part.output.metadata.repos.length} of {part.output.metadata.totalCount} repositories:
5055
</div>
51-
{part.output.metadata.repos.map((repoName, index) => (
56+
{part.output.metadata.repos.map((repo, index) => (
5257
<div key={index} className="flex items-center gap-2 text-sm">
5358
<FolderOpenIcon className="h-4 w-4 text-muted-foreground" />
54-
<span className="truncate">{repoName}</span>
59+
<span className="truncate">{repo.name}</span>
5560
</div>
5661
))}
5762
</TreeList>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
'use client';
2+
3+
import { ListTreeToolUIPart } from "@/features/chat/tools";
4+
import { isServiceError } from "@/lib/utils";
5+
import { useMemo, useState } from "react";
6+
import { ToolHeader, TreeList } from "./shared";
7+
import { CodeSnippet } from "@/app/components/codeSnippet";
8+
import { Separator } from "@/components/ui/separator";
9+
import { FileIcon, FolderIcon } from "lucide-react";
10+
11+
export const ListTreeToolComponent = ({ part }: { part: ListTreeToolUIPart }) => {
12+
const [isExpanded, setIsExpanded] = useState(false);
13+
14+
const label = useMemo(() => {
15+
switch (part.state) {
16+
case 'input-streaming':
17+
return 'Listing directory tree...';
18+
case 'output-error':
19+
return '"List tree" tool call failed';
20+
case 'input-available':
21+
case 'output-available':
22+
return 'Listed directory tree';
23+
}
24+
}, [part]);
25+
26+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
27+
? () => { navigator.clipboard.writeText(part.output.output); return true; }
28+
: undefined;
29+
30+
return (
31+
<div className="my-4">
32+
<ToolHeader
33+
isLoading={part.state !== 'output-available' && part.state !== 'output-error'}
34+
isError={part.state === 'output-error' || (part.state === 'output-available' && isServiceError(part.output))}
35+
isExpanded={isExpanded}
36+
label={label}
37+
Icon={FolderIcon}
38+
onExpand={setIsExpanded}
39+
onCopy={onCopy}
40+
/>
41+
{part.state === 'output-available' && isExpanded && (
42+
<>
43+
{isServiceError(part.output) ? (
44+
<TreeList>
45+
<span>Failed with the following error: <CodeSnippet className="text-sm text-destructive">{part.output.message}</CodeSnippet></span>
46+
</TreeList>
47+
) : (
48+
<>
49+
{part.output.metadata.entries.length === 0 ? (
50+
<span className="text-sm text-muted-foreground ml-[25px]">No entries found</span>
51+
) : (
52+
<TreeList>
53+
<div className="text-sm text-muted-foreground mb-2">
54+
{part.output.metadata.repo} - {part.output.metadata.path || '/'} ({part.output.metadata.totalReturned} entries{part.output.metadata.truncated ? ', truncated' : ''})
55+
</div>
56+
{part.output.metadata.entries.map((entry, index) => (
57+
<div key={index} className="flex items-center gap-2 text-sm" style={{ paddingLeft: `${(entry.depth - 1) * 12}px` }}>
58+
{entry.type === 'tree'
59+
? <FolderIcon className="h-4 w-4 text-muted-foreground flex-shrink-0" />
60+
: <FileIcon className="h-4 w-4 text-muted-foreground flex-shrink-0" />
61+
}
62+
<span className="truncate">{entry.name}</span>
63+
</div>
64+
))}
65+
</TreeList>
66+
)}
67+
</>
68+
)}
69+
<Separator className='ml-[7px] my-2' />
70+
</>
71+
)}
72+
</div>
73+
);
74+
};

packages/web/src/features/chat/components/chatThread/tools/readFileToolComponent.tsx

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,14 @@ import { ReadFileToolUIPart } from "@/features/chat/tools";
66
import { isServiceError } from "@/lib/utils";
77
import { EyeIcon } from "lucide-react";
88
import { useMemo, useState } from "react";
9-
import { CopyIconButton } from "@/app/[domain]/components/copyIconButton";
109
import { FileListItem, ToolHeader, TreeList } from "./shared";
1110

1211
export const ReadFileToolComponent = ({ part }: { part: ReadFileToolUIPart }) => {
1312
const [isExpanded, setIsExpanded] = useState(false);
1413

15-
const onCopy = () => {
16-
if (part.state !== 'output-available' || isServiceError(part.output)) return false;
17-
navigator.clipboard.writeText(part.output.output);
18-
return true;
19-
};
14+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
15+
? () => { navigator.clipboard.writeText((part.output as { output: string }).output); return true; }
16+
: undefined;
2017

2118
const label = useMemo(() => {
2219
switch (part.state) {
@@ -39,23 +36,15 @@ export const ReadFileToolComponent = ({ part }: { part: ReadFileToolUIPart }) =>
3936

4037
return (
4138
<div className="my-4">
42-
<div className="flex items-center gap-2 group/header">
43-
<ToolHeader
44-
isLoading={part.state !== 'output-available' && part.state !== 'output-error'}
45-
isError={part.state === 'output-error' || (part.state === 'output-available' && isServiceError(part.output))}
46-
isExpanded={isExpanded}
47-
label={label}
48-
Icon={EyeIcon}
49-
onExpand={setIsExpanded}
50-
className="flex-1"
51-
/>
52-
{part.state === 'output-available' && !isServiceError(part.output) && (
53-
<CopyIconButton
54-
onCopy={onCopy}
55-
className="opacity-0 group-hover/header:opacity-100 transition-opacity"
56-
/>
57-
)}
58-
</div>
39+
<ToolHeader
40+
isLoading={part.state !== 'output-available' && part.state !== 'output-error'}
41+
isError={part.state === 'output-error' || (part.state === 'output-available' && isServiceError(part.output))}
42+
isExpanded={isExpanded}
43+
label={label}
44+
Icon={EyeIcon}
45+
onExpand={setIsExpanded}
46+
onCopy={onCopy}
47+
/>
5948
{part.state === 'output-available' && isExpanded && (
6049
<>
6150
<TreeList>
@@ -64,7 +53,7 @@ export const ReadFileToolComponent = ({ part }: { part: ReadFileToolUIPart }) =>
6453
) : (
6554
<FileListItem
6655
path={part.output.metadata.path}
67-
repoName={part.output.metadata.repository}
56+
repoName={part.output.metadata.repo}
6857
/>
6958
)}
7059
</TreeList>

packages/web/src/features/chat/components/chatThread/tools/searchCodeToolComponent.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export const SearchCodeToolComponent = ({ part }: { part: SearchCodeToolUIPart }
3131
}
3232
}, [part, displayQuery]);
3333

34+
const onCopy = part.state === 'output-available' && !isServiceError(part.output)
35+
? () => { navigator.clipboard.writeText(part.output.output); return true; }
36+
: undefined;
37+
3438
return (
3539
<div className="my-4">
3640
<ToolHeader
@@ -40,6 +44,7 @@ export const SearchCodeToolComponent = ({ part }: { part: SearchCodeToolUIPart }
4044
label={label}
4145
Icon={SearchIcon}
4246
onExpand={setIsExpanded}
47+
onCopy={onCopy}
4348
/>
4449
{part.state === 'output-available' && isExpanded && (
4550
<>
@@ -58,7 +63,7 @@ export const SearchCodeToolComponent = ({ part }: { part: SearchCodeToolUIPart }
5863
<FileListItem
5964
key={file.fileName}
6065
path={file.fileName}
61-
repoName={file.repository}
66+
repoName={file.repo}
6267
/>
6368
)
6469
})}

0 commit comments

Comments
 (0)