Skip to content
221 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.

Merge branch 'worktree-agent-ad7c6d88d93adc817'1import { Package, Plus } from "lucide-react";
2import { Outlet, data, redirect, useLocation, useNavigation } from "react-router";
Agents as a team: lifecycle, merge queue, billing and a new shell3
4import type { Route } from "./+types/layout";
Social cards for every page: og.g1t.sh5import { page } from "../../lib/meta";
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put6import { Avatar, ButtonLink, Pill } from "../../components/ui";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas7import { WelcomeBanner } from "../../components/welcome";
8import { clearWelcome, welcomes } from "../../lib/invites";
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put9import { notFound } from "../../lib/not-found.server";
Agents and memory, checks and conflicts, profiles, slug renames, custom domains10import { redirectIfRenamed } from "../../lib/renamed.server";
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put11import { rememberWorkspace } from "../../lib/workspace-choice";
Merge branch 'worktree-agent-ad7c6d88d93adc817'12import { workspacePage, workspaceRedirect } from "../../lib/workspace-nav";
13import { WorkspacePageSkeleton } from "../../components/workspace-skeletons";
Agents as a team: lifecycle, merge queue, billing and a new shell14import { identity } from "../../lib/services.server";
15import { getViewer, roleIn } from "../../lib/session.server";
16
Social cards for every page: og.g1t.sh17export function meta({ loaderData, params, ...args }: Route.MetaArgs) {
18 return page(args, { title: `${loaderData?.workspace.name ?? params.owner} · g1t` });
Agents as a team: lifecycle, merge queue, billing and a new shell19}
20
Agents and memory, checks and conflicts, profiles, slug renames, custom domains21export async function loader({ params, context, request }: Route.LoaderArgs) {
Navigation: the workspace's own page with tabs, pins and recent in the sidebar22 const url = new URL(request.url);
Merge branch 'worktree-agent-ad7c6d88d93adc817'23 // `?tab=projects` and the like, from the tabs this page once had: that
24 // page's own address.
Navigation: the workspace's own page with tabs, pins and recent in the sidebar25 const moved = url.search ? workspaceRedirect(url.pathname, url.search) : null;
26 if (moved) throw redirect(moved);
27 const viewer = getViewer(context);
Merge branch 'worktree-agent-ad7c6d88d93adc817'28 const workspace = await identity.getWorkspace(params.owner);
Agents and memory, checks and conflicts, profiles, slug renames, custom domains29 if (!workspace) {
30 // A workspace's old name, after a rename: its pages are at the new one.
31 await redirectIfRenamed(request, params.owner);
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put32 throw notFound("workspace");
Agents and memory, checks and conflicts, profiles, slug renames, custom domains33 }
Navigation: the workspace's own page with tabs, pins and recent in the sidebar34 const role = roleIn(viewer, workspace.slug);
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put35 // Opening one of your workspaces makes it the one you are in.
Merge branch 'worktree-agent-ad7c6d88d93adc817'36 if (!role) return { workspace, role, welcome: false };
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put37 const secure = new URL(request.url).protocol === "https:";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas38 const headers = new Headers({ "Set-Cookie": rememberWorkspace(workspace.slug, secure) });
39 // Just joined with an invite: welcomed once (routes/invite.tsx).
40 const welcome = welcomes(request.headers.get("cookie"), workspace.slug);
41 if (welcome) headers.append("Set-Cookie", clearWelcome(secure));
Merge branch 'worktree-agent-ad7c6d88d93adc817'42 return data({ workspace, role, welcome }, { headers });
Agents as a team: lifecycle, merge queue, billing and a new shell43}
44
A catalogue of model providers, and settings that feel like settings45/** A workspace's own pages, each with its title and what it is for. */
46const PAGES: Record<string, { title: string; about: string }> = {
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)47 workspace: {
48 title: "Workspace",
49 about: "Who is in it, what it is on, and what it has spent this month. Everything about the workspace itself is in this sidebar.",
50 },
Merge branch 'worktree-agent-ad7c6d88d93adc817'51 settings: { title: "General", about: "The workspace's name, icon, address and description, who can create teams, and deleting it." },
A catalogue of model providers, and settings that feel like settings52 people: {
Navigation: the workspace's own page with tabs, pins and recent in the sidebar53 title: "People",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look54 about: "Members create repositories and have the base permission on each one. Owners are Admins on every repository, and also manage members, tokens, billing and integrations.",
55 },
Navigation: the workspace's own page with tabs, pins and recent in the sidebar56 projects: {
57 title: "Projects",
58 about: "Everything the workspace builds and runs. Search, filter and sort them, and pin the ones you use most to keep them in your sidebar.",
59 },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar60 teams: {
61 title: "Teams",
62 about: "Groups of members, given roles on repositories together, mentioned as @workspace/team and asked to review together. Child teams inherit their parent's access.",
63 },
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look64 repositories: {
65 title: "Repositories",
66 about: "Every repository in the workspace: who can see it, whether it is archived, and the ones deleted recently.",
A catalogue of model providers, and settings that feel like settings67 },
68 tokens: {
69 title: "Access tokens",
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules70 about: "Tokens that belong to the workspace, not a person: for CI, integrations and agents that work for the whole team. Each has Write on the workspace's repositories, or Admin when an owner gives it that.",
71 },
72 "personal-access-tokens": {
73 title: "Personal access tokens",
74 about: "Which of your members' own tokens may reach the workspace and for how long, the fine-grained tokens waiting for an owner's approval, and every token that can reach it.",
A catalogue of model providers, and settings that feel like settings75 },
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member76 packages: {
77 title: "Packages",
The Packages page names every registry it has78 about: "What the workspace publishes and installs, beside its code: container images, npm, Composer, Go, Cargo, Maven, NuGet and RubyGems, with the same members, roles and tokens.",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member79 },
A catalogue of model providers, and settings that feel like settings80 usage: { title: "Usage", about: "What the workspace's agents cost, run by run, by repository, pull request and model." },
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens81 gateway: {
82 title: "AI Gateway",
83 about: "Your own code's model requests, sent with the workspace's access tokens: each one's model, tokens, cost and status.",
84 },
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look85 billing: { title: "Billing and plans", about: "The g1t plan, the trial, your spend limit and caps, prepaying, and every charge." },
Agents and memory, checks and conflicts, profiles, slug renames, custom domains86 agents: {
87 title: "Agent fleet",
88 about: "Every agent at work across the workspace's projects: what each holds, what it is doing now, and what it has cost.",
89 },
90 memory: {
91 title: "Workspace memory",
92 about: "What holds across all of the workspace's projects, given to every agent in every one of them, beside each project's own memory.",
93 },
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API94 context: {
95 title: "Context",
96 about: "Everything the workspace builds and runs, and what it knows: a catalog built from its projects, memory with a review queue, one search over all of it, and a scorecard for each project.",
97 },
98 audit: {
99 title: "Audit log",
100 about: "Every action agents took with their run credentials, and every change people and tokens made: by whom, on whose behalf, to what, and whether it was allowed.",
101 },
102 guardrails: {
103 title: "Guardrails",
104 about: "What agents may reach, run, spend and take in every project's sandboxes. Each project can override these under its settings.",
105 },
Webhooks: every event, to your own addresses, signed and retried106 webhooks: {
107 title: "Webhooks",
108 about: "Every repository's events, sent to your own addresses as they happen. A repository can also have its own, under its settings.",
109 },
GitHub Actions on g1t, part three: .g1t/workflows, the pages, the docs110 secrets: {
111 title: "Secrets and variables",
Secrets and variables: one list, rows per environment, for workflows and deployments112 about: "Shared with every repository, or the ones you link: read by workflows, deployments, or both. A repository's own row of the same key wins.",
GitHub Actions on g1t, part three: .g1t/workflows, the pages, the docs113 },
Fast pages, required checks on the branch, self-hosted runners, honest incidents114 runners: {
115 title: "Self-hosted runners",
116 about: "Your own machines, which run workflow jobs, and if you choose agents' work, for free. They connect out to g1t; nothing reaches in.",
117 },
Security matches the workspace's other pages and leaves out deleted repositories; every section has a name in the trail118 security: {
119 title: "Security",
120 about: "Open alerts in every project: secrets found in pushes and history, and vulnerable dependencies. Each project's Security page has the details and the security update g1t opened for each.",
121 },
A catalogue of model providers, and settings that feel like settings122 integrations: {
123 title: "Integrations",
124 about: "Model providers, alerts and trackers. Secrets are sealed when saved, and agents never see them.",
125 },
126};
127
Merge branch 'worktree-agent-ad7c6d88d93adc817'128/** A workspace page's heading: its title and what it is for. */
129function PageHeader({ title, about }: { title: string; about: string }) {
Navigation: the workspace's own page with tabs, pins and recent in the sidebar130 return (
Merge branch 'worktree-agent-ad7c6d88d93adc817'131 <header className="mb-8 border-b border-line pb-6">
132 <h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
133 <p className="mt-1.5 text-sm text-muted">{about}</p>
134 </header>
Navigation: the workspace's own page with tabs, pins and recent in the sidebar135 );
136}
137
Agents as a team: lifecycle, merge queue, billing and a new shell138export default function WorkspaceLayout({ loaderData }: Route.ComponentProps) {
Merge branch 'worktree-agent-ad7c6d88d93adc817'139 const { workspace, role, welcome } = loaderData;
Navigation: the workspace's own page with tabs, pins and recent in the sidebar140 const { pathname } = useLocation();
141 const going = useNavigation().location;
Merge branch 'worktree-agent-ad7c6d88d93adc817'142 // The workspace's own page is its overview. Its other pages are found
143 // from the sidebar, which lights the one you are on, and the top bar's
144 // trail leads back here; each has a heading of its own.
145 const here = workspacePage(pathname, workspace.slug);
146 // On the way to another of its pages: that page's heading and shape
147 // until it arrives.
148 const next = going && going.pathname !== pathname ? workspacePage(going.pathname, workspace.slug) : null;
149 const shown = next ?? here;
Navigation: the workspace's own page with tabs, pins and recent in the sidebar150 const parts = (pathname.split("/-/")[1] ?? "").split("/").filter(Boolean);
Chat and workspace agents: channels, DMs and named agents you talk to151 // Chat fills the page edge to edge: its conversation, thread and details
152 // are columns of their own (components/chat/channel.tsx).
153 if (parts[0] === "chat" && !next) return <Outlet />;
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)154 // Code's Overview lays itself out, as Mission control did.
155 if (parts[0] === "overview" && parts.length === 1 && !next) return <Outlet />;
Merge branch 'worktree-agent-ad7c6d88d93adc817'156 if (shown !== "overview") {
157 const key = shown ?? parts[0] ?? "";
158 const heading = PAGES[key];
159 // A page within one (a single package or team) and a page that is
160 // coming (Insights) have headings of their own.
161 const titled = heading && (shown != null || parts.length === 1);
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member162 return (
163 <div className="mx-auto max-w-5xl px-4 py-10 sm:px-8">
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)164 {titled && (
165 // A phone's Agents tab opens on its list of agents (agents/layout.tsx); the fleet's heading would sit above it.
166 <div className={key === "agents" ? "max-md:hidden" : undefined}>
167 <PageHeader title={heading.title} about={heading.about} />
168 </div>
169 )}
Merge branch 'worktree-agent-ad7c6d88d93adc817'170 {next ? <WorkspacePageSkeleton page={next} /> : <Outlet />}
A catalogue of model providers, and settings that feel like settings171 </div>
172 );
173 }
Agents as a team: lifecycle, merge queue, billing and a new shell174 return (
175 <>
Merge branch 'worktree-agent-ad7c6d88d93adc817'176 {/* The workspace's own header band, above its overview. */}
Agents as a team: lifecycle, merge queue, billing and a new shell177 <div className="border-b border-line bg-surface/60">
Merge branch 'worktree-agent-ad7c6d88d93adc817'178 <div className="mx-auto max-w-6xl px-4 py-8">
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas179 {welcome && (
180 <div className="mb-6">
181 <WelcomeBanner title={`You're in ${workspace.name}`}>
182 You joined as a {role}. Its projects, issues and agents are all here.
183 </WelcomeBanner>
184 </div>
185 )}
Agents as a team: lifecycle, merge queue, billing and a new shell186 <div className="flex flex-wrap items-center gap-4">
Workspace names and icons, and a component kit for every control187 <Avatar name={workspace.slug} image={workspace.avatar} size={52} square />
Agents as a team: lifecycle, merge queue, billing and a new shell188 <div className="min-w-0 grow">
189 <div className="flex flex-wrap items-center gap-2.5">
190 <h1 className="truncate text-2xl font-semibold tracking-tight">
191 {workspace.name}
192 </h1>
193 {role && <Pill>{role}</Pill>}
194 </div>
195 <p className="font-mono text-sm text-muted">g1t.sh/{workspace.slug}</p>
196 </div>
Merge branch 'worktree-agent-ad7c6d88d93adc817'197 {role ? (
Agents as a team: lifecycle, merge queue, billing and a new shell198 <ButtonLink to={`/new?workspace=${workspace.slug}`}>
199 <Plus size={15} />
Projects live: fixes from walking them end to end200 New project
Agents as a team: lifecycle, merge queue, billing and a new shell201 </ButtonLink>
Merge branch 'worktree-agent-ad7c6d88d93adc817'202 ) : (
203 // A visitor's sidebar is not this workspace's: its packages
204 // are a link here, as its projects are below.
205 <ButtonLink to={`/${workspace.slug}/-/packages`} variant="quiet" prefetch="intent">
206 <Package size={15} />
207 Packages
208 </ButtonLink>
Agents as a team: lifecycle, merge queue, billing and a new shell209 )}
210 </div>
211 {workspace.description && (
212 <p className="mt-4 max-w-2xl text-sm text-muted">{workspace.description}</p>
213 )}
214 </div>
215 </div>
216 <div className="mx-auto max-w-6xl px-4 py-8">
Merge branch 'worktree-agent-ad7c6d88d93adc817'217 {next ? <WorkspacePageSkeleton page="overview" /> : <Outlet />}
Agents as a team: lifecycle, merge queue, billing and a new shell218 </div>
219 </>
220 );
221}

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