Add agent workflows & stuff
Build and Push Next App / quality (push) Failing after 48s
Build and Push Next App / build-next (push) Has been skipped

This commit is contained in:
Gabriel Brown
2026-06-21 21:15:15 -05:00
parent cf7ff2ee4e
commit 2dfa97ee4f
102 changed files with 8488 additions and 161 deletions
@@ -0,0 +1,281 @@
'use client';
import { useParams } from 'next/navigation';
import { AgentJobList } from '@/components/agents/agent-job-list';
import { AgentRequestForm } from '@/components/agents/agent-request-form';
import { SpoonActivityTimeline } from '@/components/spoons/spoon-activity-timeline';
import { SpoonAgentSettingsForm } from '@/components/spoons/spoon-agent-settings-form';
import { SpoonAiReviewPanel } from '@/components/spoons/spoon-ai-review-panel';
import { SpoonClonePanel } from '@/components/spoons/spoon-clone-panel';
import { SpoonCommitList } from '@/components/spoons/spoon-commit-list';
import { SpoonDetailHeader } from '@/components/spoons/spoon-detail-header';
import { SpoonMetrics } from '@/components/spoons/spoon-metrics';
import { SpoonPrList } from '@/components/spoons/spoon-pr-list';
import { SpoonSecretsForm } from '@/components/spoons/spoon-secrets-form';
import { SpoonSettingsForm } from '@/components/spoons/spoon-settings-form';
import { useQuery } from 'convex/react';
import type { Id } from '@spoon/backend/convex/_generated/dataModel.js';
import { api } from '@spoon/backend/convex/_generated/api.js';
import {
Card,
CardContent,
CardHeader,
CardTitle,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@spoon/ui';
const SpoonDetailPage = () => {
const params = useParams<{ spoonId: string }>();
const spoonId = params.spoonId as Id<'spoons'>;
const details = useQuery(api.spoons.getDetails, { spoonId });
const upstreamCommits =
useQuery(api.spoonCommits.listForSpoon, {
spoonId,
side: 'upstream',
limit: 100,
}) ?? [];
const forkCommits =
useQuery(api.spoonCommits.listForSpoon, {
spoonId,
side: 'fork',
limit: 100,
}) ?? [];
const pullRequests =
useQuery(api.spoonPullRequests.listForSpoon, { spoonId, limit: 100 }) ?? [];
const reviews =
useQuery(api.aiReviews.listForSpoon, { spoonId, limit: 25 }) ?? [];
const syncRuns =
useQuery(api.syncRuns.listForSpoon, { spoonId, limit: 25 }) ?? [];
const agentRequests =
useQuery(api.agentRequests.listForSpoon, { spoonId, limit: 25 }) ?? [];
const agentSettings = useQuery(api.spoonAgentSettings.getForSpoon, {
spoonId,
});
const agentJobs =
useQuery(api.agentJobs.listForSpoon, { spoonId, limit: 25 }) ?? [];
if (details === undefined) {
return <main className='text-muted-foreground p-6'>Loading Spoon...</main>;
}
return (
<main className='space-y-6'>
<SpoonDetailHeader spoon={details.spoon} state={details.state} />
<SpoonMetrics
spoon={details.spoon}
state={details.state}
latestReview={details.latestReview}
/>
{details.spoon.lastError ? (
<Card className='border-destructive shadow-none'>
<CardContent className='p-4 text-sm'>
{details.spoon.lastError}
</CardContent>
</Card>
) : null}
<Tabs defaultValue='overview' className='flex flex-col gap-5'>
<TabsList
variant='line'
className='border-border flex h-auto w-full justify-start overflow-x-auto rounded-none border-b p-0'
>
<TabsTrigger className='h-9 flex-none px-3' value='overview'>
Overview
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='upstream'>
Upstream
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='fork'>
Fork changes
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='pulls'>
Pull requests
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='ai'>
AI review
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='agent'>
Agent work
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='activity'>
Activity
</TabsTrigger>
<TabsTrigger className='h-9 flex-none px-3' value='settings'>
Settings
</TabsTrigger>
</TabsList>
<TabsContent value='overview' className='space-y-4'>
<div className='grid gap-4 xl:grid-cols-[1.15fr_0.85fr]'>
<Card className='shadow-none'>
<CardHeader className='pb-3'>
<CardTitle className='text-base'>Repository health</CardTitle>
</CardHeader>
<CardContent className='grid gap-4 text-sm md:grid-cols-2'>
<div>
<p className='text-muted-foreground'>Drift state</p>
<p className='mt-1 text-xl font-semibold capitalize'>
{(
details.state?.status ??
details.spoon.syncStatus ??
'unknown'
).replaceAll('_', ' ')}
</p>
</div>
<div>
<p className='text-muted-foreground'>Default branches</p>
<p className='mt-1 font-medium'>
{details.state?.upstreamDefaultBranch ??
details.spoon.upstreamDefaultBranch}{' '}
{' '}
{details.state?.forkDefaultBranch ??
details.spoon.forkDefaultBranch ??
details.spoon.upstreamDefaultBranch}
</p>
</div>
<div>
<p className='text-muted-foreground'>Merge base</p>
<p className='mt-1 truncate font-mono text-xs'>
{details.state?.mergeBaseSha ??
details.spoon.lastMergeBaseCommit ??
'Unknown'}
</p>
</div>
<div>
<p className='text-muted-foreground'>Cadence</p>
<p className='mt-1 font-medium capitalize'>
{details.spoon.syncCadence}
</p>
</div>
</CardContent>
</Card>
<Card className='shadow-none'>
<CardHeader className='pb-3'>
<CardTitle className='text-base'>Latest AI review</CardTitle>
</CardHeader>
<CardContent className='space-y-3 text-sm'>
{details.latestReview ? (
<>
<div className='grid grid-cols-2 gap-3'>
<div>
<p className='text-muted-foreground'>Risk</p>
<p className='mt-1 font-semibold capitalize'>
{details.latestReview.risk}
</p>
</div>
<div>
<p className='text-muted-foreground'>Action</p>
<p className='mt-1 font-semibold capitalize'>
{details.latestReview.recommendedAction.replaceAll(
'_',
' ',
)}
</p>
</div>
</div>
<p className='text-muted-foreground'>
{details.latestReview.outputSummary ??
details.latestReview.inputSummary}
</p>
</>
) : (
<p className='text-muted-foreground'>
Run a refresh and AI review to get a compatibility summary
for upstream changes.
</p>
)}
</CardContent>
</Card>
<SpoonClonePanel spoon={details.spoon} />
</div>
<div className='grid gap-4 xl:grid-cols-2'>
<section className='space-y-3'>
<div>
<h2 className='text-base font-semibold'>Upstream waiting</h2>
<p className='text-muted-foreground text-sm'>
Commits upstream has that your fork does not.
</p>
</div>
<SpoonCommitList
commits={upstreamCommits.slice(0, 5)}
empty='No upstream-only commits are cached. Refresh from GitHub to check drift.'
/>
</section>
<section className='space-y-3'>
<div>
<h2 className='text-base font-semibold'>Fork changes</h2>
<p className='text-muted-foreground text-sm'>
Custom commits Spoon should preserve during maintenance.
</p>
</div>
<SpoonCommitList
commits={forkCommits.slice(0, 5)}
empty='No fork-only commits are cached.'
/>
</section>
</div>
</TabsContent>
<TabsContent value='upstream'>
<SpoonCommitList
commits={upstreamCommits}
empty='No upstream changes are waiting, or this Spoon has not been refreshed yet.'
/>
</TabsContent>
<TabsContent value='fork'>
<SpoonCommitList
commits={forkCommits}
empty='No fork-only commits are cached. Your customizations will appear here after refresh.'
/>
</TabsContent>
<TabsContent value='pulls'>
<SpoonPrList pullRequests={pullRequests} />
</TabsContent>
<TabsContent value='ai' className='space-y-4'>
<SpoonAiReviewPanel
latestReview={details.latestReview}
reviews={reviews}
/>
</TabsContent>
<TabsContent value='agent' className='space-y-4'>
<AgentRequestForm
spoon={details.spoon}
agentSettings={agentSettings}
/>
<AgentJobList jobs={agentJobs} />
</TabsContent>
<TabsContent value='activity'>
<SpoonActivityTimeline
syncRuns={syncRuns}
reviews={reviews}
requests={agentRequests}
/>
</TabsContent>
<TabsContent value='settings' className='space-y-4'>
<SpoonSettingsForm
spoon={details.spoon}
settings={details.settings}
/>
<SpoonAgentSettingsForm
spoon={details.spoon}
settings={agentSettings}
/>
<SpoonSecretsForm spoonId={spoonId} />
</TabsContent>
</Tabs>
</main>
);
};
export default SpoonDetailPage;