Skip to content
1,249 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.

Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1import {
2 Activity as ActivityIcon,
3 ArrowDownLeft,
4 ArrowRight,
5 ArrowUpRight,
6 Bot,
7 Brain,
8 Code2,
9 GitBranch,
10 GitCommitHorizontal,
11 GitMerge,
12 Hand,
13 HeartPulse,
14 Loader2,
15 Lock,
16 Network,
A project is an app or a library: libraries show their package and how to ship a release, not production17 Package as PackageGlyph,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API18 Pin,
19 Rocket,
20 RotateCw,
21} from "lucide-react";
Fast pages, required checks on the branch, self-hosted runners, honest incidents22import { type ReactNode, Suspense } from "react";
Merge branch 'worktree-agent-a985c01f43538af01'23import { Await, Form, Link } from "react-router";
Projects: what a workspace builds and runs, first on every page24
A project is an app or a library: libraries show their package and how to ship a release, not production25import {
26 type AgentRun,
27 type G1tEvent,
28 type Memory,
29 type PackageSummary,
30 type Project,
31 type Pull,
32 RUN_KIND_LABEL,
33 isActiveRun,
34 workOwner,
35} from "@g1t/contracts";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API36
Projects: what a workspace builds and runs, first on every page37import type { Route } from "./+types/overview";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit38import { InlineMarkdown } from "../../components/inline-markdown";
Projects: what a workspace builds and runs, first on every page39import { host, StatusDot } from "../../components/deploy";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API40import { CheckBadge } from "../../components/checks";
41import { Elapsed, formatCost, useLiveRefresh } from "../../components/agents";
42import { ActivityFeed, DeployStrip, Meter, NeedsList, Panel, Quiet, Unavailable, percent } from "../../components/mission";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look43import { type ActiveBranch, ActiveBranches } from "../../components/branches";
44import { ProductionChecklist } from "../../components/checklist";
A project is an app or a library: libraries show their package and how to ship a release, not production45import { PackageIcon } from "../../components/package-icon";
Merge branch 'worktree-agent-aca4790ea223aa235'46import { Loading, Skeleton, SkeletonRows } from "../../components/ui/skeleton";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look47import { ProductionShot } from "../../components/production-shot";
48import { GithubLinkStrip } from "../../components/github";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit49import { distinctFacts } from "../../lib/memory-facts";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look50import { githubApp } from "../../lib/github.server";
Merge branch 'worktree-agent-a985c01f43538af01'51import { Avatar, ButtonLink, CopyLine, SubmitButton, TimeAgo } from "../../components/ui";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API52import { ChangeSize } from "../../components/work";
53import {
54 type ActivityItem,
55 type Need,
56 type PipelineStage,
57 PIPELINE,
58 TIME,
59 ageBuckets,
60 eventItem,
61 firstPassRate,
62 groupActivity,
63 passRate,
64 pipelineStage,
65 queuedNumbers,
66 rankNeeds,
67 stuckMinutes,
68} from "../../lib/mission";
A project is an app or a library: libraries show their package and how to ship a release, not production69import { agentWasAssigned, hasInstructions, productionChecklist, releaseChecklist } from "../../lib/checklist";
70import { ECOSYSTEM_LABEL, installCommands } from "../../lib/packages";
71import { PUBLISH_GUIDES, hasRelease, libraryPackages, packageName, packagePath, publishGuide } from "../../lib/project-kind";
72import { actions, agents, deployments, events as eventLog, packages, projects, repos, work } from "../../lib/services.server";
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent73import { madeByG1t } from "../../lib/opened-by";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look74import { assertSameOrigin, getViewer, requireUser } from "../../lib/session.server";
Fast pages, required checks on the branch, self-hosted runners, honest incidents75import { accessTo, countsFor, refusal, repoFor } from "../../lib/access.server";
Production thumbnails follow each deploy, not each commit, and old ones are cleared daily76import { shotVersion } from "./production-screenshot";
Merge branch 'worktree-agent-aaf03bdceac799c89'77import { cloneUrl, useAddresses } from "../../lib/addresses";
Branches and Tags pages, each file's last commit, and the branch menu on files78import { readBranches } from "../../lib/branches.server";
Projects: what a workspace builds and runs, first on every page79
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API80const MAX_LANDED = 6;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look81/** Branches read for the Active branches list, and shown. */
82const BRANCHES_READ = 10;
83const BRANCHES_SHOWN = 5;
A project is an app or a library: libraries show their package and how to ship a release, not production84/** A library's packages shown on its overview; the rest are a link away. */
85const PACKAGES_SHOWN = 3;
Projects: what a workspace builds and runs, first on every page86
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily87/**
88 * The overview streams: the layout's header and tabs (one repository
89 * lookup, one project) go out at once with a skeleton below them, and
90 * everything here, seventeen calls across six services, follows in the
91 * same response as it settles. Crawlers wait for all of it
92 * (entry.server.tsx). Before, the first byte waited on the slowest of them.
93 */
94export function loader({ params, context }: Route.LoaderArgs) {
95 return { overview: overviewData({ params, context }) };
96}
97
98async function overviewData({ params, context }: Pick<Route.LoaderArgs, "params" | "context">) {
Projects: what a workspace builds and runs, first on every page99 const viewer = getViewer(context);
100 const path = { namespace: params.owner, name: params.repo };
101 const ref = { workspace: params.owner, slug: params.repo };
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API102 const now = Date.now();
103 // A section whose service fails shows its own empty state; the page stays up.
104 const soft = <T,>(promise: Promise<T> | null): Promise<T | null> =>
105 promise ? promise.catch((error) => (console.warn("overview:", error), null)) : Promise.resolve(null);
Fast pages, required checks on the branch, self-hosted runners, honest incidents106 // People with a role of their own here see its running parts: deployments,
107 // memory, setup; everyone who can read it sees the rest. Not awaited: only
108 // those sections wait on the repository lookup, which the layout makes in
109 // this request too.
110 const accessP = accessTo(context, params);
111 const memberP = accessP.then((access) => access.insider);
112 const forMembers = <T,>(start: () => Promise<T>): Promise<T | null> =>
113 memberP.then((member) => (member ? soft(start()) : null));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look114 const repoP = soft(repoFor(context, params));
A project is an app or a library: libraries show their package and how to ship a release, not production115 const projectP = soft(projects.get(params.owner, params.repo, viewer));
116 // A library shows its packages where an app shows production, and its
117 // checklist counts a workflow and a release instead of deploys.
118 const libraryRepoP = Promise.all([projectP, repoP]).then(([project, repo]) =>
119 project?.ok && project.value.kind === "library" && repo?.ok ? repo.value : null,
120 );
121 const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null));
122 const workflowsP = libraryRepoP.then((repo) => (repo ? forMembers(() => actions.workflows(path, viewer)) : null));
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API123 const eventsP = repoP.then((repo) => (repo?.ok ? soft(eventLog.list({ repoId: repo.value.id, limit: 150 })) : null));
124 const minePullsP = repoP.then((repo) =>
125 repo?.ok && viewer
126 ? soft(work.listActivePulls(viewer)).then((list) => (list ?? []).filter((item) => item.pull.repoId === repo.value.id))
127 : null,
128 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look129 const openP = soft(work.listPulls(path, viewer, "open"));
130 // Where its code came from on GitHub, for members.
Fast pages, required checks on the branch, self-hosted runners, honest incidents131 const githubP = repoP.then((repo) => (repo?.ok ? forMembers(() => githubApp.link(repo.value.id)) : null));
132 const listP = forMembers(() => deployments.list(ref, viewer));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look133 // Active branches: the newest few besides the default, with how far each
134 // has moved. Branches with an open pull request are read first.
135 const branchesP = Promise.all([repoP, soft(repos.branches(path, viewer)), openP, listP]).then(
136 async ([repo, branchList, pulls, deploys]): Promise<{ main: string; total: number; shown: ActiveBranch[] } | null> => {
137 if (!repo?.ok || !branchList?.ok) return null;
138 const main = repo.value.defaultBranch;
Branches and Tags pages, each file's last commit, and the branch menu on files139 if (branchList.value.every((branch) => branch.name === main)) return { main, total: 0, shown: [] };
140 const read = await readBranches(
141 path,
142 viewer,
143 {
144 defaultBranch: main,
145 branches: branchList.value,
146 pulls: pulls?.ok ? pulls.value : [],
147 previews: deploys?.ok ? deploys.value.live.filter((app) => app.kind === "preview") : [],
148 },
149 BRANCHES_READ,
150 );
151 return { main, total: read.total, shown: read.shown.slice(0, BRANCHES_SHOWN) };
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look152 },
153 );
Fast pages, required checks on the branch, self-hosted runners, honest incidents154 // Active branches read several logs each: streamed, so the rest shows first.
155 const branches = branchesP.catch(() => null);
A project is an app or a library: libraries show their package and how to ship a release, not production156 const [{ insider: member, can }, project, settings, list, open, closed, log, counts, deps, runs, queue, issues, memories, recent, mine, domains, root, packageList, workflows] = await Promise.all([
Fast pages, required checks on the branch, self-hosted runners, honest incidents157 accessP,
A project is an app or a library: libraries show their package and how to ship a release, not production158 projectP,
Fast pages, required checks on the branch, self-hosted runners, honest incidents159 forMembers(() => deployments.settings(ref, viewer)),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look160 listP,
161 openP,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API162 soft(work.listPulls(path, viewer, "closed")),
163 soft(repos.log(path, viewer, null, 1)),
Fast pages, required checks on the branch, self-hosted runners, honest incidents164 soft(countsFor(context, params)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API165 soft(projects.dependencies(params.owner, params.repo, viewer)),
166 soft(agents.listRuns(viewer, { repo: path, limit: 60 })),
167 soft(work.queue(path, viewer)),
168 soft(work.listIssues(path, viewer, { state: "open" })),
Fast pages, required checks on the branch, self-hosted runners, honest incidents169 forMembers(() => agents.listMemories(viewer, params.owner, path)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API170 eventsP,
171 minePullsP,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look172 // For the checklist, which only members see.
Fast pages, required checks on the branch, self-hosted runners, honest incidents173 forMembers(() => deployments.domains(ref, viewer)),
174 forMembers(() => repos.tree(path, viewer, null, "")),
A project is an app or a library: libraries show their package and how to ship a release, not production175 packagesP,
176 workflowsP,
Projects: what a workspace builds and runs, first on every page177 ]);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API178 const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
179
180 const openPulls = ok(open) ?? [];
181 const runList = ok(runs) ?? [];
182 const live = runList.filter((run) => isActiveRun(run.status));
183 const builds = ok(list)?.deployments ?? [];
184 const landed = (ok(closed) ?? [])
185 .filter((pull) => pull.status === "merged")
186 .sort((a, b) => Date.parse(b.mergedAt ?? b.updatedAt) - Date.parse(a.mergedAt ?? a.updatedAt));
187 const queued = queuedNumbers(ok(queue)?.active ?? []);
188 const runOn = new Map<number, AgentRun>();
189 for (const run of live) if (run.number != null && !runOn.has(run.number)) runOn.set(run.number, run);
190 const lifecycleOf = new Map((mine ?? []).map((item) => [item.pull.number, item.lifecycle]));
191
192 // --- The pipeline -----------------------------------------------------------
193 type Card = {
194 number: number;
195 title: string;
196 agent: string;
197 status: Pull["status"];
198 checkStatus: Pull["checkStatus"];
199 step: string | null;
200 runKind: string | null;
201 runStarted: string | null;
202 needsYou: boolean;
203 at: string;
204 };
205 const columns: Record<PipelineStage, Card[]> = { working: [], checking: [], reviewing: [], queue: [], landed: [] };
206 for (const pull of openPulls.slice(0, 40)) {
207 const run = runOn.get(pull.number);
Fast pages, required checks on the branch, self-hosted runners, honest incidents208 columns[pipelineStage(pull, run, queued, lifecycleOf.get(pull.number))].push({
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API209 number: pull.number,
210 title: pull.title,
211 agent: pull.agent,
212 status: pull.status,
213 checkStatus: pull.checkStatus,
214 step: run?.step ?? null,
215 runKind: run ? RUN_KIND_LABEL[run.kind] : null,
216 runStarted: run ? (run.startedAt ?? run.createdAt) : null,
217 needsYou: lifecycleOf.get(pull.number)?.stage === "needs_you",
218 at: pull.updatedAt,
219 });
220 }
221 for (const pull of landed.filter((p) => now - Date.parse(p.mergedAt ?? p.updatedAt) < 3 * TIME.DAY).slice(0, 5)) {
222 columns.landed.push({
223 number: pull.number,
224 title: pull.title,
225 agent: pull.agent,
226 status: pull.status,
227 checkStatus: pull.checkStatus,
228 step: null,
229 runKind: null,
230 runStarted: null,
231 needsYou: false,
232 at: pull.mergedAt ?? pull.updatedAt,
233 });
234 }
235
236 // --- Needs you ---------------------------------------------------------------
237 const base = `/${params.owner}/${params.repo}`;
238 const needs: Need[] = [];
239 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
240 if (latestProduction?.status === "failed") {
241 needs.push({
242 key: "deploy",
243 kind: "deploy",
244 title: "The production build failed",
245 detail: latestProduction.error ?? "Production still serves the build before it.",
246 to: `${base}/deployments/${latestProduction.id}`,
247 action: "See the build",
248 at: Date.parse(latestProduction.finishedAt ?? latestProduction.createdAt),
249 where: latestProduction.commit.slice(0, 7),
250 });
251 }
252 const me = viewer?.username.toLowerCase();
253 for (const pull of openPulls) {
254 const to = `${base}/pull/${pull.number}`;
255 const where = `#${pull.number}`;
256 const lifecycle = lifecycleOf.get(pull.number);
257 if (lifecycle?.stage === "needs_you") {
258 const conflict = /conflict/i.test(lifecycle.detail);
259 needs.push({ key: `pull:${pull.number}`, kind: conflict ? "conflict" : "stalled", title: pull.title, detail: lifecycle.detail, to, action: conflict ? "Resolve" : "Decide", at: Date.parse(pull.updatedAt), where });
260 } else if (lifecycle?.stage === "ready") {
261 needs.push({ key: `pull:${pull.number}`, kind: "ready", title: pull.title, detail: "Ready to land when you merge it.", to, action: "Merge", at: Date.parse(pull.updatedAt), where });
262 }
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights263 // Never your own, nor one g1t made for you; whoever it is for asked.
264 const owner = workOwner(pull).username;
265 if (me && pull.status === "open" && owner.toLowerCase() !== me && pull.reviewers.some((name) => name.toLowerCase() === me)) {
266 needs.push({ key: `review:${pull.number}`, kind: "review", title: pull.title, detail: `${owner} asked for your review.`, to, action: "Review", at: Date.parse(pull.updatedAt), where });
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API267 }
268 if (member && pull.status === "open" && pull.checkStatus === "failed" && !runOn.has(pull.number) && !lifecycle) {
Fast pages, required checks on the branch, self-hosted runners, honest incidents269 needs.push({ key: `checks:${pull.number}`, kind: "checks", title: pull.title, detail: "It failed in the merge queue and no agent is fixing it.", to, action: "See checks", at: Date.parse(pull.updatedAt), where });
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API270 }
271 }
272 for (const run of live) {
273 const minutes = stuckMinutes(run, now);
274 if (minutes == null) continue;
275 needs.push({
276 key: `run:${run.id}`,
277 kind: "stuck",
278 title: run.title ?? `${run.agent}'s run`,
279 detail: `${run.agent} has reported nothing for ${minutes} min${run.step ? `. Last: ${run.step}` : ""}.`,
280 to: `${base}/agents/runs/${run.id}`,
281 action: "Look",
282 at: Date.parse(run.updatedAt),
283 where: run.number != null ? `#${run.number}` : null,
284 });
285 }
286
287 // --- Activity and health -------------------------------------------------------
288 const eventList: G1tEvent[] = recent ?? [];
289 const items = eventList
290 .map((event) => eventItem(event, path))
291 .filter((item): item is ActivityItem => item != null);
292 const checkEvents = eventList.flatMap((event) =>
293 event.type === "checks.completed" ? [{ repo: "", number: event.data.number, at: Date.parse(event.time), passed: event.data.status === "passed" }] : [],
294 );
295 const memoryList = ok(memories)?.project ?? [];
Usage, Billing settings and prepaid AI credit; fixes from the UX audit296 // Pinned first, then the newest; a fact remembered twice, once.
297 const knows = distinctFacts([...memoryList.filter((m) => m.pinned), ...memoryList.filter((m) => !m.pinned).sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt))]).slice(0, 5);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API298 const openIssues = ok(issues);
299
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look300 // --- Getting to production ----------------------------------------------------
301 const wentLive = (status: string) => status === "ready" || status === "replaced" || status === "down";
302 const liveApps = ok(list)?.live ?? [];
303 const commitNow = ok(log)?.[0] ?? null;
304 const projectValue = ok(project);
A project is an app or a library: libraries show their package and how to ship a release, not production305 const isLibrary = projectValue?.kind === "library";
306 const published = libraryPackages(ok(packageList) ?? []);
307 const firstSteps = {
308 base,
309 hasCode: commitNow != null || projectValue?.source.kind === "mirror",
310 instructions: commitNow == null ? false : root?.ok ? hasInstructions(root.value.entries.map((entry) => entry.name)) : null,
311 agentAssigned: agentWasAssigned({
312 runAgents: runList.map((run) => run.agent),
313 pullAgents: [...openPulls, ...(ok(closed) ?? [])].map((pull) => pull.agent),
314 issues: openIssues ?? [],
315 }),
316 };
317 const checklist = !member
318 ? null
319 : isLibrary
320 ? releaseChecklist({
321 ...firstSteps,
322 hasWorkflow: workflows?.ok ? workflows.value.length > 0 : null,
323 released: hasRelease(published),
324 releaseTo: published[0]
325 ? packagePath(published[0])
326 : (publishGuide(projectValue?.ecosystem ?? null)?.guide ?? "https://docs.g1t.sh/guides/packages/"),
327 })
328 : productionChecklist({
329 ...firstSteps,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look330 deploysEnabled: ok(settings)?.enabled ?? false,
331 productionDeployed:
332 liveApps.some((app) => app.kind === "production") ||
333 builds.some((build) => build.kind === "production" && wentLive(build.status)),
334 domains: ok(domains)?.domains.length ?? null,
335 previewOpened:
336 liveApps.some((app) => app.kind === "preview") || builds.some((build) => build.kind === "preview" && wentLive(build.status)),
A project is an app or a library: libraries show their package and how to ship a release, not production337 });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look338
339 const github = await githubP;
Projects: what a workspace builds and runs, first on every page340 return {
341 member,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look342 can,
343 github,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API344 project: ok(project),
345 settings: ok(settings),
346 builds: builds.slice(0, 30),
347 live: ok(list)?.live ?? [],
348 commit: ok(log)?.[0] ?? null,
349 open: ok(counts) ?? { issues: openIssues?.length ?? 0, pulls: openPulls.length },
350 dependencies: ok(deps),
351 agentsLive: live.slice(0, 6),
352 runsLoaded: runs?.ok ?? false,
353 pullsLoaded: open?.ok ?? false,
354 columns,
355 needs: rankNeeds(needs),
356 landed: landed.slice(0, MAX_LANDED),
357 groups: groupActivity(items).slice(0, 30),
358 eventsLoaded: recent != null,
359 health: {
360 passRate: passRate(checkEvents.map((c) => c.passed)),
361 firstPass: firstPassRate(checkEvents),
362 checkRuns: checkEvents.length,
363 ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null,
364 },
365 knows: knows as Memory[],
366 memoryCount: memoryList.length,
367 memoriesLoaded: memories?.ok ?? false,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look368 checklist,
A project is an app or a library: libraries show their package and how to ship a release, not production369 library: isLibrary
370 ? { packages: published.slice(0, PACKAGES_SHOWN), total: published.length, loaded: packageList?.ok ?? false }
371 : null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look372 branches,
Projects: what a workspace builds and runs, first on every page373 };
374}
375
376export async function action({ request, params, context }: Route.ActionArgs) {
377 assertSameOrigin(request);
378 const user = requireUser(context, request);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look379 const form = await request.formData().catch(() => null);
380 const intent = form?.get("intent");
381 // Syncing from GitHub is pushing; stopping it is an integration; deploying is compute.
382 const refused = await refusal(context, params, intent === "github-sync" ? "push" : intent === "github-stop" ? "manage_integrations" : "run");
383 if (refused) return { error: refused };
384 if (intent === "github-sync" || intent === "github-stop") {
385 const repo = await repos.get({ namespace: params.owner, name: params.repo }, user);
386 if (!repo.ok) return { error: repo.error.message };
387 const done =
388 intent === "github-sync" ? await githubApp.sync(user, repo.value.id) : await githubApp.unlinkRepo(user, repo.value.id);
389 return done.ok
390 ? { notice: intent === "github-sync" ? "Synced with GitHub." : "It is no longer kept in step with GitHub." }
391 : { error: done.error.message };
392 }
Projects: what a workspace builds and runs, first on every page393 const started = await deployments.redeploy(user, { workspace: params.owner, slug: params.repo }, null);
394 return started.ok ? { notice: "Production is building." } : { error: started.error.message };
395}
396
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily397type Loaded = Awaited<ReturnType<typeof overviewData>>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API398type Card = Loaded["columns"]["working"][number];
399
400const STAGE_TONE: Record<PipelineStage, string> = {
401 working: "bg-merged",
402 checking: "bg-info",
403 reviewing: "bg-warn",
404 queue: "bg-accent-dim",
405 landed: "bg-accent",
406};
407
408function PipelineCard({ card, base, stage }: { card: Card; base: string; stage: PipelineStage }) {
409 return (
410 <li>
411 <Link
412 to={`${base}/pull/${card.number}`}
413 prefetch="intent"
414 className={`block rounded-lg border bg-bg/60 p-2.5 transition-colors hover:border-line-strong hover:bg-raised ${
415 card.needsYou ? "border-warn/50" : card.step ? "border-merged/30" : "border-line"
416 }`}
417 >
418 <span className="line-clamp-2 text-[0.8125rem] leading-snug font-medium">{card.title}</span>
419 <span className="mt-1.5 flex items-center gap-1.5 text-[0.6875rem] text-muted">
420 <Avatar name={card.agent} size={14} />
421 <span className="font-mono text-faint">#{card.number}</span>
422 <span className="grow" />
423 {card.needsYou ? (
424 <span className="inline-flex items-center gap-1 text-warn" title="Stopped: a person decides what happens next">
425 <Hand size={11} /> you
426 </span>
427 ) : stage === "landed" ? (
428 <TimeAgo at={card.at} />
429 ) : card.runStarted ? (
430 <span className="inline-flex items-center gap-1 text-merged">
431 <Loader2 size={11} className="animate-spin" />
432 <Elapsed from={card.runStarted} />
433 </span>
434 ) : (
435 <CheckBadge status={card.checkStatus} />
436 )}
437 </span>
438 {card.step && (
439 <span className="mt-1.5 block truncate font-mono text-[0.6875rem] text-fg/70" title={card.step}>
440 {card.runKind}: {card.step}
441 </span>
442 )}
443 </Link>
444 </li>
445 );
446}
447
448/** Open pull requests moving through Working → Checking → Reviewing → Queue → Landed. */
449function Pipeline({ columns, base }: { columns: Loaded["columns"]; base: string }) {
450 return (
451 <div className="relative -mx-4 overflow-x-auto px-4 pb-1 sm:mx-0 sm:px-0">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look452 {/* Each stage as tall as what is in it: an empty one stays short
453 instead of stretching to the busiest. */}
454 <ol className="grid min-w-176 grid-cols-5 items-start gap-2">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API455 {PIPELINE.map(({ stage, label }, index) => {
456 const cards = columns[stage];
457 return (
458 <li key={stage} className="flex min-w-0 flex-col rounded-xl border border-line bg-surface p-2">
459 <p className="flex items-center gap-2 px-1 pt-0.5 pb-2 text-xs font-medium text-muted">
460 <span className={`size-1.5 rounded-full ${STAGE_TONE[stage]} ${cards.length && stage !== "landed" ? "animate-pulse" : ""}`} />
461 {label}
462 <span className="tabular-nums text-faint">{cards.length}</span>
463 {index < PIPELINE.length - 1 && <ArrowRight size={11} className="ml-auto text-line-strong" />}
464 </p>
465 {cards.length === 0 ? (
466 <p className="rounded-lg border border-dashed border-line/70 px-2 py-3 text-center text-[0.6875rem] text-faint">Empty</p>
467 ) : (
468 <ul className="space-y-1.5">
469 {cards.slice(0, 6).map((card) => (
470 <PipelineCard key={card.number} card={card} base={base} stage={stage} />
471 ))}
472 {cards.length > 6 && (
473 <li className="px-1 text-[0.6875rem] text-muted">
474 <Link to={`${base}/pulls`} className="hover:text-fg">
475 {cards.length - 6} more
476 </Link>
477 </li>
478 )}
479 </ul>
480 )}
481 </li>
482 );
483 })}
484 </ol>
485 </div>
486 );
487}
488
A project is an app or a library: libraries show their package and how to ship a release, not production489/**
490 * Where an app's overview shows production, a library's shows what it
491 * publishes: each package with its latest version and how to install it,
492 * or, before there is one, how to publish a first version.
493 */
494function LibraryHead({
495 library,
496 project,
497 base,
498 canChange,
499 workspace,
500}: {
501 library: { packages: PackageSummary[]; total: number; loaded: boolean };
502 project: Project | null;
503 base: string;
504 canChange: boolean;
505 workspace: string;
506}) {
507 const guide = publishGuide(project?.ecosystem ?? null);
508 const name = project?.name ?? base.split("/").pop();
509 return (
510 <div className="p-5 sm:p-6">
511 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
512 <PackageGlyph size={13} className="text-accent" />
513 Packages
514 </p>
515 {library.packages.length > 0 ? (
516 <>
517 <ul className="mt-3 space-y-4">
518 {library.packages.map((pkg) => {
519 const commands = installCommands(pkg, pkg.latest, "you");
520 return (
521 <li key={pkg.id} className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:gap-5">
522 <Link to={packagePath(pkg)} className="group flex min-w-0 items-center gap-3 lg:w-72 lg:shrink-0">
523 <PackageIcon ecosystem={pkg.ecosystem} size={32} />
524 <span className="min-w-0">
525 <span className="block truncate font-mono text-[0.9375rem] font-medium group-hover:text-accent">{packageName(pkg)}</span>
526 <span className="block text-xs text-muted">
527 {ECOSYSTEM_LABEL[pkg.ecosystem]} · {pkg.latest ?? "no versions yet"}
528 {pkg.versions > 1 && ` · ${pkg.versions} versions`}
529 </span>
530 </span>
531 </Link>
532 <div className="min-w-0 grow space-y-1.5">
533 {/* The workspace's registry first, or the tool installs a package of the same name from elsewhere. */}
534 {commands.registry && <CopyLine prompt text={commands.registry} />}
535 <CopyLine prompt text={commands.install} />
536 </div>
537 </li>
538 );
539 })}
540 </ul>
541 {library.total > library.packages.length && (
542 <Link to={`/${workspace}/-/packages`} className="mt-3 inline-block text-xs text-muted hover:text-fg">
543 All {library.total} packages
544 </Link>
545 )}
546 </>
547 ) : (
548 <>
549 <p className="mt-2 text-lg font-medium">{library.loaded ? "Not published yet" : "Packages could not be loaded just now"}</p>
550 {guide ? (
551 <>
552 <p className="mt-1 max-w-lg text-sm text-muted">
553 Publish a first version of {name} to the workspace's {guide.label} registry, for others to install
554 {guide.start ? ":" : "."}
555 </p>
556 {guide.start && (
557 <div className="mt-3 max-w-lg">
558 <CopyLine prompt text={guide.start} />
559 </div>
560 )}
561 <a href={guide.guide} className="mt-3 inline-flex items-center gap-1 text-sm text-accent hover:underline">
562 Publishing to {guide.label} <ArrowUpRight size={13} />
563 </a>
564 </>
565 ) : (
566 <>
567 <p className="mt-1 max-w-lg text-sm text-muted">Publish a first version of {name} to one of the workspace's registries:</p>
568 <p className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm">
569 {PUBLISH_GUIDES.map((one) => (
570 <a key={one.label} href={one.guide} className="inline-flex items-center gap-1 text-accent hover:underline">
571 {one.label} <ArrowUpRight size={13} />
572 </a>
573 ))}
574 </p>
575 </>
576 )}
577 </>
578 )}
579 {canChange && (
580 <p className="mt-4 text-xs text-faint">
581 Deploys?{" "}
582 <Link to={`${base}/settings#deploys`} className="underline-offset-4 hover:text-fg hover:underline">
583 Change it in Settings.
584 </Link>
585 </p>
586 )}
587 </div>
588 );
589}
590
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API591function Stat({ label, value, to }: { label: string; value: ReactNode; to?: string }) {
592 const body = (
593 <>
594 <span className="block text-[0.6875rem] text-faint">{label}</span>
595 <span className="mt-0.5 block truncate text-[0.8125rem] font-medium">{value}</span>
596 </>
597 );
598 return to ? (
599 <Link to={to} className="min-w-0 px-5 py-3 transition-colors hover:bg-raised/50">
600 {body}
601 </Link>
602 ) : (
603 <div className="min-w-0 px-5 py-3">{body}</div>
604 );
605}
606
Projects: what a workspace builds and runs, first on every page607export default function ProjectOverview({ loaderData, actionData, params }: Route.ComponentProps) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily608 return (
609 <Suspense fallback={<OverviewSkeleton />}>
610 <Await resolve={loaderData.overview} errorElement={<OverviewFailed />}>
Merge branch 'worktree-agent-aca4790ea223aa235'611 {(overview) => (
612 // Faded in over its outline, not switched for it.
613 <div className="animate-fade-in">
614 <Overview loaderData={overview} actionData={actionData} params={params} />
615 </div>
616 )}
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily617 </Await>
618 </Suspense>
619 );
620}
621
622/** The overview's sections as they will sit, while they stream in. */
623function OverviewSkeleton() {
624 return (
Merge branch 'worktree-agent-aca4790ea223aa235'625 <Loading className="space-y-8" aria-label="Loading the overview">
626 {/* Production: its screenshot beside where it is live, then the facts under them. */}
627 <section aria-hidden="true" className="overflow-hidden rounded-2xl border border-line bg-surface">
628 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
629 <Skeleton className="aspect-[16/10] w-full shrink-0 rounded-lg sm:w-72" />
630 <div className="min-w-0 grow">
631 <Skeleton className="h-3 w-24" />
632 <Skeleton className="mt-3 h-5 w-72 max-w-full" />
633 <Skeleton className="mt-3 h-3 w-48 max-w-full" />
634 </div>
635 </div>
636 <div className="grid grid-cols-2 gap-px border-t border-line bg-line sm:grid-cols-4">
637 {Array.from({ length: 4 }, (_, index) => (
638 <div key={index} className="bg-surface px-5 py-4">
639 <Skeleton className="h-3 w-16" />
640 <Skeleton className="mt-2.5 h-4 w-28" />
641 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily642 ))}
643 </div>
644 </section>
Merge branch 'worktree-agent-aca4790ea223aa235'645 {/* Right now: the work, column by column. */}
646 <div aria-hidden="true" className="grid grid-cols-2 gap-2 sm:grid-cols-5">
647 {Array.from({ length: 5 }, (_, index) => (
648 <div key={index} className="rounded-xl border border-line bg-surface p-3">
649 <Skeleton className="h-3 w-20" />
650 <Skeleton className="mt-3 h-14 rounded-lg" />
651 </div>
652 ))}
653 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily654 <div className="grid gap-6 lg:grid-cols-2">
655 <section className="rounded-2xl border border-line bg-surface">
656 <SkeletonRows rows={4} />
657 </section>
658 <section className="rounded-2xl border border-line bg-surface">
659 <SkeletonRows rows={4} />
660 </section>
661 </div>
Merge branch 'worktree-agent-aca4790ea223aa235'662 </Loading>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily663 );
664}
665
666function OverviewFailed() {
667 return (
668 <p className="rounded-2xl border border-line bg-surface p-6 text-sm text-muted">
669 The overview could not be loaded just now. Reload the page to try again.
670 </p>
671 );
672}
673
674function Overview({
675 loaderData,
676 actionData,
677 params,
678}: {
679 loaderData: Loaded;
680 actionData: Route.ComponentProps["actionData"];
681 params: Route.ComponentProps["params"];
682}) {
A project is an app or a library: libraries show their package and how to ship a release, not production683 const { member, project, settings, builds, live, commit, open, dependencies, agentsLive, columns, needs, landed, groups, health, knows, checklist, branches, library } =
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look684 loaderData;
Projects: what a workspace builds and runs, first on every page685 const base = `/${params.owner}/${params.repo}`;
686 const production = live.find((app) => app.kind === "production") ?? null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains687 // The project's own domain, once it is active, is where production is visited.
688 const productionUrl = production ? (settings?.primaryDomain ? `https://${settings.primaryDomain}` : production.url) : null;
Projects: what a workspace builds and runs, first on every page689 const previews = live.filter((app) => app.kind === "preview");
690 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
Merge branch 'worktree-agent-aaf03bdceac799c89'691 const addresses = useAddresses();
Projects: what a workspace builds and runs, first on every page692 const source = project?.source.kind === "hosted" ? project.source : null;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API693 const moving = agentsLive.length > 0 || columns.working.length + columns.checking.length > 0;
694 useLiveRefresh(moving);
695 const ages = health.ages;
696 const oldest = ages ? Math.max(1, ...ages.map((a) => a.count)) : 1;
Projects: what a workspace builds and runs, first on every page697
698 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API699 <div className="space-y-8">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look700 {loaderData.github && <GithubLinkStrip link={loaderData.github} />}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API701 {/* What the project is, running, and where its code is. */}
702 <section className="overflow-hidden rounded-2xl border border-line bg-surface">
A project is an app or a library: libraries show their package and how to ship a release, not production703 {library ? (
704 <LibraryHead library={library} project={project} base={base} canChange={loaderData.can.manage_settings} workspace={params.owner} />
705 ) : (
706 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
707 {member && (production || settings?.enabled) && (
708 <ProductionShot
709 src={production ? `${base}/production.jpg?v=${shotVersion(production)}` : null}
710 href={production ? (productionUrl ?? production.url) : null}
711 label={host(production ? (productionUrl ?? production.url) : (settings?.productionUrl ?? ""))}
712 className="w-full shrink-0 sm:w-60 lg:w-72"
713 />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API714 )}
A project is an app or a library: libraries show their package and how to ship a release, not production715 <div className="flex min-w-0 grow flex-wrap items-start justify-between gap-4">
716 <div className="min-w-0">
717 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
718 <Rocket size={13} className="text-accent" />
719 Production
720 </p>
721 {production ? (
722 <>
723 <a
724 href={productionUrl ?? production.url}
725 className="mt-2 flex items-center gap-1.5 truncate font-mono text-lg font-medium hover:text-accent"
726 >
727 {host(productionUrl ?? production.url)}
728 <ArrowUpRight size={16} className="shrink-0 text-faint" />
729 </a>
730 <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted">
731 {latestProduction && <StatusDot status={latestProduction.status} label={latestProduction.status === "ready" ? "Live" : undefined} />}
732 <span className="inline-flex items-center gap-1 font-mono">
733 <GitCommitHorizontal size={13} className="text-faint" />
734 {production.commit.slice(0, 7)}
735 </span>
736 <span>
737 deployed <TimeAgo at={production.deployedAt} />
738 </span>
739 {settings?.primaryDomain && <span className="text-faint">also at {host(production.url)}</span>}
740 </p>
741 </>
742 ) : settings?.enabled ? (
743 <>
744 <p className="mt-2 font-mono text-lg text-muted">{host(settings.productionUrl)}</p>
745 <p className="mt-1.5 text-xs text-muted">
746 {latestProduction ? (
747 <StatusDot status={latestProduction.status} label={latestProduction.status === "failed" ? "The last build failed" : undefined} />
748 ) : (
749 "Not deployed yet. Push to the default branch, or deploy it now."
750 )}
751 </p>
752 </>
753 ) : (
754 <>
755 <p className="mt-2 text-lg font-medium">Deployments are off</p>
756 <p className="mt-1 max-w-lg text-sm text-muted">
757 Nothing builds or runs until you turn them on. Then {project?.name ?? params.repo} goes up on g1t.page:
758 production from the default branch, and a live preview for every pull request. It runs only while
759 someone visits.
760 </p>
761 </>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API762 )}
Projects: what a workspace builds and runs, first on every page763 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production764 {member && (
765 <div className="flex shrink-0 items-center gap-2">
766 {production && (
767 <ButtonLink to={productionUrl ?? production.url} variant="accent" reloadDocument>
768 Visit
769 <ArrowUpRight size={14} />
770 </ButtonLink>
771 )}
772 {settings?.enabled ? (
773 loaderData.can.run && <Form method="post">
Merge branch 'worktree-agent-a985c01f43538af01'774 <SubmitButton
775 variant="quiet"
776 name="intent"
777 value="redeploy"
778 pending="Redeploying…"
779 title="Build production again from the default branch"
780 >
A project is an app or a library: libraries show their package and how to ship a release, not production781 <RotateCw size={14} />
782 Redeploy
Merge branch 'worktree-agent-a985c01f43538af01'783 </SubmitButton>
A project is an app or a library: libraries show their package and how to ship a release, not production784 </Form>
785 ) : loaderData.can.manage_integrations && (
786 <ButtonLink to={`${base}/settings/deployments`} variant="accent">
787 <Rocket size={14} />
788 Turn on deployments
789 </ButtonLink>
790 )}
791 </div>
792 )}
793 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look794 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production795 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API796 {actionData && (
797 <p className={`border-t border-line px-6 py-2.5 text-sm ${"error" in actionData ? "text-danger" : "text-accent"}`}>
798 {"error" in actionData ? actionData.error : actionData.notice}
799 </p>
800 )}
801 <div className="grid grid-cols-2 divide-line border-t border-line text-sm sm:grid-cols-4 sm:divide-x">
802 <Stat
803 label="Source"
804 to={`${base}/code`}
805 value={
806 source ? (
807 <span className="inline-flex items-center gap-1.5 font-mono">
808 {project?.private ? <Lock size={12} className="text-faint" /> : <Code2 size={12} className="text-faint" />}
809 {source.repo.name}
810 {source.rootDir ? `/${source.rootDir}` : ""}
811 </span>
812 ) : (
813 "—"
814 )
815 }
816 />
817 <Stat
818 label="Default branch"
819 to={`${base}/commits`}
820 value={
821 <span className="inline-flex items-center gap-1.5 font-mono">
822 <GitBranch size={12} className="text-faint" />
823 {source?.defaultBranch ?? "main"}
824 </span>
825 }
826 />
827 <Stat
828 label="Latest commit"
829 to={commit ? `${base}/commit/${commit.hash}` : undefined}
830 value={
831 commit ? (
832 <span title={commit.message.split("\n")[0]}>
833 <span className="font-mono">{commit.hash.slice(0, 7)}</span>{" "}
834 <span className="font-normal text-muted">
835 <TimeAgo at={commit.authoredAt} />
836 </span>
837 </span>
838 ) : (
839 "No commits yet"
840 )
841 }
842 />
843 <Stat
A project is an app or a library: libraries show their package and how to ship a release, not production844 label={member && settings?.enabled && !library ? "Last deploy" : "Open"}
845 to={member && settings?.enabled && !library ? `${base}/deployments` : `${base}/pulls`}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API846 value={
A project is an app or a library: libraries show their package and how to ship a release, not production847 member && settings?.enabled && !library ? (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API848 latestProduction ? (
849 <span className="inline-flex items-center gap-2">
850 <StatusDot status={latestProduction.status} />
851 <span className="font-normal text-muted">
852 <TimeAgo at={latestProduction.createdAt} />
Projects: what a workspace builds and runs, first on every page853 </span>
854 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API855 ) : (
856 "None yet"
857 )
858 ) : (
859 `${open.pulls} pull requests · ${open.issues} issues`
860 )
861 }
862 />
863 </div>
864 {commit && (
865 <p className="truncate border-t border-line px-5 py-2.5 text-xs text-muted sm:px-6">
866 <GitCommitHorizontal size={12} className="mr-1.5 inline text-faint" />
867 {commit.message.split("\n")[0]} <span className="text-faint">· {commit.author.name}</span>
868 </p>
869 )}
870 </section>
871
A project is an app or a library: libraries show their package and how to ship a release, not production872 {checklist && <ProductionChecklist base={base} items={checklist} title={library ? "Ship a release" : undefined} />}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look873
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API874 <Panel
875 title="Right now"
876 icon={<span className={`block size-2 rounded-full ${agentsLive.length > 0 ? "animate-pulse bg-merged" : "bg-line-strong"}`} />}
877 count={agentsLive.length}
878 all={{ to: `${base}/agents`, label: "Agents" }}
879 >
880 {agentsLive.length > 0 && (
881 <ul className="mb-3 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
882 {agentsLive.map((run) => (
883 <li key={run.id} className="min-w-0 rounded-xl border border-merged/30 bg-surface px-3.5 py-3">
884 <div className="flex items-center gap-2 text-sm">
885 <Avatar name={run.agent} size={18} />
886 <span className="font-medium">{run.agent}</span>
887 <span className="text-xs text-muted">{RUN_KIND_LABEL[run.kind].toLowerCase()}</span>
888 <span className="grow" />
889 <span className="text-xs text-muted">
890 <Elapsed from={run.startedAt ?? run.createdAt} />
Projects: what a workspace builds and runs, first on every page891 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API892 </div>
893 <Link
894 to={run.number != null ? `${base}/pull/${run.number}` : `${base}/agents/runs/${run.id}`}
895 className="mt-1 block truncate text-[0.8125rem] hover:text-accent"
896 >
897 {run.title ?? "A run"} {run.number != null && <span className="text-faint">#{run.number}</span>}
898 </Link>
899 {run.step && (
900 <p className="mt-1 truncate font-mono text-[0.6875rem] text-fg/70" title={run.step}>
901 <span className="mr-1.5 inline-block size-1.5 animate-pulse rounded-full bg-merged align-middle" />
902 {run.step}
903 </p>
904 )}
905 <p className="mt-1.5 flex items-center gap-3 text-[0.6875rem] text-faint">
906 {formatCost(run.costUsd) && <span>{formatCost(run.costUsd)} so far</span>}
907 <Link to={`${base}/agents/runs/${run.id}`} className="hover:text-fg">
908 {run.stepCount} steps
909 </Link>
910 {run.number != null && (
911 <Link to={`${base}/sessions/${run.number}`} className="hover:text-fg">
912 Session
913 </Link>
914 )}
915 </p>
916 </li>
917 ))}
918 </ul>
919 )}
920 {!loaderData.pullsLoaded ? (
921 <Unavailable what="Pull requests" />
922 ) : Object.values(columns).every((c) => c.length === 0) ? (
923 <Quiet
924 action={
925 member ? (
926 <Link to={`${base}/issues/new`} className="inline-flex items-center gap-1.5 rounded-md bg-fg px-3 py-1.5 text-xs font-medium text-bg hover:bg-white">
927 <Bot size={12} /> Open an issue for an agent
928 </Link>
929 ) : null
930 }
931 >
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent932 Nothing is moving. Open an issue and hand it to g1t, or push a branch and open a pull request.
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API933 </Quiet>
934 ) : (
935 <Pipeline columns={columns} base={base} />
Projects: what a workspace builds and runs, first on every page936 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API937 </Panel>
Projects: what a workspace builds and runs, first on every page938
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API939 <div className="grid gap-x-8 gap-y-8 lg:grid-cols-[minmax(0,1fr)_20rem]">
940 <div className="min-w-0 space-y-8">
941 {needs.length > 0 && (
942 <Panel title="Needs you" icon={<Hand size={14} className="text-warn" />} count={needs.length}>
943 <NeedsList needs={needs} limit={5} />
944 </Panel>
945 )}
946
Fast pages, required checks on the branch, self-hosted runners, honest incidents947 <Suspense
948 fallback={
949 <Panel title="Active branches" icon={<GitBranch size={14} />}>
950 <div aria-busy="true">
951 <SkeletonRows rows={3} rowClassName="h-12" />
952 </div>
953 </Panel>
954 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look955 >
Fast pages, required checks on the branch, self-hosted runners, honest incidents956 <Await resolve={branches}>
957 {(branches) => (
958 <Panel title="Active branches" icon={<GitBranch size={14} />} count={branches?.total || null}>
959 {!branches ? (
960 <Unavailable what="Branches" />
961 ) : branches.shown.length === 0 ? (
962 <Quiet>
963 Only {branches.main} so far. Branches pushed here show with how far each has moved from {branches.main}, and
964 its pull request and preview.
965 </Quiet>
966 ) : (
967 <>
968 <ActiveBranches branches={branches.shown} base={base} main={branches.main} />
969 {branches.total > branches.shown.length && (
970 <p className="mt-2 px-1 text-xs text-faint">
971 The {branches.shown.length} most recently changed of {branches.total} branches.
972 </p>
973 )}
974 </>
975 )}
976 </Panel>
977 )}
978 </Await>
979 </Suspense>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look980
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API981 <Panel title="Recent changes" icon={<GitMerge size={14} />} all={{ to: `${base}/pulls?state=closed`, label: "All landed" }}>
982 {landed.length === 0 ? (
983 <Quiet>Nothing has landed yet. Merged pull requests show here with who made them and why.</Quiet>
Projects: what a workspace builds and runs, first on every page984 ) : (
985 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API986 {landed.map((pull) => {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent987 const byAgent = pull.runtime === "hosted" || madeByG1t(pull);
Projects: what a workspace builds and runs, first on every page988 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API989 <li key={pull.id} className="flex items-start gap-3 px-4 py-3">
990 <GitMerge size={15} className="mt-0.5 shrink-0 text-merged" />
991 <span className="min-w-0 grow">
992 <Link to={`${base}/pull/${pull.number}`} prefetch="intent" className="block truncate text-sm font-medium hover:text-accent">
993 {pull.title}
994 </Link>
995 <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted">
996 <span className="font-mono text-faint">#{pull.number}</span>
997 <span>·</span>
998 <span className="inline-flex items-center gap-1">
999 <Avatar name={pull.agent} size={13} />
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1000 {pull.requestedBy
1001 ? `made by ${pull.author.username} for ${pull.requestedBy.username}`
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1002 : byAgent
1003 ? `made by ${pull.agent}`
1004 : `by ${pull.author.username}`}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1005 </span>
1006 {pull.issue != null && (
1007 <>
1008 <span>· for</span>
1009 <Link to={`${base}/issues/${pull.issue}`} className="hover:text-fg">
1010 #{pull.issue}
1011 </Link>
1012 </>
1013 )}
1014 {pull.mergedBy && <span>· landed by {pull.mergedBy}</span>}
1015 <span>
1016 · <TimeAgo at={pull.mergedAt ?? pull.updatedAt} />
Projects: what a workspace builds and runs, first on every page1017 </span>
1018 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1019 </span>
1020 <span className="hidden shrink-0 text-xs text-faint sm:block">
1021 <ChangeSize files={pull.files} />
1022 </span>
1023 {byAgent && (
1024 <Link to={`${base}/sessions/${pull.number}`} className="shrink-0 text-xs text-muted hover:text-fg">
1025 Session
1026 </Link>
1027 )}
Projects: what a workspace builds and runs, first on every page1028 </li>
1029 );
1030 })}
1031 </ul>
1032 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1033 </Panel>
Projects: what a workspace builds and runs, first on every page1034
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1035 <Panel title="Activity" icon={<ActivityIcon size={14} />}>
1036 {loaderData.eventsLoaded ? (
1037 <ActivityFeed groups={groups} showRepo={false} limit={8} empty="Nothing has happened here yet." />
1038 ) : (
1039 <Unavailable what="Activity" />
1040 )}
1041 </Panel>
1042
1043 {previews.length > 0 && (
1044 <Panel title="Previews" icon={<GitBranch size={14} />} count={previews.length} all={{ to: `${base}/deployments`, label: "Deployments" }}>
1045 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1046 {previews.map((app) => (
1047 <li key={app.url} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1048 <span className="min-w-0 grow">
1049 <a href={app.url} className="block truncate font-mono text-[0.8125rem] hover:text-accent">
1050 {host(app.url)}
1051 </a>
1052 <span className="text-xs text-muted">
1053 {app.branch}
1054 {app.number != null && (
1055 <>
1056 {" · "}
1057 <Link to={`${base}/pull/${app.number}`} className="hover:underline">
1058 #{app.number}
1059 </Link>
1060 </>
1061 )}
1062 </span>
Projects: what a workspace builds and runs, first on every page1063 </span>
1064 <span className="shrink-0 text-xs text-faint">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1065 <TimeAgo at={app.deployedAt} />
Projects: what a workspace builds and runs, first on every page1066 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1067 </li>
1068 ))}
1069 </ul>
1070 </Panel>
1071 )}
1072 </div>
Projects: what a workspace builds and runs, first on every page1073
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1074 <aside className="space-y-4 text-sm">
1075 {member && (
1076 <section className="rounded-xl border border-line bg-surface p-5">
1077 <div className="flex items-center justify-between">
1078 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1079 <Brain size={14} className="text-merged" />
1080 What agents know here
1081 </h2>
1082 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1083 Memory
1084 </Link>
Projects: what a workspace builds and runs, first on every page1085 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1086 {!loaderData.memoriesLoaded ? (
1087 <p className="mt-3 text-xs text-muted">Memory could not be loaded just now.</p>
1088 ) : knows.length === 0 ? (
1089 <p className="mt-3 text-xs leading-5 text-muted">
1090 Nothing yet. Agents note conventions, decisions and traps as they work, and every agent starting here reads them.
1091 </p>
1092 ) : (
1093 <ul className="mt-3 space-y-2.5">
1094 {knows.map((memory) => (
1095 <li key={memory.id} className="text-xs leading-5">
1096 <p className="line-clamp-3 text-fg-soft">
1097 {memory.pinned && <Pin size={11} className="mr-1 inline text-accent" />}
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1098 <InlineMarkdown text={memory.text} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1099 </p>
1100 <p className="text-faint">
1101 {memory.kind} · {memory.createdBy} · <TimeAgo at={memory.createdAt} />
1102 </p>
1103 </li>
1104 ))}
1105 {loaderData.memoryCount > knows.length && (
1106 <li>
1107 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1108 {loaderData.memoryCount - knows.length} more
1109 </Link>
1110 </li>
1111 )}
1112 </ul>
1113 )}
1114 </section>
Projects: what a workspace builds and runs, first on every page1115 )}
1116
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1117 <section className="rounded-xl border border-line bg-surface p-5">
1118 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1119 <HeartPulse size={14} className="text-faint" />
1120 Health
Project dependencies: addresses, preview stacks, Affects, and agents who know1121 </h2>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1122 <div className="mt-4 space-y-4 text-xs">
1123 <div>
1124 <div className="flex items-baseline justify-between">
1125 <span className="text-muted">Checks passing</span>
1126 <span className="font-medium tabular-nums">{percent(health.passRate)}</span>
1127 </div>
1128 <div className="mt-1.5">
1129 <Meter value={health.passRate} tone={health.passRate != null && health.passRate < 0.7 ? "bg-warn" : "bg-accent"} />
1130 </div>
1131 <p className="mt-1 text-faint">
1132 {health.checkRuns
1133 ? `${health.checkRuns} recent runs · ${percent(health.firstPass.rate)} pass on the first try`
1134 : "No checks have run recently."}
1135 </p>
1136 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production1137 {member && (!library || builds.length > 0) && (
Project dependencies: addresses, preview stacks, Affects, and agents who know1138 <div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1139 <div className="flex items-baseline justify-between">
1140 <span className="text-muted">Deploys</span>
1141 <Link to={`${base}/deployments`} className="text-faint hover:text-fg">
1142 {builds.length ? `last ${Math.min(builds.length, 20)}` : "none yet"}
1143 </Link>
1144 </div>
1145 <div className="mt-1.5">
1146 <DeployStrip builds={builds} base={base} />
1147 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1148 </div>
1149 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1150 <div>
1151 <div className="flex items-baseline justify-between">
1152 <span className="text-muted">Open issues by age</span>
1153 <Link to={`${base}/issues`} className="font-medium tabular-nums hover:text-accent">
1154 {open.issues}
1155 </Link>
1156 </div>
1157 {ages ? (
1158 <ul className="mt-2 space-y-1.5">
1159 {ages.map((bucket) => (
1160 <li key={bucket.label} className="grid grid-cols-[6.5rem_1fr_1.5rem] items-center gap-2">
1161 <span className="text-faint">{bucket.label}</span>
1162 <span className="block h-1.5 overflow-hidden rounded-full bg-line">
1163 <span
1164 className={`block h-full rounded-full ${bucket.label === "Older" ? "bg-warn" : "bg-fg-soft/60"}`}
1165 style={{ width: `${(bucket.count / oldest) * 100}%` }}
1166 />
1167 </span>
1168 <span className="text-right tabular-nums text-muted">{bucket.count}</span>
Project dependencies: addresses, preview stacks, Affects, and agents who know1169 </li>
1170 ))}
1171 </ul>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1172 ) : (
1173 <p className="mt-1 text-faint">Issues could not be loaded just now.</p>
1174 )}
1175 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1176 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1177 </section>
Project dependencies: addresses, preview stacks, Affects, and agents who know1178
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1179 <section className="rounded-xl border border-line bg-surface p-5">
1180 <div className="flex items-center justify-between">
1181 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1182 <Network size={14} className="text-faint" />
1183 Dependencies
1184 </h2>
1185 {member && (
1186 <Link to={`${base}/settings/dependencies`} className="text-xs text-muted hover:text-fg">
1187 Manage
1188 </Link>
1189 )}
1190 </div>
1191 {!dependencies ? (
1192 <p className="mt-3 text-xs text-muted">Dependencies could not be loaded just now.</p>
1193 ) : dependencies.dependsOn.length === 0 && dependencies.usedBy.length === 0 ? (
1194 <p className="mt-3 text-xs leading-5 text-muted">
1195 Uses no other project, and none uses it. Declare one, and builds get its address and agents know what depends on
1196 what.
1197 </p>
1198 ) : (
1199 <div className="mt-3 space-y-3 text-xs">
1200 {dependencies.dependsOn.length > 0 && (
1201 <div>
1202 <p className="flex items-center gap-1 text-muted">
1203 <ArrowUpRight size={12} /> Uses
1204 </p>
1205 <ul className="mt-1.5 space-y-1">
1206 {dependencies.dependsOn.map((d) => (
1207 <li key={d.slug} className="flex items-center justify-between gap-2">
1208 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1209 {d.name}
1210 </Link>
1211 {d.as && <code className="font-mono text-faint">{d.as}</code>}
1212 </li>
1213 ))}
1214 </ul>
1215 </div>
1216 )}
1217 {dependencies.usedBy.length > 0 && (
1218 <div>
1219 <p className="flex items-center gap-1 text-muted">
1220 <ArrowDownLeft size={12} /> Used by
1221 </p>
1222 <ul className="mt-1.5 space-y-1">
1223 {dependencies.usedBy.map((d) => (
1224 <li key={d.slug}>
1225 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1226 {d.name}
1227 </Link>
1228 </li>
1229 ))}
1230 </ul>
1231 </div>
1232 )}
1233 </div>
1234 )}
Projects: what a workspace builds and runs, first on every page1235 </section>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1236
1237 {source && (
1238 <section className="rounded-xl border border-line bg-surface p-5">
1239 <h2 className="text-sm font-semibold">Clone</h2>
1240 <div className="mt-3">
Merge branch 'worktree-agent-aaf03bdceac799c89'1241 <CopyLine text={`git clone ${cloneUrl(addresses, `${source.repo.namespace}/${source.repo.name}`)}`} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1242 </div>
1243 </section>
1244 )}
1245 </aside>
1246 </div>
Projects: what a workspace builds and runs, first on every page1247 </div>
1248 );
1249}

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