Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
449 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 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)1/**
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.2 * Code's Overview data: what needs the viewer, what landed, and the week,
3 * across a workspace's projects. Code's Overview
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)4 * (routes/workspace/code-overview.tsx) shows all of it; Home
5 * (routes/workspace/home.tsx) takes what needs you from it.
6 */
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.7import { type G1tEvent, type Pull, type Repo, type RepoPath, type User, type Viewer, REPO_ROLE_LABELS, workOwner } from "@g1t/contracts";
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)8
9import { WORKSPACE_COOKIE, chosenWorkspace } from "./workspace-choice";
10import {
11 type ActivityItem,
12 type Need,
13 TIME,
14 actorIds,
15 eventItem,
16 FEED_EVENT_TYPES,
17 groupActivity,
18 nameActor,
19 rankNeeds,
20 readCookie,
21 waitedFor,
22} from "./mission";
23import { G1T_COMMIT_EMAILS, normalizeEmail } from "./commit-people";
24import {
25 type Fact,
26 type Merged,
27 type NeedRow,
28 type QuickAction,
29 dateLine,
30 dayKey,
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.31 historyCovers,
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)32 landedToday,
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.33 placePushes,
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)34 pullFacts,
35 reachesBack,
36 reasonFor,
37 summaryLine,
38 weekOf,
39 who,
40 whyFor,
41} from "./mission-control";
42import { needsYou } from "./inbox";
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.43import { deployments, events as eventLog, identity, inbox, projects as projectsApi, repos as reposApi, work } from "./services.server";
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)44import { roleIn } from "./session.server";
45import { runners } from "./runners.server";
46import { readableRepos } from "./access.server";
47
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.48/** The viewer's time zone, which the page sets, so the date fits their day. */
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)49const TZ_COOKIE = "g1t_tz";
50/** The most projects whose pull requests and events are read for the page. */
51const MAX_PROJECTS = 10;
52const EVENTS_PER_PROJECT = 80;
53/** How many pull requests work lists at once (`LIST_PAGE` in services/work). */
54const PULL_PAGE = 100;
55/** Unread inbox items read for the Needs you card, and how many it shows. */
56const INBOX_READ = 50;
57const INBOX_SHOWN = 4;
58
59/** Where a need came from, so its row can say what is known about it. */
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.60type Extra = Partial<Pick<NeedRow, "repo" | "ref" | "by" | "facts" | "quick" | "link" | "open">>;
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)61
62/**
63 * Push events read per project, a page at a time, back two weeks. Pushes
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.64 * to every branch are in the log, so a fixed count lost a busy week's
65 * earlier days to other branches.
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)66 */
67const PUSH_PAGE = 200;
68const PUSH_PAGES = 5;
69/** The first page is smaller: most projects push far less in two weeks. */
70const FIRST_PUSH_PAGE = 50;
71/**
72 * Commits of the default branch read to place each push's commits: a
73 * short read first, which covers most projects' fortnight, and the longer
74 * one only when it does not reach the oldest push.
75 */
76const HISTORY_FIRST = 200;
77const HISTORY_READ = 1000;
78
79/**
80 * The commits people pushed straight to a project's default branch in the
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.81 * last two weeks, each with when its push landed. Merges and g1t's own
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)82 * commits are left out: pull requests count those.
83 */
84async function directCommits(repo: Repo, viewer: Viewer): Promise<{ hash: string; at: string }[]> {
85 const since = Date.now() - 14 * TIME.DAY;
86 const pushes: G1tEvent<"git.push">[] = [];
87 let before: string | undefined;
88 for (let page = 0; page < PUSH_PAGES; page++) {
89 const limit = page === 0 ? FIRST_PUSH_PAGE : PUSH_PAGE;
90 const batch = await eventLog.list({ repoId: repo.id, types: ["git.push"], limit, ...(before ? { before } : {}) });
91 for (const event of batch) {
92 if (event.type === "git.push" && event.data.defaultBranch && Date.parse(event.time) >= since) pushes.push(event as G1tEvent<"git.push">);
93 }
94 const oldest = batch.at(-1);
95 if (batch.length < limit || !oldest || Date.parse(oldest.time) < since) break;
96 before = oldest.id;
97 }
98 if (pushes.length === 0) return [];
99 // A read of the branch from the newest push back; each push brought
100 // what lies between its `after` and its `before` in that history.
101 const path = { namespace: repo.namespace, name: repo.name };
102 const read = (limit: number) => reposApi.log(path, viewer, pushes[0].data.after, limit).catch(() => null);
103 let history = await read(HISTORY_FIRST);
104 if (history?.ok && !historyCovers(history.value, pushes, HISTORY_FIRST)) history = await read(HISTORY_READ);
105 if (!history?.ok) return [];
106 // g1t's own commits are told by their author address, never by name.
107 return placePushes(history.value, pushes, (commit) => G1T_COMMIT_EMAILS.has(normalizeEmail(commit.author.email)));
108}
109
110export async function loadMissionControl(viewer: User, request: Request, workspace: string | null) {
111 const now = Date.now();
112 const started = Date.now();
113 const times: Record<string, number> = {};
114 // Each call is timed, and one that fails leaves its section empty rather
115 // than taking the page down.
116 const soft = <T,>(name: string, promise: Promise<T>): Promise<T | null> =>
117 promise.then(
118 (value) => ((times[name] = Date.now() - started), value),
119 (error) => {
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.120 console.warn(`overview: ${name} failed`, error);
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)121 times[name] = Date.now() - started;
122 return null;
123 },
124 );
125 const okOr = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
126
127 const cookies = request.headers.get("cookie");
128 const tz = readCookie(cookies, TZ_COOKIE);
129 const memberships = viewer.workspaces ?? [];
130 // The workspace asked for, else the one you chose, as the sidebar shows it (lib/workspace-choice.ts).
131 const slug =
132 (workspace && roleIn(viewer, workspace) ? workspace.toLowerCase() : null) ??
133 chosenWorkspace(memberships, readCookie(cookies, WORKSPACE_COOKIE))?.slug ??
134 null;
135 const username = viewer.username;
136
137 const reposP = soft("repos", reposApi.list(viewer, { memberOnly: true }));
138 // Workflow jobs stuck waiting for a self-hosted runner that is not there.
139 const stuckJobsP = soft("stuck_jobs", runners.stuck(viewer));
140 // The chosen workspace's projects: their open and recently merged pull
141 // requests, all in one call to work, and their recent events, read as
142 // soon as the projects are known.
143 const perRepoP = reposP.then(async (repos) => {
144 const chosen = (repos ?? [])
145 .filter((repo) => slug != null && repo.namespace.toLowerCase() === slug.toLowerCase())
146 .slice(0, MAX_PROJECTS);
147 const [batch, logs, pushes] = await Promise.all([
148 work.pullsForRepos(chosen.map((repo) => repo.id), viewer, PULL_PAGE).catch(() => []),
149 Promise.all(chosen.map((repo) => eventLog.list({ repoId: repo.id, types: [...FEED_EVENT_TYPES], limit: EVENTS_PER_PROJECT }).catch(() => null))),
150 // People's pushes straight to the default branch, which no pull
151 // request counts: the commits each brought, for the week.
152 Promise.all(chosen.map((repo) => directCommits(repo, viewer).catch(() => []))),
153 ]);
154 const byId = new Map(batch.map((entry) => [entry.repoId, entry]));
155 return Promise.all(
156 chosen.map(async (repo, index) => {
157 const found = byId.get(repo.id);
158 if (found) return { repo, pulls: found.open.slice(0, 60), closed: found.closed, events: logs[index], direct: pushes[index] ?? [] };
159 // A fork, which the batch leaves out: asked on its own.
160 const path = { namespace: repo.namespace, name: repo.name };
161 const [pulls, closed] = await Promise.all([
162 work.listPulls(path, viewer, "open").catch(() => null),
163 work.listPulls(path, viewer, "closed").catch(() => null),
164 ]);
165 return {
166 repo,
167 pulls: pulls?.ok ? pulls.value.slice(0, 60) : null,
168 closed: closed?.ok ? closed.value : null,
169 events: logs[index],
170 direct: pushes[index] ?? [],
171 };
172 }),
173 );
174 });
175
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.176 const [repos, perRepo, active, profile, overview, projectList, invitations, unread] = await Promise.all([
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)177 reposP,
178 soft("projects", perRepoP),
179 soft("pulls", work.listActivePulls(viewer)),
180 soft("profile", identity.profile(username)),
181 slug ? soft("deploys", deployments.overview(slug, viewer)) : null,
182 slug ? soft("projectList", projectsApi.list(slug, viewer)) : null,
183 // Repositories someone has invited the viewer to.
184 soft("invitations", identity.myRepoInvitations(viewer)),
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.185 // What in their inbox is unread: a failure or a warning goes on the Needs you card.
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)186 soft("inbox", inbox.list(viewer, { unread: true, limit: INBOX_READ })),
187 ]);
188
189 const repoList = repos ?? [];
190 // Each pull request is shown under its repository. Most are in the
191 // viewer's own, already listed; the rest are looked up once each.
192 const known = new Map<string, Repo>(repoList.map((repo) => [repo.id, repo]));
193 const missing = [...new Set((active ?? []).map(({ pull }) => pull.repoId))].filter((id) => !known.has(id));
194 for (const repo of await readableRepos(missing, viewer)) known.set(repo.id, repo);
195 const pathOf = (repo: Repo): RepoPath => ({ namespace: repo.namespace, name: repo.name });
196 const inWorkspace = (repo: RepoPath) => slug != null && repo.namespace.toLowerCase() === slug.toLowerCase();
197
198 const activeList = (active ?? []).flatMap((item) => {
199 const repo = known.get(item.pull.repoId);
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.200 return repo ? [{ pull: item.pull, repo }] : [];
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)201 });
202 const overviewList = okOr(overview ?? null);
203 const projectsOk = okOr(projectList ?? null);
204
205 const lower = username.toLowerCase();
206 const openPulls = (perRepo ?? []).flatMap(({ repo, pulls }) => (pulls ?? []).map((pull) => ({ pull, repo })));
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.207 // Never your own.
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)208 const reviewRequested = openPulls.filter(
209 ({ pull }) =>
210 pull.status === "open" &&
211 workOwner(pull).username.toLowerCase() !== lower &&
212 pull.reviewers.some((name) => name.toLowerCase() === lower),
213 );
214
215 // --- Needs you ------------------------------------------------------------
216 const needs: Need[] = [];
217 const extras = new Map<string, Extra>();
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.218 /** What every pull request's row shares: where it is, who opened it, what is known. */
219 const pullExtra = (pull: Pull, repo: Repo): Extra => {
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)220 const base = `/${repo.namespace}/${repo.name}`;
221 return {
222 repo: pathOf(repo),
223 ref: `#${pull.number}`,
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.224 by: who(workOwner(pull).username),
225 facts: pullFacts({ checkStatus: pull.checkStatus, files: pull.files }),
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)226 open: pull.issue != null ? `${base}/issues/${pull.issue}` : `${base}/pull/${pull.number}?tab=changes`,
227 };
228 };
229 const pullAction = (repo: Repo, pull: Pull, quick: Omit<QuickAction, "to">): QuickAction => ({
230 ...quick,
231 to: `/${repo.namespace}/${repo.name}/pull/${pull.number}`,
232 });
233
234 for (const invitation of invitations ?? []) {
235 if (invitation.status !== "pending") continue;
236 const key = `invitation:${invitation.id}`;
237 needs.push({
238 key,
239 kind: "invitation",
240 title: `${invitation.invited_by ?? "Someone"} invited you to ${invitation.repo}`,
241 detail: `With the ${REPO_ROLE_LABELS[invitation.role]} role. The invitation expires ${new Date(invitation.expires_at).toISOString().slice(0, 10)}.`,
242 to: `/${invitation.repo}/invitations`,
243 action: "Respond",
244 at: Date.parse(invitation.created_at),
245 where: invitation.repo,
246 });
247 const [namespace, name] = invitation.repo.split("/");
248 extras.set(key, {
249 repo: namespace && name ? { namespace, name } : null,
250 by: who(invitation.invited_by),
251 facts: [
252 { label: "Role", value: REPO_ROLE_LABELS[invitation.role], tone: null },
253 { label: "Expires", value: new Date(invitation.expires_at).toISOString().slice(0, 10), tone: null },
254 ],
255 });
256 }
257 for (const entry of overviewList ?? []) {
258 const latest = entry.latest;
259 if (latest?.kind === "production" && latest.status === "failed" && slug) {
260 const key = `deploy:${entry.slug}`;
261 const to = `/${slug}/${entry.slug}/deployments/${latest.id}`;
262 needs.push({
263 key,
264 kind: "deploy",
265 title: `Production build of ${entry.slug} failed`,
266 detail: latest.error ?? "The last build of the default branch failed. Production still serves the build before it.",
267 to,
268 action: "See the build",
269 at: Date.parse(latest.finishedAt ?? latest.createdAt),
270 where: `${slug}/${entry.slug}`,
271 });
272 const facts: Fact[] = [
273 { label: "Commit", value: latest.commit.slice(0, 7), tone: null },
274 {
275 label: "Production",
276 value: entry.production ? "Serving the build before" : "Not live yet",
277 tone: entry.production ? "good" : "warn",
278 },
279 ];
280 extras.set(key, {
281 repo: { namespace: slug, name: entry.slug },
282 by: who(latest.createdBy),
283 facts,
284 link: { label: "Deployment settings", to: `/${slug}/${entry.slug}/settings/deployments` },
285 });
286 }
287 }
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.288 for (const { pull, repo } of activeList) {
289 // Taken out of the merge queue: its change failed combined with what was ahead.
290 if (pull.status !== "open" || pull.checkStatus !== "failed") continue;
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)291 const key = `pull:${pull.id}`;
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.292 needs.push({
293 key,
294 kind: "checks",
295 title: pull.title,
296 detail: "It failed in the merge queue. Push a fix, then merge it again.",
297 to: `/${repo.namespace}/${repo.name}/pull/${pull.number}`,
298 action: "See checks",
299 at: Date.parse(pull.updatedAt),
300 where: `${repo.namespace}/${repo.name}#${pull.number}`,
301 });
302 extras.set(key, { ...pullExtra(pull, repo), quick: null });
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)303 }
304 for (const { pull, repo } of reviewRequested) {
305 const key = `review:${pull.id}`;
306 needs.push({
307 key,
308 kind: "review",
309 title: pull.title,
310 detail: `${workOwner(pull).username} asked for your review.`,
311 to: `/${repo.namespace}/${repo.name}/pull/${pull.number}`,
312 action: "Review",
313 at: Date.parse(pull.updatedAt),
314 where: `${repo.namespace}/${repo.name}#${pull.number}`,
315 });
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.316 extras.set(key, {
317 ...pullExtra(pull, repo),
318 quick: pullAction(repo, pull, { label: "Approve the change", fields: { action: "comment", verdict: "approve", body: "" }, done: "Approved" }),
319 });
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)320 }
321 for (const job of (await stuckJobsP) ?? []) {
322 const key = `runner:${job.id}`;
323 const minutes = Math.max(10, Math.round((now - Date.parse(job.queuedAt)) / 60_000));
324 const [namespace, name] = job.repo.split("/");
325 needs.push({
326 key,
327 kind: "runner",
328 title: `${job.name} is waiting for a self-hosted runner`,
329 detail: `No runner with labels ${job.labels} is online. Start one, or change the job's runs-on.`,
330 to: `/${job.repo}/actions/runs/${job.runId}`,
331 action: "Look",
332 at: Date.parse(job.queuedAt),
333 where: job.repo,
334 });
335 extras.set(key, {
336 repo: namespace && name ? { namespace, name } : undefined,
337 facts: [
338 { label: "Labels", value: job.labels, tone: null },
339 { label: "Waiting", value: waitedFor(minutes), tone: "warn" },
340 ],
341 // Runners are the owners' to see to.
342 ...(namespace && roleIn(viewer, namespace) === "owner" ? { link: { label: "Runners", to: `/${namespace}/-/runners` } } : {}),
343 });
344 }
345
346 const needRows: NeedRow[] = rankNeeds(needs).map((need) => {
347 const extra = extras.get(need.key) ?? {};
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.348 const reason = reasonFor(need);
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)349 return {
350 key: need.key,
351 reason,
352 repo: extra.repo ?? null,
353 ref: extra.ref ?? null,
354 title: need.title,
355 ask: need.detail,
356 by: extra.by ?? null,
357 at: need.at,
358 to: need.to,
359 open: extra.open ?? need.to,
360 facts: extra.facts ?? [],
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.361 why: whyFor(reason, need),
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)362 quick: extra.quick ?? null,
363 link: extra.link ?? null,
364 };
365 });
366
367 // --- Landed ---------------------------------------------------------------
368 const merged: Merged[] = (perRepo ?? []).flatMap(({ repo, closed }) =>
369 (closed ?? []).flatMap((pull) =>
370 pull.status === "merged" && pull.mergedAt
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.371 ? [{ repo: pathOf(repo), number: pull.number, title: pull.title, mergedBy: pull.mergedBy, mergedAt: pull.mergedAt, files: pull.files }]
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)372 : [],
373 ),
374 );
375 const twoWeeksAgo = now - 14 * TIME.DAY;
376 const complete = perRepo != null && perRepo.every(({ closed }) => closed != null && reachesBack(closed, twoWeeksAgo, PULL_PAGE));
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.377 // Commits pushed straight to the default branch are changes too, on the day they landed.
378 const direct = (perRepo ?? []).flatMap(({ direct }) => direct.map((commit) => ({ mergedAt: commit.at })));
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)379 const week = weekOf([...merged, ...direct], now, tz, complete);
380 const landed = landedToday(merged, now, tz);
381
382 // --- Activity -------------------------------------------------------------
383 const items: ActivityItem[] = [];
384 const titles: Record<string, string> = {};
385 const titleKey = (repo: RepoPath, number: number) => `${repo.namespace}/${repo.name}#${number}`.toLowerCase();
386 for (const { repo, pulls, closed, events } of perRepo ?? []) {
387 for (const pull of [...(pulls ?? []), ...(closed ?? [])]) titles[titleKey(repo, pull.number)] = pull.title;
388 for (const event of events ?? []) {
389 if (event.type === "issue.opened") titles[titleKey(repo, event.data.number)] ??= event.data.title;
390 const item = eventItem(event, pathOf(repo));
391 if (item) items.push(item);
392 }
393 }
394 // The log names people by account id: their usernames, in one lookup.
395 const ids = actorIds(items.map((item) => item.actor));
396 const names = ids.length > 0 ? await soft("usernames", identity.usernames(ids)) : {};
397 const groups = groupActivity(items.map((item) => ({ ...item, actor: nameActor(item.actor, names) }))).slice(0, 40);
398 // Only the titles the feed names travel to the page.
399 const shownTitles: Record<string, string> = {};
400 for (const group of groups) {
401 for (const part of group.parts) {
402 for (const number of part.numbers) {
403 const key = titleKey(group.repo, number);
404 if (titles[key]) shownTitles[key] = titles[key];
405 }
406 }
407 }
408
409 // --- The strip ------------------------------------------------------------
410 const month = dayKey(now, tz).slice(0, 7);
411 const projectCount = projectsOk?.length ?? (perRepo != null ? perRepo.length : null);
412 const projectsThisMonth = projectsOk ? projectsOk.filter((project) => dayKey(Date.parse(project.createdAt), tz).slice(0, 7) === month).length : null;
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.413 const openCount = perRepo != null ? openPulls.filter(({ pull }) => pull.status === "open" || pull.status === "draft").length : null;
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)414
415 times.total = Date.now() - started;
416 const serverTiming = Object.entries(times)
417 .map(([name, ms]) => `${name};dur=${ms}`)
418 .join(", ");
419 return {
420 timing: serverTiming,
421 value: {
422 signedIn: true as const,
423 viewer,
424 name: profile?.name?.trim() || username,
425 date: dateLine(now, tz),
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.426 summary: summaryLine({ total: week.total, needs: needRows.length }),
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)427 workspace: slug,
428 repos: repoList.filter((repo) => inWorkspace(repo)).map(pathOf),
429 needs: needRows,
430 landed,
431 perRepoLoaded: perRepo != null,
432 stats: {
433 projects: projectCount,
434 projectsThisMonth,
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.435 openPulls: openCount,
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)436 },
437 week,
438 groups,
439 titles: shownTitles,
440 inboxNeeds: needsYou(unread?.items ?? [], INBOX_SHOWN),
441 },
442 };
443}
444
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.445/** What Code's Overview takes. */
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)446export type Loaded = Awaited<ReturnType<typeof loadMissionControl>>["value"];
447
448/** The cookie that holds the viewer's time zone. */
449export { TZ_COOKIE };

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