Skip to content
1,214 linesCodeBlameRaw

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.

Mission control: a place to ask g1t, shown and not on yet, with the actions under it1import { ArrowDownWideNarrow, ArrowRight, ArrowUpRight, Check, ChevronDown, ChevronRight, ListTree, Plus, Sparkles } from "lucide-react";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2import { type ReactNode, useEffect, useState } from "react";
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)3import { Link, useFetcher, useLocation, useRouteLoaderData, useSearchParams } from "react-router";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look4
5import { trialClosed } from "../lib/trial";
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms6import { type ActivityGroup, type Verb, greetingFor, isAgent } from "../lib/mission";
7import {
8 BLOCKING,
9 type Fact,
10 type LandedRow,
11 type NeedRow,
12 type QuickAction,
13 REASON_LABEL,
14 type Reason,
15 type Sort,
16 type Tab,
17 type WaitingRow,
18 type Week,
19 type Who,
20 change,
21 dateLine,
22 parseSort,
23 parseTab,
24 signedPercent,
25 sortRows,
26 usd,
27 whyFor,
28} from "../lib/mission-control";
29import { cn } from "../lib/cn";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step30import { AgentComposer, type ComposerResult } from "./agent-composer";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look31import { AgentSetup } from "./agent-setup";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all32import { InboxNeedsCard } from "./inbox";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look33import type { ShellData } from "./shell";
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms34import { useLiveRefresh } from "./agents";
35import { Unavailable } from "./mission";
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix36import { TokenUsagePanel } from "./token-usage";
Merge branch 'worktree-agent-a985c01f43538af01'37import { Avatar, SubmitButton, TimeAgo } from "./ui";
Merge branch 'worktree-agent-ae1299e92e4462012'38import { Hint } from "./ui/hint";
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms39import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from "./ui/dropdown-menu";
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)40import type { Loaded } from "../lib/mission-control.server";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look41
42/*
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms43 * Mission control: the home page of someone signed in. It answers where
44 * the viewer is needed, what agents are doing, and what landed without
45 * them. It is a chunk of its own, loaded only for them, so the signed-out
46 * home page does not carry it. routes/home.tsx loads its data;
47 * lib/mission-control.ts shapes it.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look48 */
49
50/** The viewer's time zone, set by the page itself, so the greeting fits their day. */
51const TZ_COOKIE = "g1t_tz";
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms52/** Rows a tab shows before "See all". */
53const ROWS = 6;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look54
55const dollars = (micros: number) => `$${(Math.max(0, micros) / 1_000_000).toFixed(2)}`;
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms56const plural = (n: number, one: string, many = `${one}s`) => `${n.toLocaleString("en-US")} ${n === 1 ? one : many}`;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look57
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms58// --- Small pieces -----------------------------------------------------------------
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look59
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms60const CHIP_TONE: Record<Reason, string> = {
61 blocking: "border-danger/35 bg-danger/10 text-danger",
62 checks_failing: "border-danger/35 bg-danger/10 text-danger",
63 outside_guardrails: "border-warn/35 bg-warn/10 text-warn",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step64 low_confidence: "border-warn/35 bg-warn/10 text-warn",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms65 stalled: "border-warn/35 bg-warn/10 text-warn",
Merge branch 'worktree-agent-ad7c6d88d93adc817'66 asked_for_you: "border-accent/35 bg-accent/10 text-accent",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms67 needs_review: "border-info/35 bg-info/10 text-info",
Merge branch 'worktree-agent-ad7c6d88d93adc817'68 ready_to_merge: "border-success/35 bg-success/10 text-success",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms69};
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look70
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms71function Chip({ children, tone }: { children: ReactNode; tone: string }) {
72 return (
73 <span
74 className={cn(
75 "inline-flex shrink-0 items-center rounded border px-1.5 py-px font-mono text-[0.625rem] font-medium tracking-wider whitespace-nowrap uppercase",
76 tone,
77 )}
78 >
79 {children}
80 </span>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look81 );
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms82}
83
84/** A person or an agent, by name, wearing the mark that tells them apart. */
85function Person({ who, size = 16, className }: { who: Who; size?: number; className?: string }) {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look86 return (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms87 <span className={cn("inline-flex min-w-0 items-center gap-1.5", className)}>
88 <Avatar name={who.name} size={size} />
89 <span className="truncate">{who.name}</span>
90 </span>
91 );
92}
93
94function Eyebrow({ children }: { children: ReactNode }) {
95 return <p className="font-mono text-[0.625rem] font-medium tracking-[0.12em] text-faint uppercase">{children}</p>;
96}
97
Merge branch 'worktree-agent-ad7c6d88d93adc817'98const FACT_TONE = { good: "text-success", warn: "text-warn", bad: "text-danger" } as const;
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms99
100function Facts({ facts }: { facts: Fact[] }) {
101 if (facts.length === 0) return <p className="text-sm text-muted">Nothing more is known about it yet.</p>;
102 return (
103 <dl className="grid grid-cols-2 gap-x-4 gap-y-2.5">
104 {facts.map((fact) => (
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step105 <div key={fact.label} className={cn("min-w-0", fact.wide && "col-span-2")}>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms106 <dt className="text-xs text-faint">{fact.label}</dt>
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step107 <dd
108 className={cn(
109 "mt-0.5 text-sm font-medium tabular-nums",
110 fact.wide ? "leading-5" : "truncate",
111 fact.tone ? FACT_TONE[fact.tone] : "text-fg-soft",
112 )}
113 >
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms114 {fact.value}
115 </dd>
116 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look117 ))}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms118 </dl>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look119 );
120}
121
Merge branch 'worktree-agent-a985c01f43538af01'122/**
123 * A form that acts on a pull request from here: working until the action
124 * has answered and the page has reloaded what it changed, then done, or
125 * the error beside it.
126 */
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms127function QuickForm({ quick, variant = "quiet" }: { quick: QuickAction; variant?: "quiet" | "accent" }) {
128 const fetcher = useFetcher<{ error?: string } | null>();
129 const [sent, setSent] = useState(false);
Merge branch 'worktree-agent-a985c01f43538af01'130 const done = sent && fetcher.state === "idle" && !fetcher.data?.error;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look131 return (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms132 <fetcher.Form method="post" action={quick.to} onSubmit={() => setSent(true)} className="contents">
133 {Object.entries(quick.fields).map(([name, value]) => (
134 <input key={name} type="hidden" name={name} value={value} />
135 ))}
Merge branch 'worktree-agent-a985c01f43538af01'136 <SubmitButton
137 fetcher={fetcher}
138 disabled={done}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms139 className={cn(
140 "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-default",
141 variant === "accent"
Merge branch 'worktree-agent-ad7c6d88d93adc817'142 ? "bg-accent text-bg hover:bg-accent-hover"
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms143 : "border border-line-strong text-fg/90 hover:bg-raised hover:text-fg",
Merge branch 'worktree-agent-ad7c6d88d93adc817'144 done && "border-success/40 text-success",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms145 )}
146 >
Fast pages, required checks on the branch, self-hosted runners, honest incidents147 {done ? <Check size={14} /> : null}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms148 {done ? quick.done : quick.label}
Merge branch 'worktree-agent-a985c01f43538af01'149 </SubmitButton>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms150 {fetcher.data?.error && <span className="basis-full text-xs text-danger">{fetcher.data.error}</span>}
151 </fetcher.Form>
152 );
153}
154
155function ProjectMark({ repo }: { repo: { namespace: string; name: string } | null }) {
156 return <Avatar name={repo ? repo.name : "g1t"} size={32} square />;
157}
158
159/** A row that opens to what is known about it; the first one starts open. */
160function Row({
161 open,
162 repo,
163 by,
164 title,
165 sub,
166 chip,
167 at,
168 children,
169}: {
170 open: boolean;
171 repo: { namespace: string; name: string } | null;
172 by: Who | null;
173 title: ReactNode;
174 sub: ReactNode;
175 chip: ReactNode;
176 at: number;
177 children: ReactNode;
178}) {
179 return (
180 <li>
181 <details open={open} className="group">
182 <summary className="flex cursor-pointer list-none items-center gap-3 px-4 py-3 transition-colors select-none hover:bg-raised/60 sm:px-5 [&::-webkit-details-marker]:hidden">
183 <ProjectMark repo={repo} />
184 <span className="min-w-0 grow">
185 <span className="block truncate text-sm">{title}</span>
186 <span className="mt-0.5 flex min-w-0 items-center gap-1.5 text-xs text-muted">
187 <span className="shrink-0 font-medium text-fg-soft">{repo ? repo.name : "Workspace"}</span>
188 {by && (
189 <>
190 <span className="text-faint">·</span>
Merge branch 'worktree-agent-ad7c6d88d93adc817'191 <Person who={by} size={14} className={cn("shrink-0", by.agent ? "text-accent" : "")} />
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms192 </>
193 )}
194 {sub && (
195 <>
196 <span className="text-faint">—</span>
197 <span className="truncate">{sub}</span>
198 </>
199 )}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look200 </span>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms201 </span>
202 <span className="hidden sm:inline-flex">{chip}</span>
203 <span className="hidden w-16 shrink-0 text-right text-xs text-faint sm:block">
204 <TimeAgo at={at} />
205 </span>
206 <ChevronDown size={16} className="shrink-0 text-faint transition-transform group-open:rotate-180" />
207 </summary>
208 <div className="px-4 pb-4 sm:px-5 sm:pb-5">
209 <div className="mb-3 flex items-center gap-2 sm:hidden">
210 {chip}
211 <span className="text-xs text-faint">
212 <TimeAgo at={at} />
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look213 </span>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms214 </div>
215 {children}
216 </div>
217 </details>
218 </li>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look219 );
220}
221
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms222function NeedCard({ row, first }: { row: NeedRow; first: boolean }) {
223 const isPull = row.to.includes("/pull/");
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look224 return (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms225 <Row
226 open={first}
227 repo={row.repo}
228 by={row.by}
229 title={
230 <>
231 <span className="font-medium">{row.title}</span>
232 {row.ref && <span className="ml-1 font-mono text-xs text-faint">{row.ref}</span>}
233 </>
234 }
235 sub={row.ask}
236 chip={<Chip tone={CHIP_TONE[row.reason]}>{REASON_LABEL[row.reason]}</Chip>}
237 at={row.at}
238 >
239 <div className="grid gap-4 rounded-xl border border-line bg-bg/50 p-4 md:grid-cols-2 md:gap-x-6 2xl:grid-cols-[minmax(0,1.1fr)_minmax(0,1fr)_minmax(0,0.95fr)]">
240 <div className="min-w-0">
241 <Eyebrow>The ask</Eyebrow>
242 <p className="mt-2 text-sm leading-6 text-fg-soft">{row.ask}</p>
243 <p className="mt-2 text-xs leading-5 text-muted">
244 <Link to={row.to} className="font-medium text-fg hover:underline">
245 {row.repo ? `${row.repo.name}${row.ref ?? ""}` : row.title}
246 </Link>
247 {row.for && (
248 <>
249 {" "}
250 · started for <span className="text-fg-soft">{row.for}</span>
251 </>
252 )}
253 </p>
254 </div>
255 <div className="min-w-0">
Usage, Billing settings and prepaid AI credit; fixes from the UX audit256 <Eyebrow>{row.by?.agent ? "What the agent already knows" : "What is known"}</Eyebrow>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms257 <div className="mt-2">
258 <Facts facts={row.facts} />
259 </div>
260 </div>
261 <div className="rounded-lg border border-warn/20 bg-warn/[0.06] p-3.5 md:col-span-2 2xl:col-span-1">
262 <p className="text-xs font-semibold text-warn">Why this needs you</p>
263 <p className="mt-1.5 text-sm leading-6 text-fg-soft">{row.why}</p>
264 <p className="mt-2 text-xs text-faint">
Usage, Billing settings and prepaid AI credit; fixes from the UX audit265 Started waiting <TimeAgo at={row.at} />
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms266 </p>
267 </div>
268 </div>
269 <div className="mt-3 flex flex-wrap items-center gap-2">
270 <Link
271 to={row.to}
272 prefetch="intent"
273 className="inline-flex items-center gap-1.5 rounded-md bg-fg px-3 py-1.5 text-sm font-medium text-bg transition-colors hover:bg-white"
274 >
275 Review and respond <ArrowUpRight size={14} />
276 </Link>
277 {row.quick && <QuickForm quick={row.quick} />}
278 {row.link && (
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look279 <Link
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms280 to={row.link.to}
281 className="inline-flex items-center rounded-md border border-line-strong px-3 py-1.5 text-sm font-medium text-fg/90 hover:bg-raised hover:text-fg"
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look282 >
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms283 {row.link.label}
284 </Link>
285 )}
286 {row.open !== row.to && (
287 <Link to={row.open} prefetch="intent" className="px-2 py-1.5 text-sm text-muted hover:text-fg">
288 {row.open.includes("/issues/") ? "Open issue" : isPull ? "See the changes" : "Open pull request"}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look289 </Link>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms290 )}
291 </div>
292 </Row>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look293 );
294}
295
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms296function WaitingCard({ row, first }: { row: WaitingRow; first: boolean }) {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look297 return (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms298 <Row
299 open={first}
300 repo={row.repo}
301 by={row.by}
302 title={
303 <>
304 <span className="font-medium">{row.title}</span>
305 {row.ref && <span className="ml-1 font-mono text-xs text-faint">{row.ref}</span>}
306 </>
307 }
308 sub={row.detail}
309 chip={
310 <Chip tone="border-line-strong bg-raised text-muted">
Merge branch 'worktree-agent-ad7c6d88d93adc817'311 {row.live && <span className="mr-1.5 size-1.5 animate-pulse rounded-full bg-success" />}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms312 {row.chip}
313 </Chip>
314 }
315 at={row.at}
316 >
317 <div className="grid gap-4 rounded-xl border border-line bg-bg/50 p-4 md:grid-cols-2 md:gap-6">
318 <div className="min-w-0">
319 <Eyebrow>{row.live ? "Doing now" : "Where it stands"}</Eyebrow>
320 <p className="mt-2 text-sm leading-6 text-fg-soft">{row.detail}</p>
321 </div>
322 <div className="min-w-0">
323 <Eyebrow>What is known</Eyebrow>
324 <div className="mt-2">
325 <Facts facts={row.facts} />
326 </div>
327 </div>
328 </div>
329 <div className="mt-3 flex flex-wrap gap-2">
330 <Link
331 to={row.to}
332 prefetch="intent"
333 className="inline-flex items-center gap-1.5 rounded-md border border-line-strong px-3 py-1.5 text-sm font-medium text-fg/90 hover:bg-raised hover:text-fg"
334 >
335 {row.to.includes("/runs/") ? "Watch the run" : "Open pull request"} <ArrowUpRight size={14} />
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look336 </Link>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms337 {row.run && row.run !== row.to && (
338 <Link to={row.run} prefetch="intent" className="px-2 py-1.5 text-sm text-muted hover:text-fg">
339 Watch the run
340 </Link>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look341 )}
342 </div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms343 </Row>
344 );
345}
346
347function LandedCard({ row, first }: { row: LandedRow; first: boolean }) {
348 return (
349 <Row
350 open={first}
351 repo={row.repo}
352 by={row.byAgents ? { name: row.agent, agent: isAgent(row.agent) } : row.by}
353 title={
354 <>
355 <span className="font-medium">{row.title}</span>
356 <span className="ml-1 font-mono text-xs text-faint">{row.ref}</span>
357 </>
358 }
359 sub={row.byAgents ? "landed without a person" : `merged by ${row.by?.name ?? "a person"}`}
360 chip={
361 row.byAgents ? (
Merge branch 'worktree-agent-ad7c6d88d93adc817'362 <Chip tone="border-accent/35 bg-accent/10 text-accent">By agents</Chip>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look363 ) : (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms364 <Chip tone="border-warn/35 bg-warn/10 text-warn">Needed a person</Chip>
365 )
366 }
367 at={row.at}
368 >
369 <div className="grid gap-4 rounded-xl border border-line bg-bg/50 p-4 md:grid-cols-2 md:gap-6">
370 <div className="min-w-0">
371 <Eyebrow>How it landed</Eyebrow>
372 <p className="mt-2 text-sm leading-6 text-fg-soft">
373 {row.byAgents
374 ? `g1t merged it once everything the repository asks for was met, with no one pressing merge.`
375 : `${row.by?.name ?? "A person"} merged it.`}
376 {row.agent && isAgent(row.agent) && ` ${row.agent} made the change.`}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look377 </p>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms378 </div>
379 <div className="min-w-0">
380 <Eyebrow>What changed</Eyebrow>
381 <div className="mt-2">
382 <Facts facts={row.facts} />
383 </div>
384 </div>
385 </div>
386 <div className="mt-3">
387 <Link
388 to={row.to}
389 prefetch="intent"
390 className="inline-flex items-center gap-1.5 rounded-md border border-line-strong px-3 py-1.5 text-sm font-medium text-fg/90 hover:bg-raised hover:text-fg"
391 >
392 Open pull request <ArrowUpRight size={14} />
393 </Link>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look394 </div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms395 </Row>
396 );
397}
398
399function List({ children }: { children: ReactNode }) {
400 return <ul className="divide-y divide-line">{children}</ul>;
401}
402
403function Empty({ children, action }: { children: ReactNode; action?: ReactNode }) {
404 return (
405 <div className="px-5 py-10 text-center">
406 <div className="mx-auto max-w-md text-sm leading-6 text-muted">{children}</div>
407 {action && <div className="mt-4 flex flex-wrap justify-center gap-2">{action}</div>}
408 </div>
409 );
410}
411
412// --- The stat strip -----------------------------------------------------------------
413
414function Stat({ label, value, hint, dot, title }: { label: string; value: string; hint: ReactNode; dot?: string; title?: string }) {
415 return (
Merge branch 'worktree-agent-ae1299e92e4462012'416 <Hint label={title}>
417 <div className="min-w-0 bg-surface px-4 py-3.5 sm:px-5 sm:py-4">
418 <p className="flex items-center gap-1.5 truncate text-xs text-muted">
419 {dot && <span className={cn("size-1.5 shrink-0 rounded-full", dot)} />}
420 {label}
421 </p>
422 <p className="mt-1 font-display text-2xl font-semibold tracking-tight tabular-nums sm:text-[1.75rem]">{value}</p>
423 <p className="mt-0.5 truncate text-xs text-faint">{hint}</p>
424 </div>
425 </Hint>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms426 );
427}
428
429// --- This week ----------------------------------------------------------------------
430
431/** Seven days of landed changes, each split into what agents landed alone and what a person merged. */
432function WeekChart({ week }: { week: Week }) {
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar433 const max = Math.max(1, ...week.days.map((d) => d.agents + d.assisted + d.people));
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms434 const height = 112;
435 const delta = change(week.total, week.previous);
436 return (
437 <div>
438 <div className="flex items-end justify-between gap-3">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look439 <div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms440 <p className="font-display text-3xl font-semibold tracking-tight tabular-nums">{week.total.toLocaleString("en-US")}</p>
441 <p className="text-xs text-muted">changes landed in 7 days</p>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look442 </div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms443 {delta != null ? (
444 <span
445 className={cn(
446 "rounded-full px-2 py-0.5 text-xs font-medium tabular-nums ring-1",
Merge branch 'worktree-agent-ad7c6d88d93adc817'447 delta >= 0 ? "text-success ring-success/30" : "text-warn ring-warn/30",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms448 )}
449 >
450 {signedPercent(delta)} vs last week
451 </span>
452 ) : week.previous === 0 && week.total > 0 ? (
453 <span className="text-xs text-faint">none the week before</span>
454 ) : null}
455 </div>
456 {week.total === 0 ? (
457 <p className="mt-4 rounded-lg border border-dashed border-line px-4 py-6 text-center text-sm leading-6 text-muted">
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar458 Nothing landed in the last 7 days. Each change that does shows here, by day: agents' changes that landed on their own, agents' that a person merged, and people's own.
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms459 </p>
460 ) : (
461 <div className="relative mt-5" style={{ height: height + 20 }}>
462 <div className="absolute inset-x-0 border-t border-line" style={{ top: height }} />
463 <div className="absolute inset-x-0 top-0 flex items-end justify-between gap-1" style={{ height }}>
464 {week.days.map((day, index) => {
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar465 const total = day.agents + day.assisted + day.people;
466 const bar = (n: number) => Math.max(3, Math.round((n / max) * (height - 4)));
467 // Top to bottom: agents alone, agents with a person, people.
468 const segments = [
Merge branch 'worktree-agent-ad7c6d88d93adc817'469 { n: day.agents, tone: "bg-accent" },
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar470 { n: day.assisted, tone: "bg-warn" },
471 { n: day.people, tone: "bg-info" },
472 ].filter((segment) => segment.n > 0);
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms473 const today = index === week.days.length - 1;
474 return (
475 <div key={day.key} className="group relative flex h-full flex-1 flex-col items-center justify-end">
476 {total > 0 && (
477 <span
478 className={cn(
479 "mb-1 text-[0.625rem] tabular-nums",
Phones reach every page: each mode's menu from the top bar or by tapping the tab you're on, Docs first in the avatar sheet, a project's pages as a swipeable strip; dialogs as bottom sheets above the keyboard, safe areas, menus kept on screen, 40px targets, touch-visible controls, save bars above the tab bar480 today ? "text-fg-soft" : "text-faint opacity-0 group-hover:opacity-100 max-md:hidden",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms481 )}
482 >
483 {total}
484 </span>
485 )}
486 <div className="flex w-full max-w-6 flex-col items-stretch gap-[2px]">
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar487 {segments.map((segment, at) => (
488 <span key={segment.tone} className={cn("block", segment.tone, at === 0 && "rounded-t")} style={{ height: bar(segment.n) }} />
489 ))}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms490 {total === 0 && <span className="block h-[2px] rounded-full bg-line-strong" />}
491 </div>
Phones reach every page: each mode's menu from the top bar or by tapping the tab you're on, Docs first in the avatar sheet, a project's pages as a swipeable strip; dialogs as bottom sheets above the keyboard, safe areas, menus kept on screen, 40px targets, touch-visible controls, save bars above the tab bar492 <div className="pointer-events-none absolute bottom-full left-1/2 z-10 mb-1 hidden -translate-x-1/2 rounded-md border border-line-strong bg-raised px-2.5 py-1.5 text-xs whitespace-nowrap shadow-lg shadow-black/40 md:group-hover:block">
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms493 <p className="font-medium text-fg">{day.label}</p>
494 <p className="mt-0.5 flex items-center gap-1.5 text-muted">
Merge branch 'worktree-agent-ad7c6d88d93adc817'495 <span className="size-1.5 rounded-full bg-accent" /> {day.agents} by agents on their own
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar496 </p>
497 <p className="flex items-center gap-1.5 text-muted">
498 <span className="size-1.5 rounded-full bg-warn" /> {day.assisted} by agents, merged by a person
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms499 </p>
500 <p className="flex items-center gap-1.5 text-muted">
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar501 <span className="size-1.5 rounded-full bg-info" /> {day.people} by people
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms502 </p>
503 </div>
504 </div>
505 );
506 })}
507 </div>
508 <div className="absolute inset-x-0 bottom-0 flex justify-between gap-1">
509 {week.days.map((day, index) => (
510 <span
511 key={day.key}
512 className={cn(
513 "flex-1 text-center text-[0.6875rem]",
514 index === week.days.length - 1 ? "font-medium text-fg-soft" : "text-faint",
515 )}
516 >
517 {day.label}
518 </span>
519 ))}
520 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look521 </div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms522 )}
523 <div className={cn("mt-3 flex-wrap gap-x-4 gap-y-1 text-xs text-muted", week.total === 0 ? "hidden" : "flex")}>
524 <span className="inline-flex items-center gap-1.5">
Merge branch 'worktree-agent-ad7c6d88d93adc817'525 <span className="size-2 rounded-sm bg-accent" /> Agents, on their own
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar526 </span>
527 <span className="inline-flex items-center gap-1.5">
528 <span className="size-2 rounded-sm bg-warn" /> Agents, merged by a person
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms529 </span>
530 <span className="inline-flex items-center gap-1.5">
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar531 <span className="size-2 rounded-sm bg-info" /> People
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms532 </span>
533 </div>
Fixes from the page sweep534 {/* A table does not shrink to sr-only's 1px; its wrapper does. */}
535 <div className="sr-only">
536 <table>
537 <caption>Changes landed each day</caption>
538 <thead>
539 <tr>
540 <th>Day</th>
541 <th>Agents, on their own</th>
542 <th>Agents, merged by a person</th>
543 <th>People</th>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms544 </tr>
Fixes from the page sweep545 </thead>
546 <tbody>
547 {week.days.map((day) => (
548 <tr key={day.key}>
549 <td>{day.key}</td>
550 <td>{day.agents}</td>
551 <td>{day.assisted}</td>
552 <td>{day.people}</td>
553 </tr>
554 ))}
555 </tbody>
556 </table>
557 </div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms558 </div>
559 );
560}
561
562// --- Activity -----------------------------------------------------------------------
563
564const VERB: Record<Verb, string> = {
565 landed: "landed",
566 opened_issue: "opened",
567 closed_issue: "closed",
568 started: "started on",
569 ready: "marked ready",
570 checks_passed: "checks passed on",
571 checks_failed: "checks failed on",
572 approved: "approved",
573 changes_requested: "asked for changes on",
574 commented: "commented on",
575 asked: "was asked about",
576 deployed: "deployed production",
577 deploy_failed: "production build failed",
Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md578 pushed: "pushed to the default branch",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms579 learned: "learned",
580};
581
582/** One line of the feed: who, what, and the one thing it was about, by name. */
583function FeedLine({ group, titles }: { group: ActivityGroup; titles: Record<string, string> }) {
584 const part = group.parts[0];
585 const base = `/${group.repo.namespace}/${group.repo.name}`;
586 const actor = group.actor ?? "g1t";
587 const agent = group.actor == null || isAgent(group.actor);
588 const number = part.numbers[0];
589 const title = number != null ? titles[`${group.repo.namespace}/${group.repo.name}#${number}`.toLowerCase()] : undefined;
590 const more = group.parts.length - 1 + Math.max(0, part.numbers.length - 1);
591 return (
592 <li className="flex gap-2.5 py-2">
593 <span className="mt-px">
594 <Avatar name={actor} size={22} />
595 </span>
596 <span className="min-w-0 grow text-[0.8125rem] leading-5 text-muted">
Merge branch 'worktree-agent-ad7c6d88d93adc817'597 <span className={cn("font-medium", agent ? "text-accent" : "text-fg")}>{actor}</span> {VERB[part.verb]}{" "}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms598 {part.verb === "learned" ? (
599 <Link to={part.to ?? `${base}/memory`} className="text-fg-soft hover:text-fg">
600 “{part.texts[0]}”
601 </Link>
602 ) : number != null ? (
603 <Link to={`${base}/issues/${number}`} prefetch="intent" className="text-fg-soft hover:text-fg">
604 <span className="font-mono text-xs">#{number}</span>
605 {title && <> {title}</>}
606 </Link>
607 ) : part.to ? (
608 <Link to={part.to} className="text-fg-soft hover:text-fg">
609 {group.repo.name}
610 </Link>
611 ) : null}
612 {more > 0 && <span className="text-faint"> and {more} more</span>}
613 <span className="block text-xs text-faint">
614 <span className="font-mono">{group.repo.name}</span> · <TimeAgo at={group.at} />
615 </span>
616 </span>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look617 </li>
618 );
619}
620
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms621// --- Get started --------------------------------------------------------------------
622
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look623type Step = { done: boolean; title: string; about: string; to: string | null; action: string };
624
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms625/** The first things to do, ticked off as they are done, until work has been handed to agents. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look626function GetStarted({ steps }: { steps: Step[] }) {
627 const left = steps.filter((step) => !step.done).length;
628 return (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms629 <section className="rounded-xl border border-line bg-surface p-5">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look630 <div className="flex items-baseline justify-between gap-3">
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms631 <h2 className="text-base font-semibold tracking-tight">Get started</h2>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look632 <span className="text-xs text-muted">
633 {steps.length - left} of {steps.length} done
634 </span>
635 </div>
636 <ol className="mt-4 space-y-2">
637 {steps.map((step, index) => (
638 <li
639 key={step.title}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms640 className={cn("flex items-start gap-3 rounded-lg px-3 py-2.5", step.done ? "" : "bg-bg/50 ring-1 ring-line")}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look641 >
642 <span
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms643 className={cn(
644 "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full text-[0.6875rem] font-medium",
Merge branch 'worktree-agent-ad7c6d88d93adc817'645 step.done ? "bg-success text-bg" : "text-muted ring-1 ring-line-strong",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms646 )}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look647 >
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms648 {step.done ? <Check size={12} /> : index + 1}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look649 </span>
650 <span className="min-w-0 grow">
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms651 <span className={cn("block text-sm font-medium", step.done && "text-muted line-through decoration-faint")}>{step.title}</span>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look652 {!step.done && <span className="mt-0.5 block text-xs leading-5 text-muted">{step.about}</span>}
653 </span>
654 {!step.done && step.to && (
655 <Link
656 to={step.to}
657 className="shrink-0 rounded-md bg-fg px-2.5 py-1 text-xs font-medium text-bg transition-colors hover:bg-white"
658 >
659 {step.action}
660 </Link>
661 )}
662 </li>
663 ))}
664 </ol>
665 </section>
666 );
667}
668
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms669// --- The page -----------------------------------------------------------------------
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look670
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms671const TAB_LABEL: Record<Tab, string> = { needs: "Needs you", waiting: "Waiting on agents", landed: "Landed today" };
672const TAB_SHORT: Record<Tab, string> = { needs: "Needs you", waiting: "Waiting", landed: "Today" };
673const SORT_LABEL: Record<Sort, string> = { impact: "By impact", newest: "Newest" };
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look674
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)675export default function MissionControl({
676 loaderData,
677 delegated = null,
678 variant = "code",
679}: {
680 loaderData: Loaded;
681 delegated?: ComposerResult;
682 /** Code's Overview: its own title in place of the greeting, and the actions on their own (Home has the Agent box). */
683 variant?: "code";
684}) {
685 const { pathname } = useLocation();
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look686 const shell = useRouteLoaderData("root")?.shell as ShellData | null | undefined;
687 const loaded: Loaded = loaderData;
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms688 const [params] = useSearchParams();
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look689 // Agents are at work, so the page changes without anyone touching it.
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms690 useLiveRefresh(loaded.changing);
691 const [greeting, setGreeting] = useState(loaded.greeting);
692 const [date, setDate] = useState(loaded.date);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look693 useEffect(() => {
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms694 // The greeting and date follow the viewer's own clock, and the server
695 // learns their zone for next time.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look696 setGreeting(greetingFor(new Date().getHours()));
697 try {
698 const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms699 setDate(dateLine(Date.now(), zone ?? null));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look700 if (zone) document.cookie = `${TZ_COOKIE}=${encodeURIComponent(zone)}; Path=/; Max-Age=31536000; SameSite=Lax`;
701 } catch {
702 // Nothing to remember.
703 }
704 }, []);
705
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms706 const { viewer, repos, waiting, landed, liveTotal, week, groups, titles, stats, canRunAgents, trial } = loaded;
707 const workspace = shell?.workspace?.slug ?? loaded.workspace ?? viewer.workspaces?.[0]?.slug ?? null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look708
709 // The usage limit, as the sidebar knows it, heads the list when it bites.
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms710 const limit = shell?.limit && !shell.limit.comped && shell.limit.state !== "ok" && workspace ? shell.limit : null;
711 const limitRow: NeedRow[] = limit
712 ? [
713 (() => {
714 const detail =
715 limit.state === "stopped"
716 ? "No new agent starts until the limit is raised or a card is added."
717 : `${dollars(limit.exposureMicros)} of ${dollars(limit.ceilingMicros ?? 0)} used. Add a card so work keeps going.`;
718 return {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look719 key: "limit",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms720 reason: "blocking" as const,
721 repo: null,
722 ref: null,
723 title: limit.state === "stopped" ? "Agents are stopped: the usage limit was reached" : "Usage is nearing its limit",
724 ask: detail,
725 by: null,
726 for: null,
727 at: Date.now(),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look728 to: `/${workspace}/-/billing`,
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms729 open: `/${workspace}/-/billing`,
730 facts: [
731 { label: "Used", value: `${dollars(limit.exposureMicros)} of ${dollars(limit.ceilingMicros ?? 0)}`, tone: "warn" as const },
732 ],
733 why: whyFor("blocking", { kind: "limit", detail }),
734 quick: null,
735 link: null,
736 };
737 })(),
738 ]
739 : [];
740 const needs = [...limitRow, ...loaded.needs];
741 const blocking = needs.filter((row) => BLOCKING.has(row.reason)).length;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look742
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms743 const counts: Record<Tab, number> = { needs: needs.length, waiting: waiting.length, landed: landed.length };
744 const tab = parseTab(params.get("tab")) ?? "needs";
745 const sort = parseSort(params.get("sort"));
746 const all = params.get("all") === "1";
747 const everyActivity = params.get("activity") === "all";
748 const link = (changes: Record<string, string | null>) => {
749 const next = new URLSearchParams(params);
750 for (const [key, value] of Object.entries(changes)) {
751 if (value == null) next.delete(key);
752 else next.set(key, value);
753 }
754 const text = next.toString();
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)755 return text ? `?${text}` : pathname;
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms756 };
757
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look758 const steps: Step[] = [
759 {
760 done: Boolean(workspace),
761 title: "Create a workspace",
762 about: "Projects, people and agent credit live in one.",
763 to: "/workspaces/new",
764 action: "Create",
765 },
766 {
767 done: repos.length > 0,
768 title: "Add a project",
769 about: "Create a repository, or import one by its address. Push to it with git as usual.",
770 to: workspace ? `/new?workspace=${workspace}` : "/new",
771 action: "Add",
772 },
773 trial?.open
774 ? {
775 done: true,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent776 title: "Try g1t free",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look777 about: trial.granted
778 ? `This workspace has ${dollars(trial.limitMicros - trial.usedMicros)} of its ${dollars(trial.limitMicros)} trial credit left, for g1t's models and sandboxes.`
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent779 : `This workspace gets ${dollars(trial.limitMicros)} of trial credit, for g1t's models and sandboxes, the first time g1t works here.`,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look780 to: workspace ? `/${workspace}/-/integrations` : null,
781 action: "Connect",
782 }
783 : canRunAgents
784 ? {
785 done: Boolean(shell?.free) || shell?.limit == null || shell.limit.comped || shell.limit.state === "ok",
786 title: "Keep work running",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms787 about:
788 "Usage is charged after it runs. Add a card under Billing, so g1t charges it as you near your limit instead of stopping work.",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look789 to: workspace ? `/${workspace}/-/billing` : null,
790 action: "Billing",
791 }
792 : {
793 done: false,
794 title: "Connect a model",
795 about: `${
796 trialClosed(trial, workspace ?? "This workspace") ? `${trialClosed(trial, workspace ?? "This workspace")} ` : ""
797 }Agents need a model to think with. Connect your Anthropic or OpenAI key, or any compatible endpoint.`,
798 to: workspace ? `/${workspace}/-/integrations` : null,
799 action: "Connect",
800 },
801 {
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms802 done: loaded.handedOff,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look803 title: "Hand off an outcome",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms804 about:
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent805 "Open an issue and assign it to g1t, or write an outcome on a project's Plans page and let a planner split it into issues.",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms806 to: repos[0] ? `/${repos[0].namespace}/${repos[0].name}/plans` : null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look807 action: "Write one",
808 },
809 ];
810 const starting = steps.some((step) => !step.done);
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms811 const noProjects = repos.length === 0;
812
813 const rows = tab === "needs" ? sortRows(needs, sort) : tab === "waiting" ? sortRows(waiting, sort) : sortRows(landed, sort);
814 const shown = all ? rows : rows.slice(0, ROWS);
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar815 // Of the agents' own changes only: people's work is not theirs to land.
816 const share = week.agentChanges > 0 ? week.byAgents / week.agentChanges : null;
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms817 const delta = change(week.total, week.previous);
818 const feed = everyActivity ? groups : groups.slice(0, 8);
819
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step820 // "Put an agent on it" first, with "New issue" beside it as before: one
821 // split control, the agent the main way in.
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms822 const newIssue =
823 repos.length > 0 ? (
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step824 <div className="flex items-stretch">
825 <AgentComposer
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)826 action={pathname}
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step827 repos={repos}
828 open={params.get("agent") === "new" || delegated != null}
829 result={delegated}
830 note={
831 canRunAgents ? null : (
832 <>
833 Agents need a model first.{" "}
834 {workspace && (
835 <Link to={`/${workspace}/-/integrations`} className="font-medium text-fg hover:underline">
836 Connect one
837 </Link>
838 )}
839 . The issue still opens.
840 </>
841 )
842 }
843 >
844 <span className="inline-flex cursor-pointer items-center gap-1.5 rounded-l-md border border-line-strong bg-raised px-3 py-2 text-sm font-medium text-fg transition-colors hover:bg-line/60 group-open/composer:bg-line/60">
Merge branch 'worktree-agent-ad7c6d88d93adc817'845 <Sparkles size={14} className="text-accent" /> Put an agent on it
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step846 </span>
847 </AgentComposer>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms848 <DropdownMenu>
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step849 <DropdownMenuTrigger className="-ml-px inline-flex items-center gap-1.5 rounded-r-md border border-line-strong px-3 py-2 text-sm font-medium text-fg/90 transition-colors hover:bg-raised hover:text-fg">
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms850 <Plus size={14} /> New issue
851 </DropdownMenuTrigger>
852 <DropdownMenuContent align="end" className="max-h-80 overflow-y-auto">
853 <DropdownMenuLabel>In which project?</DropdownMenuLabel>
854 {repos.map((repo) => (
855 <DropdownMenuItem key={repo.name} asChild>
856 <Link to={`/${repo.namespace}/${repo.name}/issues/new`}>
857 <Avatar name={repo.name} size={18} square />
858 {repo.name}
859 </Link>
860 </DropdownMenuItem>
861 ))}
862 </DropdownMenuContent>
863 </DropdownMenu>
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step864 </div>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms865 ) : (
866 <Link
867 to={workspace ? `/new?workspace=${workspace}` : "/new"}
868 className="inline-flex items-center gap-1.5 rounded-md border border-line-strong px-3 py-2 text-sm font-medium text-fg/90 hover:bg-raised hover:text-fg"
869 >
870 <Plus size={14} /> New project
871 </Link>
872 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look873
874 return (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms875 <main className="mx-auto max-w-7xl space-y-6 px-4 py-8 sm:px-6 sm:py-10">
876 <header className="flex flex-wrap items-end justify-between gap-x-6 gap-y-4">
877 <div className="min-w-0">
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)878 <h1 className="text-2xl leading-tight font-semibold tracking-tight" suppressHydrationWarning>
879 {variant === "code" ? "Overview" : `${greeting}, ${loaded.name}`}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look880 </h1>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms881 <p className="mt-1.5 text-sm text-muted" suppressHydrationWarning>
882 <span className="text-fg-soft">{date}</span> · {loaded.summary}
883 </p>
884 </div>
885 </header>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look886
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)887 {/* The ways to start work. The Agent box itself is on Home. */}
888 <section aria-label="Start work" className="flex flex-wrap items-center gap-2">
Mission control: a place to ask g1t, shown and not on yet, with the actions under it889 {newIssue}
890 {repos.length > 0 && (
891 <DropdownMenu>
892 <DropdownMenuTrigger className="inline-flex items-center gap-1.5 rounded-md border border-line-strong px-3 py-2 text-sm font-medium text-fg/90 transition-colors hover:bg-raised hover:text-fg">
893 <ListTree size={14} /> Plan work
894 </DropdownMenuTrigger>
895 <DropdownMenuContent align="start" className="max-h-80 overflow-y-auto">
896 <DropdownMenuLabel>Plan in which project?</DropdownMenuLabel>
897 {repos.map((repo) => (
898 <DropdownMenuItem key={repo.name} asChild>
899 <Link to={`/${repo.namespace}/${repo.name}/plans`}>
900 <Avatar name={repo.name} size={18} square />
901 {repo.name}
902 </Link>
903 </DropdownMenuItem>
904 ))}
905 </DropdownMenuContent>
906 </DropdownMenu>
907 )}
908 {needs.length > 0 && (
909 <Link
910 to={`${link({ tab: "needs", all: null })}#work`}
911 preventScrollReset
Merge branch 'worktree-agent-ad7c6d88d93adc817'912 className="inline-flex items-center gap-1.5 rounded-md bg-accent px-3.5 py-2 text-sm font-semibold text-bg transition-colors hover:bg-accent-hover"
Mission control: a place to ask g1t, shown and not on yet, with the actions under it913 >
914 Review {needs.length} that need{needs.length === 1 ? "s" : ""} you <ArrowRight size={14} />
915 </Link>
916 )}
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)917 </section>
Mission control: a place to ask g1t, shown and not on yet, with the actions under it918
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms919 {starting && <GetStarted steps={steps} />}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look920
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms921 <section
922 aria-label="At a glance"
923 className="grid grid-cols-2 gap-px overflow-hidden rounded-xl border border-line bg-line lg:grid-cols-5"
924 >
925 <Stat
926 label="Projects"
927 value={stats.projects == null ? "—" : String(stats.projects)}
928 hint={
929 stats.projectsThisMonth == null
930 ? "in this workspace"
931 : stats.projectsThisMonth > 0
932 ? `+${stats.projectsThisMonth} this month`
933 : "none new this month"
934 }
935 />
936 <Stat
937 label="Agents"
938 value={loaded.runsLoaded || !workspace ? String(liveTotal) : "—"}
Merge branch 'worktree-agent-ad7c6d88d93adc817'939 dot={liveTotal > 0 ? "bg-success animate-pulse" : undefined}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms940 hint={
941 liveTotal > 0
942 ? `live now · ${stats.agentHours < 10 ? stats.agentHours.toFixed(1) : Math.round(stats.agentHours)}h this week`
943 : stats.agentHours > 0
944 ? `none live · ${stats.agentHours < 10 ? stats.agentHours.toFixed(1) : Math.round(stats.agentHours)}h this week`
945 : "none live now"
946 }
947 />
948 <Stat
949 label="Changes this week"
950 value={loaded.perRepoLoaded ? String(week.total) : "—"}
951 hint={
952 delta != null ? `${signedPercent(delta)} vs last week` : week.previous === 0 ? "none the week before" : "merged pull requests"
953 }
954 />
955 <Stat
956 label="Landed without you"
Merge branch 'worktree-agent-ad7c6d88d93adc817'957 dot="bg-accent"
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms958 value={share == null ? "—" : `${Math.round(share * 100)}%`}
Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar959 hint={share == null ? "no agent changes yet" : `${week.byAgents} of ${week.agentChanges} agent changes`}
960 title="Of the changes agents wrote, those g1t merged by auto-merge or the merge queue, with no person pressing merge. People's own changes are not counted."
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms961 />
962 <div className="col-span-2 lg:col-span-1">
963 <Stat
Usage, Billing settings and prepaid AI credit; fixes from the UX audit964 label="Needs you"
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms965 dot="bg-warn"
966 value={String(needs.length)}
967 hint={blocking > 0 ? `${blocking} blocking` : needs.length > 0 ? "nothing blocking" : "all clear"}
968 />
969 </div>
970 </section>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look971
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms972 <div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_21rem] xl:grid-cols-[minmax(0,1fr)_23rem]">
973 <section id="work" className="min-w-0 scroll-mt-20 self-start overflow-hidden rounded-xl border border-line bg-surface">
974 <div className="flex items-center gap-2 border-b border-line px-2 sm:px-3">
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)975 <nav className="-mb-px flex min-w-0 grow gap-1 overflow-x-auto [scrollbar-width:none]" aria-label="Overview">
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms976 {(["needs", "waiting", "landed"] as const).map((value) => (
977 <Link
978 key={value}
979 to={link({ tab: value, all: null })}
980 preventScrollReset
981 aria-current={tab === value ? "page" : undefined}
982 className={cn(
983 "flex shrink-0 items-center gap-1.5 border-b-2 px-2 py-3 text-sm whitespace-nowrap transition-colors sm:px-2.5",
Merge branch 'worktree-agent-ad7c6d88d93adc817'984 tab === value ? "border-accent font-medium text-fg" : "border-transparent text-muted hover:text-fg",
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms985 )}
986 >
987 <span className="sm:hidden">{TAB_SHORT[value]}</span>
988 <span className="hidden sm:inline">{TAB_LABEL[value]}</span>
989 <span
990 className={cn(
991 "rounded-full px-1.5 text-[0.6875rem] tabular-nums",
992 tab === value && value === "needs" && counts.needs > 0 ? "bg-warn/15 text-warn" : "bg-line text-muted",
993 )}
994 >
995 {counts[value]}
996 </span>
997 </Link>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look998 ))}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms999 </nav>
1000 <DropdownMenu>
1001 <DropdownMenuTrigger
1002 className="inline-flex shrink-0 items-center gap-1.5 rounded-md px-2 py-1.5 text-xs text-muted hover:bg-raised hover:text-fg"
1003 aria-label="Sort"
1004 >
1005 <ArrowDownWideNarrow size={14} />
1006 <span className="hidden sm:inline">{SORT_LABEL[sort]}</span>
1007 </DropdownMenuTrigger>
1008 <DropdownMenuContent align="end">
1009 {(["impact", "newest"] as const).map((value) => (
1010 <DropdownMenuItem key={value} asChild>
1011 <Link to={link({ sort: value === "impact" ? null : value })} preventScrollReset>
1012 {sort === value ? <Check /> : <span className="size-4" />}
1013 {SORT_LABEL[value]}
1014 </Link>
1015 </DropdownMenuItem>
1016 ))}
1017 </DropdownMenuContent>
1018 </DropdownMenu>
1019 </div>
1020
1021 {!loaded.perRepoLoaded && tab !== "waiting" ? (
1022 <div className="p-4">
1023 <Unavailable what={tab === "needs" ? "Some of what needs you" : "What landed"} />
1024 </div>
1025 ) : null}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1026
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1027 {tab === "needs" &&
1028 (needs.length === 0 ? (
1029 noProjects ? (
1030 <Empty
1031 action={
1032 <Link
1033 to={workspace ? `/new?workspace=${workspace}` : "/new"}
1034 className="rounded-md bg-fg px-3 py-1.5 text-sm font-medium text-bg hover:bg-white"
1035 >
1036 Create or import a project
1037 </Link>
1038 }
1039 >
1040 <p className="font-medium text-fg">No projects yet</p>
1041 <p className="mt-1">
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1042 Create a repository or import one. Assign its issues to g1t, and what needs you shows up here.
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1043 </p>
1044 </Empty>
1045 ) : (
1046 <>
1047 <Empty>
1048 <p className="font-medium text-fg">Nothing needs you</p>
1049 <p className="mt-1">
1050 Agents are handling everything.{" "}
1051 {landed.length > 0
1052 ? "Here's what they landed today."
1053 : "Reviews, stopped work and failed deploys show up here first."}
1054 </p>
1055 </Empty>
1056 {landed.length > 0 && (
1057 <div className="border-t border-line">
1058 <List>
1059 {landed.slice(0, ROWS).map((row) => (
1060 <LandedCard key={row.key} row={row} first={false} />
1061 ))}
1062 </List>
1063 </div>
1064 )}
1065 </>
1066 )
1067 ) : (
1068 <List>
1069 {(shown as NeedRow[]).map((row, index) => (
1070 <NeedCard key={row.key} row={row} first={index === 0} />
1071 ))}
1072 </List>
1073 ))}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1074
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1075 {tab === "waiting" &&
1076 (!loaded.runsLoaded && workspace ? (
1077 <div className="p-4">
1078 <Unavailable what="Agent runs" />
1079 </div>
1080 ) : waiting.length === 0 ? (
1081 <Empty>
1082 <p className="font-medium text-fg">No agent is at work right now</p>
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1083 <p className="mt-1">Assign an issue to g1t and one starts on it in seconds. Its run shows here while it works.</p>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1084 </Empty>
1085 ) : (
1086 <List>
1087 {(shown as WaitingRow[]).map((row, index) => (
1088 <WaitingCard key={row.key} row={row} first={index === 0} />
1089 ))}
1090 </List>
1091 ))}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1092
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1093 {tab === "landed" &&
1094 loaded.perRepoLoaded &&
1095 (landed.length === 0 ? (
1096 <Empty>
1097 <p className="font-medium text-fg">Nothing has landed today yet</p>
1098 <p className="mt-1">
1099 {week.total > 0
1100 ? `${plural(week.total, "change")} landed in the last 7 days.`
1101 : "Merged pull requests show up here the moment they land."}
1102 </p>
1103 </Empty>
1104 ) : (
1105 <List>
1106 {(shown as LandedRow[]).map((row, index) => (
1107 <LandedCard key={row.key} row={row} first={index === 0} />
1108 ))}
1109 </List>
1110 ))}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1111
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1112 {rows.length > ROWS && (
1113 <div className="flex items-center justify-between border-t border-line px-4 py-2.5 text-xs text-muted sm:px-5">
1114 <span>
1115 Showing {shown.length} of {rows.length}
1116 </span>
1117 <Link
1118 to={link({ all: all ? null : "1" })}
1119 preventScrollReset
1120 className="inline-flex items-center gap-1 font-medium text-fg-soft hover:text-fg"
1121 >
1122 {all ? "Show fewer" : "See all"} <ChevronRight size={12} />
1123 </Link>
1124 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1125 )}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1126 </section>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1127
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1128 <aside className="min-w-0 space-y-6">
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all1129 <InboxNeedsCard items={loaderData.inboxNeeds.items} total={loaderData.inboxNeeds.total} />
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1130 <section className="rounded-xl border border-line bg-surface p-5">
1131 <div className="flex items-center justify-between">
1132 <h2 className="text-base font-semibold tracking-tight">This week</h2>
1133 {workspace && (
1134 <Link to={`/${workspace}/-/agents`} className="text-xs text-muted hover:text-fg">
1135 Fleet
1136 </Link>
1137 )}
1138 </div>
1139 <div className="mt-4">{loaded.perRepoLoaded ? <WeekChart week={week} /> : <Unavailable what="The week" />}</div>
1140 {workspace && stats.weekCost > 0 && (
1141 <Link
1142 to={`/${workspace}/-/usage`}
1143 className="mt-4 flex items-center justify-between border-t border-line pt-3 text-xs text-muted hover:text-fg"
1144 >
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1145 <span>Usage at price this week: {usd(stats.weekCost)}</span>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1146 <ChevronRight size={12} />
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1147 </Link>
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1148 )}
1149 </section>
1150
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix1151 {workspace && (
1152 <TokenUsagePanel
1153 workspace={loaderData.tokens.workspace}
1154 mine={loaderData.tokens.mine}
1155 usageHref={`/${workspace}/-/usage`}
1156 />
1157 )}
1158
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1159 <section id="activity" className="scroll-mt-20 rounded-xl border border-line bg-surface p-5">
1160 <div className="flex items-center justify-between">
1161 <h2 className="text-base font-semibold tracking-tight">Activity</h2>
1162 {groups.length > 8 && (
1163 <Link
1164 to={`${link({ activity: everyActivity ? null : "all" })}#activity`}
1165 preventScrollReset
1166 className="inline-flex items-center gap-0.5 text-xs text-muted hover:text-fg"
1167 >
1168 {everyActivity ? "Less" : "All activity"} <ChevronRight size={12} />
1169 </Link>
1170 )}
1171 </div>
1172 <p className="mt-1 flex items-center gap-3 text-[0.6875rem] text-faint">
1173 <span className="inline-flex items-center gap-1.5">
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1174 <Avatar name="g1t" size={12} /> agents
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1175 </span>
1176 <span className="inline-flex items-center gap-1.5">
1177 <Avatar name={viewer.username} size={12} /> people
1178 </span>
1179 </p>
1180 {!loaded.perRepoLoaded ? (
1181 <div className="mt-3">
1182 <Unavailable what="Activity" />
1183 </div>
1184 ) : feed.length === 0 ? (
1185 <p className="mt-3 text-sm leading-6 text-muted">
1186 Nothing has moved yet. Merges, deploys, checks, reviews and what agents learn show up here.
1187 </p>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1188 ) : (
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1189 <ol className="mt-2 divide-y divide-line/70">
1190 {feed.map((group) => (
1191 <FeedLine key={group.id} group={group} titles={titles} />
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1192 ))}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1193 </ol>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1194 )}
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1195 </section>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1196
Mission control shows where you are needed and what agents landed without you; git answers in about 200ms1197 <section className="rounded-xl border border-line bg-surface p-5">
1198 <h2 className="text-sm font-semibold">Connect your own agent</h2>
1199 <p className="mt-1.5 text-xs leading-5 text-muted">
1200 Add g1t to your coding agent. It signs in through your browser; there is no token to copy.
1201 </p>
1202 <AgentSetup className="mt-3" />
1203 <Link
1204 to="https://docs.g1t.sh/guides/bring-your-own-agent/"
1205 className="mt-3 inline-flex items-center gap-1 text-xs text-muted hover:text-fg"
1206 >
1207 How it works <ArrowRight size={12} />
1208 </Link>
1209 </section>
1210 </aside>
1211 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1212 </main>
1213 );
1214}

This file's history is long; its oldest lines are credited to the oldest commit read.