Skip to content

Commit daea565

Browse files
[feat][render preview] prettier agent store (#300)
Co-authored-by: Codebuff <noreply@codebuff.com>
1 parent 41846b7 commit daea565

File tree

8 files changed

+336
-259
lines changed

8 files changed

+336
-259
lines changed

bun.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

npm-app/src/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,21 @@ For all commands and options, run 'codebuff' and then type 'help'.
240240
)
241241
const initialInput = isCommand ? '' : filteredArgs.join(' ')
242242

243+
// Handle --agent flag by prefilling user input instead of directly invoking
244+
let finalInitialInput = initialInput
245+
if (options.agent && !initialInput) {
246+
finalInitialInput = `@${options.agent}`
247+
} else if (options.agent && initialInput) {
248+
finalInitialInput = `@${options.agent} ${initialInput}`
249+
}
250+
243251
codebuff({
244-
initialInput,
252+
initialInput: finalInitialInput,
245253
git,
246254
costMode,
247255
runInitFlow: options.init,
248256
model: options.model,
249-
agent: options.agent,
257+
agent: undefined, // Don't pass agent to CLI - use prefilled input instead
250258
params: parsedAgentParams,
251259
print: options.print,
252260
cwd: options.cwd,

web/src/app/publishers/[id]/agents/[agentId]/[version]/agent-usage-metrics.tsx

Lines changed: 58 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { useQuery } from '@tanstack/react-query'
44
import { TrendingUp, Users, DollarSign, Play, Calendar } from 'lucide-react'
5-
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
65
import { Skeleton } from '@/components/ui/skeleton'
76

87
interface AgentUsageMetricsProps {
@@ -89,21 +88,14 @@ export const AgentUsageMetrics = ({
8988

9089
if (isLoading) {
9190
return (
92-
<Card className="mb-6">
93-
<CardHeader>
94-
<CardTitle className="text-lg">Usage Metrics</CardTitle>
95-
</CardHeader>
96-
<CardContent>
97-
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
98-
{Array.from({ length: 4 }).map((_, i) => (
99-
<div key={i} className="flex flex-col items-center gap-2">
100-
<Skeleton className="h-6 w-16" />
101-
<Skeleton className="h-4 w-20" />
102-
</div>
103-
))}
91+
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
92+
{Array.from({ length: 4 }).map((_, i) => (
93+
<div key={i} className="flex flex-col items-center gap-2">
94+
<Skeleton className="h-6 w-16" />
95+
<Skeleton className="h-4 w-20" />
10496
</div>
105-
</CardContent>
106-
</Card>
97+
))}
98+
</div>
10799
)
108100
}
109101

@@ -112,68 +104,63 @@ export const AgentUsageMetrics = ({
112104
}
113105

114106
return (
115-
<Card className="mb-6">
116-
<CardHeader>
117-
<CardTitle className="text-lg">Usage Metrics</CardTitle>
118-
</CardHeader>
119-
<CardContent>
120-
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
121-
<div className="flex flex-col items-center gap-2">
122-
<div className="flex items-center gap-2">
123-
<TrendingUp className="h-4 w-4 text-emerald-400" />
124-
<span className="font-medium text-emerald-300">
125-
{formatCurrency(usageMetrics.weekly_spent)}
126-
</span>
127-
</div>
128-
<span className="text-xs text-muted-foreground">Weekly Usage</span>
107+
<div>
108+
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
109+
<div className="flex flex-col items-center gap-2">
110+
<div className="flex items-center gap-2">
111+
<TrendingUp className="h-4 w-4 text-emerald-400" />
112+
<span className="font-medium text-emerald-300">
113+
{formatCurrency(usageMetrics.weekly_spent)}
114+
</span>
129115
</div>
130-
<div className="flex flex-col items-center gap-2">
131-
<div className="flex items-center gap-2">
132-
<Play className="h-4 w-4 text-muted-foreground" />
133-
<span>{formatUsageCount(usageMetrics.usage_count)}</span>
134-
</div>
135-
<span className="text-xs text-muted-foreground">Total Runs</span>
116+
<span className="text-xs text-muted-foreground">Weekly Usage</span>
117+
</div>
118+
<div className="flex flex-col items-center gap-2">
119+
<div className="flex items-center gap-2">
120+
<Play className="h-4 w-4 text-muted-foreground" />
121+
<span>{formatUsageCount(usageMetrics.usage_count)}</span>
136122
</div>
137-
<div className="flex flex-col items-center gap-2">
138-
<div className="flex items-center gap-2">
139-
<Users className="h-4 w-4 text-muted-foreground" />
140-
<span>{usageMetrics.unique_users || 0}</span>
141-
</div>
142-
<span className="text-xs text-muted-foreground">Unique Users</span>
123+
<span className="text-xs text-muted-foreground">Total Runs</span>
124+
</div>
125+
<div className="flex flex-col items-center gap-2">
126+
<div className="flex items-center gap-2">
127+
<Users className="h-4 w-4 text-muted-foreground" />
128+
<span>{usageMetrics.unique_users || 0}</span>
143129
</div>
144-
<div className="flex flex-col items-center gap-2">
145-
<div className="flex items-center gap-2">
146-
<DollarSign className="h-4 w-4 text-muted-foreground" />
147-
<span>
148-
{formatCurrency(usageMetrics.avg_cost_per_invocation).replace(
149-
'$',
150-
''
151-
)}
152-
</span>
153-
</div>
154-
<span className="text-xs text-muted-foreground">
155-
Avg Cost per Run
130+
<span className="text-xs text-muted-foreground">Unique Users</span>
131+
</div>
132+
<div className="flex flex-col items-center gap-2">
133+
<div className="flex items-center gap-2">
134+
<DollarSign className="h-4 w-4 text-muted-foreground" />
135+
<span>
136+
{formatCurrency(usageMetrics.avg_cost_per_invocation).replace(
137+
'$',
138+
''
139+
)}
156140
</span>
157141
</div>
142+
<span className="text-xs text-muted-foreground">
143+
Avg Cost per Run
144+
</span>
158145
</div>
159-
{usageMetrics.last_used && (
160-
<div className="mt-4 pt-4 border-t border-border/40">
161-
<div className="flex items-center gap-2 text-sm text-muted-foreground">
162-
<Calendar className="h-4 w-4" />
163-
<span>
164-
Last used:{' '}
165-
{new Date(usageMetrics.last_used).toLocaleDateString('en-US', {
166-
year: 'numeric',
167-
month: 'short',
168-
day: 'numeric',
169-
hour: '2-digit',
170-
minute: '2-digit',
171-
})}
172-
</span>
173-
</div>
146+
</div>
147+
{usageMetrics.last_used && (
148+
<div className="mt-4 pt-4 border-t border-border/40">
149+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
150+
<Calendar className="h-4 w-4" />
151+
<span>
152+
Last used:{' '}
153+
{new Date(usageMetrics.last_used).toLocaleDateString('en-US', {
154+
year: 'numeric',
155+
month: 'short',
156+
day: 'numeric',
157+
hour: '2-digit',
158+
minute: '2-digit',
159+
})}
160+
</span>
174161
</div>
175-
)}
176-
</CardContent>
177-
</Card>
162+
</div>
163+
)}
164+
</div>
178165
)
179166
}

web/src/app/publishers/[id]/agents/[agentId]/[version]/page.tsx

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { AgentUsageMetrics } from './agent-usage-metrics'
1616
import { RunAgentButton } from './run-agent-button'
1717
import { CopyIdButton } from './copy-id-button'
1818
import { SaveAgentButton } from './save-agent-button'
19+
import { VersionUsageBadge } from './version-usage-badge'
1920
import { Button } from '@/components/ui/button'
2021

2122
interface AgentDetailPageProps {
@@ -200,12 +201,6 @@ const AgentDetailPage = async ({ params }: AgentDetailPageProps) => {
200201
</div>
201202
</CardHeader>
202203
</Card>
203-
{/* Usage Metrics */}
204-
<AgentUsageMetrics
205-
publisherId={params.id}
206-
agentId={params.agentId}
207-
version={params.version}
208-
/>
209204
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
210205
{/* Version Navigation */}
211206
<div className="lg:col-span-1">
@@ -236,9 +231,18 @@ const AgentDetailPage = async ({ params }: AgentDetailPageProps) => {
236231
className="w-full justify-start group transition-colors"
237232
>
238233
<div className="flex items-center justify-between w-full">
239-
<span className="font-mono">
240-
v{version.version}
241-
</span>
234+
<div className="flex items-center">
235+
<span className="font-mono">
236+
v{version.version}
237+
</span>
238+
{index !== 0 && (
239+
<VersionUsageBadge
240+
publisherId={params.id}
241+
agentId={params.agentId}
242+
version={version.version}
243+
/>
244+
)}
245+
</div>
242246
{index === 0 && (
243247
<Badge
244248
className={cn(
@@ -260,17 +264,30 @@ const AgentDetailPage = async ({ params }: AgentDetailPageProps) => {
260264
</Card>
261265
</div>
262266

263-
{/* Agent Definition */}
267+
{/* Agent Definition and Usage Stats Combined */}
264268
<div className="lg:col-span-3">
265269
<Card>
266-
<CardHeader>
267-
<CardTitle className="text-lg">Agent Definition</CardTitle>
268-
<p className="text-sm text-muted-foreground">
269-
Complete agent data in TypeScript format
270-
</p>
271-
</CardHeader>
272-
<CardContent>
273-
<TypeScriptViewer data={agentData} />
270+
<CardContent className="space-y-6 pt-6">
271+
{/* Usage Metrics for this version */}
272+
<div>
273+
<h3 className="text-base font-semibold mb-3 flex items-center gap-2">
274+
Usage Statistics
275+
<Badge variant="secondary" className="text-xs">
276+
v{params.version}
277+
</Badge>
278+
</h3>
279+
<AgentUsageMetrics
280+
publisherId={params.id}
281+
agentId={params.agentId}
282+
version={params.version}
283+
/>
284+
</div>
285+
286+
{/* Agent Definition */}
287+
<div className="border-t pt-6">
288+
<h3 className="text-base font-semibold mb-3">Definition</h3>
289+
<TypeScriptViewer data={agentData} />
290+
</div>
274291
</CardContent>
275292
</Card>
276293
</div>

web/src/app/publishers/[id]/agents/[agentId]/[version]/run-agent-button.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { Copy } from 'lucide-react'
3+
import { Play } from 'lucide-react'
44
import { Button } from '@/components/ui/button'
55
import { toast } from '@/components/ui/use-toast'
66

@@ -12,7 +12,7 @@ export function RunAgentButton({ agentId }: RunAgentButtonProps) {
1212
const handleCopy = () => {
1313
navigator.clipboard.writeText(`codebuff --agent ${agentId}`)
1414
toast({
15-
description: `Command copied to clipboard: "codebuff --agent ${agentId}"`,
15+
description: `Command copied! Go to your terminal and paste to run this agent.`,
1616
})
1717
}
1818

@@ -23,7 +23,7 @@ export function RunAgentButton({ agentId }: RunAgentButtonProps) {
2323
onClick={handleCopy}
2424
className="flex items-center gap-2"
2525
>
26-
<Copy className="h-4 w-4" />
26+
<Play className="h-4 w-4" />
2727
Run this agent
2828
</Button>
2929
)

web/src/app/publishers/[id]/agents/[agentId]/[version]/save-agent-button.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { Copy } from 'lucide-react'
3+
import { Bookmark } from 'lucide-react'
44
import { Button } from '@/components/ui/button'
55
import { toast } from '@/components/ui/use-toast'
66

@@ -12,7 +12,7 @@ export function SaveAgentButton({ agentId }: SaveAgentButtonProps) {
1212
const handleCopy = () => {
1313
navigator.clipboard.writeText(`codebuff save-agent ${agentId}`)
1414
toast({
15-
description: `Command copied to clipboard: "codebuff save-agent ${agentId}"`,
15+
description: `Command copied! Go to your terminal and paste to save this agent to your project.`,
1616
})
1717
}
1818

@@ -23,7 +23,7 @@ export function SaveAgentButton({ agentId }: SaveAgentButtonProps) {
2323
onClick={handleCopy}
2424
className="flex items-center gap-2"
2525
>
26-
<Copy className="h-4 w-4" />
26+
<Bookmark className="h-4 w-4" />
2727
Save this agent
2828
</Button>
2929
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use client'
2+
3+
import { useQuery } from '@tanstack/react-query'
4+
import { Badge } from '@/components/ui/badge'
5+
import { Skeleton } from '@/components/ui/skeleton'
6+
7+
interface VersionUsageBadgeProps {
8+
publisherId: string
9+
agentId: string
10+
version: string
11+
}
12+
13+
interface AgentData {
14+
id: string
15+
publisher: {
16+
id: string
17+
}
18+
version_stats?: Record<
19+
string,
20+
{
21+
total_invocations: number
22+
}
23+
>
24+
}
25+
26+
const formatUsageCount = (count?: number) => {
27+
if (!count) return '0'
28+
if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`
29+
if (count >= 1000) return `${(count / 1000).toFixed(1)}K`
30+
return count.toString()
31+
}
32+
33+
export const VersionUsageBadge = ({
34+
publisherId,
35+
agentId,
36+
version,
37+
}: VersionUsageBadgeProps) => {
38+
const { data: agents, isLoading } = useQuery<AgentData[]>({
39+
queryKey: ['agents'],
40+
queryFn: async () => {
41+
const response = await fetch('/api/agents')
42+
if (!response.ok) {
43+
throw new Error('Failed to fetch agents')
44+
}
45+
return await response.json()
46+
},
47+
})
48+
49+
const agent = agents?.find(
50+
(agent) => agent.id === agentId && agent.publisher.id === publisherId
51+
)
52+
53+
const totalRuns = agent?.version_stats?.[version]?.total_invocations || 0
54+
55+
if (isLoading) {
56+
return <Skeleton className="h-4 w-8" />
57+
}
58+
59+
if (totalRuns === 0) {
60+
return null
61+
}
62+
63+
return (
64+
<Badge variant="secondary" className="text-xs px-1.5 py-0 ml-2">
65+
{formatUsageCount(totalRuns)} runs
66+
</Badge>
67+
)
68+
}

0 commit comments

Comments
 (0)