g1t/apps/web/app/routes/repo/pulls.tsx

102 lines4,289 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Pull requests from branches1import { Bot, GitBranch, Plus } from "lucide-react";
Issues and pull requests replace intents and attempts2import { Link } from "react-router";
3
4import type { Route } from "./+types/pulls";
Social cards for every page: og.g1t.sh5import { page } from "../../lib/meta";
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights6import { openedBy } from "../../lib/opened-by";
Pull requests from branches7import { ButtonLink, EmptyState, TimeAgo } from "../../components/ui";
Acceptance checks in sandboxes, line comments and review verdicts8import { CheckBadge } from "../../components/checks";
Agents as a team: lifecycle, merge queue, billing and a new shell9import { ChangeSize, PullIcon, StateTabs } from "../../components/work";
Agents and memory, checks and conflicts, profiles, slug renames, custom domains10import { AgentBadge, useActiveRuns } from "../../components/agents";
Issues and pull requests replace intents and attempts11import { work } from "../../lib/services.server";
12import { getViewer, unwrap } from "../../lib/session.server";
13
Social cards for every page: og.g1t.sh14export function meta({ params, ...args }: Route.MetaArgs) {
15 return page(args, { title: `Pull requests · ${params.owner}/${params.repo} · g1t` });
Issues and pull requests replace intents and attempts16}
17
18export async function loader({ params, context, request }: Route.LoaderArgs) {
19 const path = { namespace: params.owner, name: params.repo };
20 const state =
21 new URL(request.url).searchParams.get("state") === "closed" ? "closed" : "open";
22 return {
23 pulls: unwrap(await work.listPulls(path, getViewer(context), state)),
24 state,
25 } as const;
26}
27
28export default function Pulls({ loaderData, params }: Route.ComponentProps) {
29 const { pulls, state } = loaderData;
30 const base = `/${params.owner}/${params.repo}`;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains31 // Which pull requests an agent is working on this minute, and at what.
32 const working = useActiveRuns(params.owner, params.repo);
Issues and pull requests replace intents and attempts33 return (
34 <div>
Pull requests from branches35 <StateTabs
36 to={`${base}/pulls`}
37 state={state}
38 action={
39 <ButtonLink to={`${base}/pulls/new`}>
40 <Plus size={15} />
41 New pull request
42 </ButtonLink>
43 }
44 />
Issues and pull requests replace intents and attempts45 <div className="mt-4">
46 {pulls.length === 0 ? (
47 <EmptyState
48 title={state === "open" ? "No open pull requests" : "No closed pull requests"}
49 >
Pull requests from branches50 A pull request proposes a change. Assign agents to an issue and
51 each opens one in its own fork, or push a branch and open one
52 yourself.
Issues and pull requests replace intents and attempts53 </EmptyState>
54 ) : (
55 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line">
56 {pulls.map((pull) => (
57 <li key={pull.id}>
58 <Link
Agents as a team: lifecycle, merge queue, billing and a new shell59 prefetch="intent"
Issues and pull requests replace intents and attempts60 to={`${base}/pull/${pull.number}`}
61 className="flex items-start gap-3 px-4 py-3 transition-colors hover:bg-surface"
62 >
63 <span className="mt-0.5">
64 <PullIcon status={pull.status} />
65 </span>
66 <span className="min-w-0 grow">
67 <span className="flex items-center gap-2">
68 <span className="truncate font-medium">{pull.title}</span>
69 {pull.status === "draft" && (
70 <span className="shrink-0 rounded-full border border-line px-1.5 py-px text-[0.6875rem] text-faint">
71 draft
72 </span>
73 )}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains74 <AgentBadge run={working.get(pull.number)} />
Issues and pull requests replace intents and attempts75 </span>
76 <span className="mt-0.5 block text-xs text-faint">
77 #{pull.number} opened <TimeAgo at={pull.createdAt} /> by{" "}
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent78 {openedBy(pull).name}
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights79 {pull.requestedBy && <> for {pull.requestedBy.username}</>}
Issues and pull requests replace intents and attempts80 {pull.issue != null && <> · for #{pull.issue}</>}
81 {pull.supersededBy != null && <> · superseded by #{pull.supersededBy}</>}
82 </span>
83 </span>
Agents as a team: lifecycle, merge queue, billing and a new shell84 <span className="mt-0.5 hidden sm:block">
85 <ChangeSize files={pull.files} />
86 </span>
Acceptance checks in sandboxes, line comments and review verdicts87 <span className="mt-0.5">
88 <CheckBadge status={pull.checkStatus} />
89 </span>
Issues and pull requests replace intents and attempts90 <span className="mt-0.5 flex shrink-0 items-center gap-1 font-mono text-xs text-muted">
Pull requests from branches91 {pull.branch ? <GitBranch size={13} /> : <Bot size={13} />}
92 {pull.branch ?? pull.agent}
Issues and pull requests replace intents and attempts93 </span>
94 </Link>
95 </li>
96 ))}
97 </ul>
98 )}
99 </div>
100 </div>
101 );
102}