Skip to content

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

1,489 lines68,978 bytesCodeBlame

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

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,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb976 BookOpen,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API7 Bot,
8 Brain,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb979 Globe,
10 Tag,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API11 Code2,
12 GitBranch,
13 GitCommitHorizontal,
14 GitMerge,
15 Hand,
16 HeartPulse,
17 Loader2,
18 Lock,
19 Network,
A project is an app or a library: libraries show their package and how to ship a release, not production20 Package as PackageGlyph,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API21 Pin,
22 Rocket,
23 RotateCw,
24} from "lucide-react";
Fast pages, required checks on the branch, self-hosted runners, honest incidents25import { type ReactNode, Suspense } from "react";
Merge branch 'worktree-agent-a985c01f43538af01'26import { Await, Form, Link } from "react-router";
Projects: what a workspace builds and runs, first on every page27
A project is an app or a library: libraries show their package and how to ship a release, not production28import {
29 type AgentRun,
30 type G1tEvent,
31 type Memory,
32 type PackageSummary,
33 type Project,
34 type Pull,
35 RUN_KIND_LABEL,
36 isActiveRun,
37 workOwner,
38} from "@g1t/contracts";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API39
Projects: what a workspace builds and runs, first on every page40import type { Route } from "./+types/overview";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit41import { InlineMarkdown } from "../../components/inline-markdown";
Projects: what a workspace builds and runs, first on every page42import { host, StatusDot } from "../../components/deploy";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API43import { CheckBadge } from "../../components/checks";
44import { Elapsed, formatCost, useLiveRefresh } from "../../components/agents";
45import { 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 look46import { type ActiveBranch, ActiveBranches } from "../../components/branches";
47import { ProductionChecklist } from "../../components/checklist";
A project is an app or a library: libraries show their package and how to ship a release, not production48import { PackageIcon } from "../../components/package-icon";
Merge branch 'worktree-agent-ae1299e92e4462012'49import { Hint } from "../../components/ui/hint";
Merge branch 'worktree-agent-aca4790ea223aa235'50import { Loading, Skeleton, SkeletonRows } from "../../components/ui/skeleton";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look51import { ProductionShot } from "../../components/production-shot";
52import { GithubLinkStrip } from "../../components/github";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit53import { distinctFacts } from "../../lib/memory-facts";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look54import { githubApp } from "../../lib/github.server";
Merge branch 'worktree-agent-a985c01f43538af01'55import { 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 API56import { ChangeSize } from "../../components/work";
57import {
58 type ActivityItem,
59 type Need,
60 type PipelineStage,
61 PIPELINE,
62 TIME,
63 ageBuckets,
64 eventItem,
65 firstPassRate,
66 groupActivity,
67 passRate,
68 pipelineStage,
69 queuedNumbers,
70 rankNeeds,
71 stuckMinutes,
72} from "../../lib/mission";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9773import { agentWasAssigned, checklistPlan, hasInstructions, productionChecklist, releaseChecklist, startChecklist } from "../../lib/checklist";
A project is an app or a library: libraries show their package and how to ship a release, not production74import { ECOSYSTEM_LABEL, installCommands } from "../../lib/packages";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9775import {
76 PUBLISH_GUIDES,
77 bare,
78 hasRelease,
79 kindLabel,
80 linksToShow,
81 latestTag,
82 libraryPackages,
83 packageName,
84 packagePath,
85 projectChanges,
86 publishGuide,
87} from "../../lib/project-kind";
88import { AboutEditor, KindMenu, LinkList } from "../../components/project-about";
89import { DocsHead, ElsewhereHead, type ExternalDeployment, OtherHead, WhereItRuns } from "../../components/project-head";
A project is an app or a library: libraries show their package and how to ship a release, not production90import { 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-agent91import { madeByG1t } from "../../lib/opened-by";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look92import { assertSameOrigin, getViewer, requireUser } from "../../lib/session.server";
Fast pages, required checks on the branch, self-hosted runners, honest incidents93import { accessTo, countsFor, refusal, repoFor } from "../../lib/access.server";
Production thumbnails follow each deploy, not each commit, and old ones are cleared daily94import { shotVersion } from "./production-screenshot";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9795import { DeploymentsPanel } from "../../components/deployments-panel";
96import { environmentUrl, productionEnvironment } from "../../lib/deployments";
Merge branch 'worktree-agent-aaf03bdceac799c89'97import { cloneUrl, useAddresses } from "../../lib/addresses";
Branches and Tags pages, each file's last commit, and the branch menu on files98import { readBranches } from "../../lib/branches.server";
Merge checks: statuses and check runs on every commit99import { commitChecksFor } from "../../lib/commit-checks.server";
100import { CommitChecksBadge } from "../../components/commit-checks";
Projects: what a workspace builds and runs, first on every page101
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API102const MAX_LANDED = 6;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look103/** Branches read for the Active branches list, and shown. */
104const BRANCHES_READ = 10;
105const BRANCHES_SHOWN = 5;
A project is an app or a library: libraries show their package and how to ship a release, not production106/** A library's packages shown on its overview; the rest are a link away. */
107const PACKAGES_SHOWN = 3;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97108/** The default branch's latest commits listed. */
109const COMMITS_SHOWN = 5;
110/** How long Active branches may take before the section links to Branches instead. */
111const BRANCHES_WAIT_MS = 3_500;
Projects: what a workspace builds and runs, first on every page112
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily113/**
114 * The overview streams: the layout's header and tabs (one repository
115 * lookup, one project) go out at once with a skeleton below them, and
116 * everything here, seventeen calls across six services, follows in the
117 * same response as it settles. Crawlers wait for all of it
118 * (entry.server.tsx). Before, the first byte waited on the slowest of them.
119 */
120export function loader({ params, context }: Route.LoaderArgs) {
121 return { overview: overviewData({ params, context }) };
122}
123
124async function overviewData({ params, context }: Pick<Route.LoaderArgs, "params" | "context">) {
Projects: what a workspace builds and runs, first on every page125 const viewer = getViewer(context);
126 const path = { namespace: params.owner, name: params.repo };
127 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 API128 const now = Date.now();
129 // A section whose service fails shows its own empty state; the page stays up.
130 const soft = <T,>(promise: Promise<T> | null): Promise<T | null> =>
131 promise ? promise.catch((error) => (console.warn("overview:", error), null)) : Promise.resolve(null);
Fast pages, required checks on the branch, self-hosted runners, honest incidents132 // People with a role of their own here see its running parts: deployments,
133 // memory, setup; everyone who can read it sees the rest. Not awaited: only
134 // those sections wait on the repository lookup, which the layout makes in
135 // this request too.
136 const accessP = accessTo(context, params);
137 const memberP = accessP.then((access) => access.insider);
138 const forMembers = <T,>(start: () => Promise<T>): Promise<T | null> =>
139 memberP.then((member) => (member ? soft(start()) : null));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look140 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 production141 const projectP = soft(projects.get(params.owner, params.repo, viewer));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97142 // A library or a tool shows its packages where an app shows production,
143 // and every project g1t does not deploy counts a workflow in its checklist.
144 // Deployments from anywhere (g1t.page, g1t Actions, the API), by environment, for anyone who can read it.
145 const environmentsP = projectP.then((found) =>
146 soft(deployments.environments(found?.ok && found.value.source.kind === "hosted" ? found.value.source.repo : path, viewer)),
A project is an app or a library: libraries show their package and how to ship a release, not production147 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97148 const planP = projectP.then((project) => (project?.ok ? checklistPlan(project.value).plan : null));
149 const libraryRepoP = Promise.all([planP, repoP]).then(([plan, repo]) => (plan === "release" && repo?.ok ? repo.value : null));
A project is an app or a library: libraries show their package and how to ship a release, not production150 const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97151 const workflowsP = planP.then((plan) => (plan && plan !== "production" ? 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 API152 const eventsP = repoP.then((repo) => (repo?.ok ? soft(eventLog.list({ repoId: repo.value.id, limit: 150 })) : null));
153 const minePullsP = repoP.then((repo) =>
154 repo?.ok && viewer
155 ? soft(work.listActivePulls(viewer)).then((list) => (list ?? []).filter((item) => item.pull.repoId === repo.value.id))
156 : null,
157 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look158 const openP = soft(work.listPulls(path, viewer, "open"));
159 // Where its code came from on GitHub, for members.
Fast pages, required checks on the branch, self-hosted runners, honest incidents160 const githubP = repoP.then((repo) => (repo?.ok ? forMembers(() => githubApp.link(repo.value.id)) : null));
161 const listP = forMembers(() => deployments.list(ref, viewer));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look162 // Active branches: the newest few besides the default, with how far each
163 // has moved. Branches with an open pull request are read first.
164 const branchesP = Promise.all([repoP, soft(repos.branches(path, viewer)), openP, listP]).then(
165 async ([repo, branchList, pulls, deploys]): Promise<{ main: string; total: number; shown: ActiveBranch[] } | null> => {
166 if (!repo?.ok || !branchList?.ok) return null;
167 const main = repo.value.defaultBranch;
Branches and Tags pages, each file's last commit, and the branch menu on files168 if (branchList.value.every((branch) => branch.name === main)) return { main, total: 0, shown: [] };
169 const read = await readBranches(
170 path,
171 viewer,
172 {
173 defaultBranch: main,
174 branches: branchList.value,
175 pulls: pulls?.ok ? pulls.value : [],
176 previews: deploys?.ok ? deploys.value.live.filter((app) => app.kind === "preview") : [],
177 },
178 BRANCHES_READ,
179 );
180 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 look181 },
182 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97183 // Active branches read several logs each: streamed, so the rest shows
184 // first. Bounded well inside the response's stream timeout
185 // (entry.server.tsx): a promise still pending when the stream ends never
186 // settles in the browser, and its skeleton would stay.
187 const branches = Promise.race([
188 branchesP.catch(() => null),
189 new Promise<"slow">((resolve) => setTimeout(() => resolve("slow"), BRANCHES_WAIT_MS)),
190 ]);
191 const [{ insider: member, can }, project, settings, list, open, closed, log, counts, deps, runs, queue, issues, memories, recent, mine, domains, root, packageList, workflows, tagList] = await Promise.all([
Fast pages, required checks on the branch, self-hosted runners, honest incidents192 accessP,
A project is an app or a library: libraries show their package and how to ship a release, not production193 projectP,
Fast pages, required checks on the branch, self-hosted runners, honest incidents194 forMembers(() => deployments.settings(ref, viewer)),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look195 listP,
196 openP,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API197 soft(work.listPulls(path, viewer, "closed")),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97198 soft(repos.log(path, viewer, null, COMMITS_SHOWN)),
Fast pages, required checks on the branch, self-hosted runners, honest incidents199 soft(countsFor(context, params)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API200 soft(projects.dependencies(params.owner, params.repo, viewer)),
201 soft(agents.listRuns(viewer, { repo: path, limit: 60 })),
202 soft(work.queue(path, viewer)),
203 soft(work.listIssues(path, viewer, { state: "open" })),
Fast pages, required checks on the branch, self-hosted runners, honest incidents204 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 API205 eventsP,
206 minePullsP,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look207 // For the checklist, which only members see.
Fast pages, required checks on the branch, self-hosted runners, honest incidents208 forMembers(() => deployments.domains(ref, viewer)),
209 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 production210 packagesP,
211 workflowsP,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97212 // The latest release, for About.
213 soft(repos.tags(path, viewer)),
Projects: what a workspace builds and runs, first on every page214 ]);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API215 const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
216
217 const openPulls = ok(open) ?? [];
218 const runList = ok(runs) ?? [];
219 const live = runList.filter((run) => isActiveRun(run.status));
220 const builds = ok(list)?.deployments ?? [];
221 const landed = (ok(closed) ?? [])
222 .filter((pull) => pull.status === "merged")
223 .sort((a, b) => Date.parse(b.mergedAt ?? b.updatedAt) - Date.parse(a.mergedAt ?? a.updatedAt));
224 const queued = queuedNumbers(ok(queue)?.active ?? []);
225 const runOn = new Map<number, AgentRun>();
226 for (const run of live) if (run.number != null && !runOn.has(run.number)) runOn.set(run.number, run);
227 const lifecycleOf = new Map((mine ?? []).map((item) => [item.pull.number, item.lifecycle]));
228
229 // --- The pipeline -----------------------------------------------------------
230 type Card = {
231 number: number;
232 title: string;
233 agent: string;
234 status: Pull["status"];
235 checkStatus: Pull["checkStatus"];
236 step: string | null;
237 runKind: string | null;
238 runStarted: string | null;
239 needsYou: boolean;
240 at: string;
241 };
242 const columns: Record<PipelineStage, Card[]> = { working: [], checking: [], reviewing: [], queue: [], landed: [] };
243 for (const pull of openPulls.slice(0, 40)) {
244 const run = runOn.get(pull.number);
Fast pages, required checks on the branch, self-hosted runners, honest incidents245 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 API246 number: pull.number,
247 title: pull.title,
248 agent: pull.agent,
249 status: pull.status,
250 checkStatus: pull.checkStatus,
251 step: run?.step ?? null,
252 runKind: run ? RUN_KIND_LABEL[run.kind] : null,
253 runStarted: run ? (run.startedAt ?? run.createdAt) : null,
254 needsYou: lifecycleOf.get(pull.number)?.stage === "needs_you",
255 at: pull.updatedAt,
256 });
257 }
258 for (const pull of landed.filter((p) => now - Date.parse(p.mergedAt ?? p.updatedAt) < 3 * TIME.DAY).slice(0, 5)) {
259 columns.landed.push({
260 number: pull.number,
261 title: pull.title,
262 agent: pull.agent,
263 status: pull.status,
264 checkStatus: pull.checkStatus,
265 step: null,
266 runKind: null,
267 runStarted: null,
268 needsYou: false,
269 at: pull.mergedAt ?? pull.updatedAt,
270 });
271 }
272
273 // --- Needs you ---------------------------------------------------------------
274 const base = `/${params.owner}/${params.repo}`;
275 const needs: Need[] = [];
276 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
277 if (latestProduction?.status === "failed") {
278 needs.push({
279 key: "deploy",
280 kind: "deploy",
281 title: "The production build failed",
282 detail: latestProduction.error ?? "Production still serves the build before it.",
283 to: `${base}/deployments/${latestProduction.id}`,
284 action: "See the build",
285 at: Date.parse(latestProduction.finishedAt ?? latestProduction.createdAt),
286 where: latestProduction.commit.slice(0, 7),
287 });
288 }
289 const me = viewer?.username.toLowerCase();
290 for (const pull of openPulls) {
291 const to = `${base}/pull/${pull.number}`;
292 const where = `#${pull.number}`;
293 const lifecycle = lifecycleOf.get(pull.number);
294 if (lifecycle?.stage === "needs_you") {
295 const conflict = /conflict/i.test(lifecycle.detail);
296 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 });
297 } else if (lifecycle?.stage === "ready") {
298 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 });
299 }
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights300 // Never your own, nor one g1t made for you; whoever it is for asked.
301 const owner = workOwner(pull).username;
302 if (me && pull.status === "open" && owner.toLowerCase() !== me && pull.reviewers.some((name) => name.toLowerCase() === me)) {
303 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 API304 }
305 if (member && pull.status === "open" && pull.checkStatus === "failed" && !runOn.has(pull.number) && !lifecycle) {
Fast pages, required checks on the branch, self-hosted runners, honest incidents306 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 API307 }
308 }
309 for (const run of live) {
310 const minutes = stuckMinutes(run, now);
311 if (minutes == null) continue;
312 needs.push({
313 key: `run:${run.id}`,
314 kind: "stuck",
315 title: run.title ?? `${run.agent}'s run`,
316 detail: `${run.agent} has reported nothing for ${minutes} min${run.step ? `. Last: ${run.step}` : ""}.`,
317 to: `${base}/agents/runs/${run.id}`,
318 action: "Look",
319 at: Date.parse(run.updatedAt),
320 where: run.number != null ? `#${run.number}` : null,
321 });
322 }
323
324 // --- Activity and health -------------------------------------------------------
325 const eventList: G1tEvent[] = recent ?? [];
326 const items = eventList
327 .map((event) => eventItem(event, path))
328 .filter((item): item is ActivityItem => item != null);
329 const checkEvents = eventList.flatMap((event) =>
330 event.type === "checks.completed" ? [{ repo: "", number: event.data.number, at: Date.parse(event.time), passed: event.data.status === "passed" }] : [],
331 );
332 const memoryList = ok(memories)?.project ?? [];
Usage, Billing settings and prepaid AI credit; fixes from the UX audit333 // Pinned first, then the newest; a fact remembered twice, once.
334 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 API335 const openIssues = ok(issues);
336
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look337 // --- Getting to production ----------------------------------------------------
338 const wentLive = (status: string) => status === "ready" || status === "replaced" || status === "down";
339 const liveApps = ok(list)?.live ?? [];
340 const commitNow = ok(log)?.[0] ?? null;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97341 // Production deployed somewhere other than g1t.page (by g1t Actions, or
342 // reported through the API): the slot an app deployed elsewhere shows.
343 const environments = await environmentsP;
344 const environmentList = environments?.ok ? environments.value : null;
345 const reported = productionEnvironment(environmentList?.environments ?? []);
346 const deployment: ExternalDeployment | null =
347 reported?.latest && reported.latest.source !== "g1t_page"
348 ? {
349 environment: reported.name,
350 url: environmentUrl(reported),
351 state: reported.latest.state,
352 sha: reported.latest.sha,
353 created_at: reported.latest.updated_at,
354 }
355 : null;
356 // An app that has not said where it runs, but reports production from
357 // elsewhere, runs elsewhere.
358 const found = ok(project);
359 const projectValue = found && deployment && found.kind === "app" && found.runs == null ? { ...found, runs: "elsewhere" as const } : found;
360 const plan = projectValue ? checklistPlan(projectValue) : { plan: "production" as const, title: "Get to production" };
361 const isLibrary = plan.plan === "release";
A project is an app or a library: libraries show their package and how to ship a release, not production362 const published = libraryPackages(ok(packageList) ?? []);
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97363 const hasWorkflow = workflows?.ok ? workflows.value.length > 0 : null;
A project is an app or a library: libraries show their package and how to ship a release, not production364 const firstSteps = {
365 base,
366 hasCode: commitNow != null || projectValue?.source.kind === "mirror",
367 instructions: commitNow == null ? false : root?.ok ? hasInstructions(root.value.entries.map((entry) => entry.name)) : null,
368 agentAssigned: agentWasAssigned({
369 runAgents: runList.map((run) => run.agent),
370 pullAgents: [...openPulls, ...(ok(closed) ?? [])].map((pull) => pull.agent),
371 issues: openIssues ?? [],
372 }),
373 };
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97374 const links = projectValue?.links;
A project is an app or a library: libraries show their package and how to ship a release, not production375 const checklist = !member
376 ? null
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97377 : plan.plan !== "production" && plan.plan !== "release"
378 ? startChecklist(plan.plan, {
379 ...firstSteps,
380 hasWorkflow,
381 productionUrl: projectValue?.productionUrl ?? null,
382 hasLinks: !!links && (links.homepage != null || links.docs != null || links.custom.length > 0),
383 hasDocsLink: !!links && (links.docs != null || links.homepage != null),
384 })
A project is an app or a library: libraries show their package and how to ship a release, not production385 : isLibrary
386 ? releaseChecklist({
387 ...firstSteps,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97388 hasWorkflow,
A project is an app or a library: libraries show their package and how to ship a release, not production389 released: hasRelease(published),
390 releaseTo: published[0]
391 ? packagePath(published[0])
392 : (publishGuide(projectValue?.ecosystem ?? null)?.guide ?? "https://docs.g1t.sh/guides/packages/"),
393 })
394 : productionChecklist({
395 ...firstSteps,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look396 deploysEnabled: ok(settings)?.enabled ?? false,
397 productionDeployed:
398 liveApps.some((app) => app.kind === "production") ||
399 builds.some((build) => build.kind === "production" && wentLive(build.status)),
400 domains: ok(domains)?.domains.length ?? null,
401 previewOpened:
402 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 production403 });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look404
Merge checks: statuses and check runs on every commit405 // The checks on the latest commit and each active branch's head, in
406 // one call once the branches are read: streamed in beside them.
407 const checks = branches.then((found) =>
408 commitChecksFor(path, viewer, [commitNow?.hash, ...(found && found !== "slow" ? found.shown : []).map((branch) => branch.commit?.hash)]),
409 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look410 const github = await githubP;
Projects: what a workspace builds and runs, first on every page411 return {
412 member,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look413 can,
414 github,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97415 project: projectValue,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API416 settings: ok(settings),
417 builds: builds.slice(0, 30),
418 live: ok(list)?.live ?? [],
419 commit: ok(log)?.[0] ?? null,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97420 commits: (ok(log) ?? []).map((one) => ({ hash: one.hash, message: one.message.split("\n")[0] ?? "", author: one.author.name, at: one.authoredAt })),
421 release: latestTag(ok(tagList) ?? []),
422 checklistTitle: plan.title,
423 // Production as reported from outside g1t, for an app deployed elsewhere.
424 deployment,
425 // Every environment's latest deployment, for the Deployments panel.
426 environments: environmentList,
427 // About's languages and contributors, filled in by the files' About work.
428 languages: null as { name: string; share: number }[] | null,
429 contributors: null as { username: string }[] | null,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API430 open: ok(counts) ?? { issues: openIssues?.length ?? 0, pulls: openPulls.length },
431 dependencies: ok(deps),
432 agentsLive: live.slice(0, 6),
433 runsLoaded: runs?.ok ?? false,
434 pullsLoaded: open?.ok ?? false,
435 columns,
436 needs: rankNeeds(needs),
437 landed: landed.slice(0, MAX_LANDED),
438 groups: groupActivity(items).slice(0, 30),
439 eventsLoaded: recent != null,
440 health: {
441 passRate: passRate(checkEvents.map((c) => c.passed)),
442 firstPass: firstPassRate(checkEvents),
443 checkRuns: checkEvents.length,
444 ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null,
445 },
446 knows: knows as Memory[],
447 memoryCount: memoryList.length,
448 memoriesLoaded: memories?.ok ?? false,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look449 checklist,
A project is an app or a library: libraries show their package and how to ship a release, not production450 library: isLibrary
451 ? { packages: published.slice(0, PACKAGES_SHOWN), total: published.length, loaded: packageList?.ok ?? false }
452 : null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look453 branches,
Merge checks: statuses and check runs on every commit454 checks,
Projects: what a workspace builds and runs, first on every page455 };
456}
457
458export async function action({ request, params, context }: Route.ActionArgs) {
459 assertSameOrigin(request);
460 const user = requireUser(context, request);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look461 const form = await request.formData().catch(() => null);
462 const intent = form?.get("intent");
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97463 // What it is, where it runs, its description and links: its settings.
464 if (form && (intent === "kind" || intent === "about")) {
465 const refused = await refusal(context, params, "manage_settings");
466 if (refused) return { error: refused };
467 const saved = await projects.update(user, params.owner, params.repo, projectChanges(form));
468 return saved.ok ? { notice: "Saved." } : { error: saved.error.message };
469 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look470 // Syncing from GitHub is pushing; stopping it is an integration; deploying is compute.
471 const refused = await refusal(context, params, intent === "github-sync" ? "push" : intent === "github-stop" ? "manage_integrations" : "run");
472 if (refused) return { error: refused };
473 if (intent === "github-sync" || intent === "github-stop") {
474 const repo = await repos.get({ namespace: params.owner, name: params.repo }, user);
475 if (!repo.ok) return { error: repo.error.message };
476 const done =
477 intent === "github-sync" ? await githubApp.sync(user, repo.value.id) : await githubApp.unlinkRepo(user, repo.value.id);
478 return done.ok
479 ? { notice: intent === "github-sync" ? "Synced with GitHub." : "It is no longer kept in step with GitHub." }
480 : { error: done.error.message };
481 }
Projects: what a workspace builds and runs, first on every page482 const started = await deployments.redeploy(user, { workspace: params.owner, slug: params.repo }, null);
483 return started.ok ? { notice: "Production is building." } : { error: started.error.message };
484}
485
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily486type Loaded = Awaited<ReturnType<typeof overviewData>>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API487type Card = Loaded["columns"]["working"][number];
488
489const STAGE_TONE: Record<PipelineStage, string> = {
Merge branch 'worktree-agent-ad7c6d88d93adc817'490 working: "bg-accent",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API491 checking: "bg-info",
492 reviewing: "bg-warn",
Merge branch 'worktree-agent-ad7c6d88d93adc817'493 queue: "bg-success-dim",
494 landed: "bg-success",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API495};
496
497function PipelineCard({ card, base, stage }: { card: Card; base: string; stage: PipelineStage }) {
498 return (
499 <li>
500 <Link
501 to={`${base}/pull/${card.number}`}
502 prefetch="intent"
503 className={`block rounded-lg border bg-bg/60 p-2.5 transition-colors hover:border-line-strong hover:bg-raised ${
Merge branch 'worktree-agent-ad7c6d88d93adc817'504 card.needsYou ? "border-warn/50" : card.step ? "border-accent/30" : "border-line"
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API505 }`}
506 >
507 <span className="line-clamp-2 text-[0.8125rem] leading-snug font-medium">{card.title}</span>
508 <span className="mt-1.5 flex items-center gap-1.5 text-[0.6875rem] text-muted">
509 <Avatar name={card.agent} size={14} />
510 <span className="font-mono text-faint">#{card.number}</span>
511 <span className="grow" />
512 {card.needsYou ? (
Merge branch 'worktree-agent-ae1299e92e4462012'513 <Hint label="Stopped: a person decides what happens next">
514 <span className="inline-flex items-center gap-1 text-warn">
515 <Hand size={11} /> you
516 <span className="sr-only">: stopped, a person decides what happens next</span>
517 </span>
518 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API519 ) : stage === "landed" ? (
520 <TimeAgo at={card.at} />
521 ) : card.runStarted ? (
Merge branch 'worktree-agent-ad7c6d88d93adc817'522 <span className="inline-flex items-center gap-1 text-accent">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API523 <Loader2 size={11} className="animate-spin" />
524 <Elapsed from={card.runStarted} />
525 </span>
526 ) : (
527 <CheckBadge status={card.checkStatus} />
528 )}
529 </span>
530 {card.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'531 <Hint label={card.step}>
532 <span className="mt-1.5 block truncate font-mono text-[0.6875rem] text-fg/70">
533 {card.runKind}: {card.step}
534 </span>
535 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API536 )}
537 </Link>
538 </li>
539 );
540}
541
542/** Open pull requests moving through Working → Checking → Reviewing → Queue → Landed. */
543function Pipeline({ columns, base }: { columns: Loaded["columns"]; base: string }) {
544 return (
545 <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 look546 {/* Each stage as tall as what is in it: an empty one stays short
547 instead of stretching to the busiest. */}
548 <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 API549 {PIPELINE.map(({ stage, label }, index) => {
550 const cards = columns[stage];
551 return (
552 <li key={stage} className="flex min-w-0 flex-col rounded-xl border border-line bg-surface p-2">
553 <p className="flex items-center gap-2 px-1 pt-0.5 pb-2 text-xs font-medium text-muted">
554 <span className={`size-1.5 rounded-full ${STAGE_TONE[stage]} ${cards.length && stage !== "landed" ? "animate-pulse" : ""}`} />
555 {label}
556 <span className="tabular-nums text-faint">{cards.length}</span>
557 {index < PIPELINE.length - 1 && <ArrowRight size={11} className="ml-auto text-line-strong" />}
558 </p>
559 {cards.length === 0 ? (
560 <p className="rounded-lg border border-dashed border-line/70 px-2 py-3 text-center text-[0.6875rem] text-faint">Empty</p>
561 ) : (
562 <ul className="space-y-1.5">
563 {cards.slice(0, 6).map((card) => (
564 <PipelineCard key={card.number} card={card} base={base} stage={stage} />
565 ))}
566 {cards.length > 6 && (
567 <li className="px-1 text-[0.6875rem] text-muted">
568 <Link to={`${base}/pulls`} className="hover:text-fg">
569 {cards.length - 6} more
570 </Link>
571 </li>
572 )}
573 </ul>
574 )}
575 </li>
576 );
577 })}
578 </ol>
579 </div>
580 );
581}
582
A project is an app or a library: libraries show their package and how to ship a release, not production583/**
584 * Where an app's overview shows production, a library's shows what it
585 * publishes: each package with its latest version and how to install it,
586 * or, before there is one, how to publish a first version.
587 */
588function LibraryHead({
589 library,
590 project,
591 base,
592 workspace,
593}: {
594 library: { packages: PackageSummary[]; total: number; loaded: boolean };
595 project: Project | null;
596 base: string;
597 workspace: string;
598}) {
599 const guide = publishGuide(project?.ecosystem ?? null);
600 const name = project?.name ?? base.split("/").pop();
601 return (
602 <div className="p-5 sm:p-6">
603 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
604 <PackageGlyph size={13} className="text-accent" />
605 Packages
606 </p>
607 {library.packages.length > 0 ? (
608 <>
609 <ul className="mt-3 space-y-4">
610 {library.packages.map((pkg) => {
611 const commands = installCommands(pkg, pkg.latest, "you");
612 return (
613 <li key={pkg.id} className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:gap-5">
614 <Link to={packagePath(pkg)} className="group flex min-w-0 items-center gap-3 lg:w-72 lg:shrink-0">
615 <PackageIcon ecosystem={pkg.ecosystem} size={32} />
616 <span className="min-w-0">
617 <span className="block truncate font-mono text-[0.9375rem] font-medium group-hover:text-accent">{packageName(pkg)}</span>
618 <span className="block text-xs text-muted">
619 {ECOSYSTEM_LABEL[pkg.ecosystem]} · {pkg.latest ?? "no versions yet"}
620 {pkg.versions > 1 && ` · ${pkg.versions} versions`}
621 </span>
622 </span>
623 </Link>
624 <div className="min-w-0 grow space-y-1.5">
625 {/* The workspace's registry first, or the tool installs a package of the same name from elsewhere. */}
626 {commands.registry && <CopyLine prompt text={commands.registry} />}
627 <CopyLine prompt text={commands.install} />
628 </div>
629 </li>
630 );
631 })}
632 </ul>
633 {library.total > library.packages.length && (
634 <Link to={`/${workspace}/-/packages`} className="mt-3 inline-block text-xs text-muted hover:text-fg">
635 All {library.total} packages
636 </Link>
637 )}
638 </>
639 ) : (
640 <>
641 <p className="mt-2 text-lg font-medium">{library.loaded ? "Not published yet" : "Packages could not be loaded just now"}</p>
642 {guide ? (
643 <>
644 <p className="mt-1 max-w-lg text-sm text-muted">
645 Publish a first version of {name} to the workspace's {guide.label} registry, for others to install
646 {guide.start ? ":" : "."}
647 </p>
648 {guide.start && (
649 <div className="mt-3 max-w-lg">
650 <CopyLine prompt text={guide.start} />
651 </div>
652 )}
653 <a href={guide.guide} className="mt-3 inline-flex items-center gap-1 text-sm text-accent hover:underline">
654 Publishing to {guide.label} <ArrowUpRight size={13} />
655 </a>
656 </>
657 ) : (
658 <>
659 <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>
660 <p className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm">
661 {PUBLISH_GUIDES.map((one) => (
662 <a key={one.label} href={one.guide} className="inline-flex items-center gap-1 text-accent hover:underline">
663 {one.label} <ArrowUpRight size={13} />
664 </a>
665 ))}
666 </p>
667 </>
668 )}
669 </>
670 )}
671 </div>
672 );
673}
674
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API675function Stat({ label, value, to }: { label: string; value: ReactNode; to?: string }) {
676 const body = (
677 <>
678 <span className="block text-[0.6875rem] text-faint">{label}</span>
679 <span className="mt-0.5 block truncate text-[0.8125rem] font-medium">{value}</span>
680 </>
681 );
682 return to ? (
683 <Link to={to} className="min-w-0 px-5 py-3 transition-colors hover:bg-raised/50">
684 {body}
685 </Link>
686 ) : (
687 <div className="min-w-0 px-5 py-3">{body}</div>
688 );
689}
690
Projects: what a workspace builds and runs, first on every page691export default function ProjectOverview({ loaderData, actionData, params }: Route.ComponentProps) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily692 return (
693 <Suspense fallback={<OverviewSkeleton />}>
694 <Await resolve={loaderData.overview} errorElement={<OverviewFailed />}>
Merge branch 'worktree-agent-aca4790ea223aa235'695 {(overview) => (
696 // Faded in over its outline, not switched for it.
697 <div className="animate-fade-in">
698 <Overview loaderData={overview} actionData={actionData} params={params} />
699 </div>
700 )}
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily701 </Await>
702 </Suspense>
703 );
704}
705
706/** The overview's sections as they will sit, while they stream in. */
707function OverviewSkeleton() {
708 return (
Merge branch 'worktree-agent-aca4790ea223aa235'709 <Loading className="space-y-8" aria-label="Loading the overview">
710 {/* Production: its screenshot beside where it is live, then the facts under them. */}
711 <section aria-hidden="true" className="overflow-hidden rounded-2xl border border-line bg-surface">
712 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
713 <Skeleton className="aspect-[16/10] w-full shrink-0 rounded-lg sm:w-72" />
714 <div className="min-w-0 grow">
715 <Skeleton className="h-3 w-24" />
716 <Skeleton className="mt-3 h-5 w-72 max-w-full" />
717 <Skeleton className="mt-3 h-3 w-48 max-w-full" />
718 </div>
719 </div>
720 <div className="grid grid-cols-2 gap-px border-t border-line bg-line sm:grid-cols-4">
721 {Array.from({ length: 4 }, (_, index) => (
722 <div key={index} className="bg-surface px-5 py-4">
723 <Skeleton className="h-3 w-16" />
724 <Skeleton className="mt-2.5 h-4 w-28" />
725 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily726 ))}
727 </div>
728 </section>
Merge branch 'worktree-agent-aca4790ea223aa235'729 {/* Right now: the work, column by column. */}
730 <div aria-hidden="true" className="grid grid-cols-2 gap-2 sm:grid-cols-5">
731 {Array.from({ length: 5 }, (_, index) => (
732 <div key={index} className="rounded-xl border border-line bg-surface p-3">
733 <Skeleton className="h-3 w-20" />
734 <Skeleton className="mt-3 h-14 rounded-lg" />
735 </div>
736 ))}
737 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily738 <div className="grid gap-6 lg:grid-cols-2">
739 <section className="rounded-2xl border border-line bg-surface">
740 <SkeletonRows rows={4} />
741 </section>
742 <section className="rounded-2xl border border-line bg-surface">
743 <SkeletonRows rows={4} />
744 </section>
745 </div>
Merge branch 'worktree-agent-aca4790ea223aa235'746 </Loading>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily747 );
748}
749
750function OverviewFailed() {
751 return (
752 <p className="rounded-2xl border border-line bg-surface p-6 text-sm text-muted">
753 The overview could not be loaded just now. Reload the page to try again.
754 </p>
755 );
756}
757
758function Overview({
759 loaderData,
760 actionData,
761 params,
762}: {
763 loaderData: Loaded;
764 actionData: Route.ComponentProps["actionData"];
765 params: Route.ComponentProps["params"];
766}) {
Merge checks: statuses and check runs on every commit767 const { member, project, settings, builds, live, commit, open, dependencies, agentsLive, columns, needs, landed, groups, health, knows, checklist, branches, library, checks } =
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look768 loaderData;
Projects: what a workspace builds and runs, first on every page769 const base = `/${params.owner}/${params.repo}`;
770 const production = live.find((app) => app.kind === "production") ?? null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains771 // The project's own domain, once it is active, is where production is visited.
772 const productionUrl = production ? (settings?.primaryDomain ? `https://${settings.primaryDomain}` : production.url) : null;
Projects: what a workspace builds and runs, first on every page773 const previews = live.filter((app) => app.kind === "preview");
774 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97775 const plan = project ? checklistPlan(project).plan : "production";
776 const canChange = member && loaderData.can.manage_settings;
777 // Its homepage and docs beside what it is, unless the card below already links there.
778 const headUrl =
779 plan === "production"
780 ? (productionUrl ?? settings?.productionUrl)
781 : plan === "elsewhere"
782 ? (loaderData.deployment?.url ?? project?.productionUrl)
783 : plan === "docs"
784 ? (project?.links.docs ?? project?.productionUrl ?? project?.links.homepage)
785 : plan === "other"
786 ? (project?.links.homepage ?? project?.links.docs)
787 : null;
788 const stripLinks = project ? linksToShow({ ...project.links, custom: [] }, [headUrl]) : [];
Merge branch 'worktree-agent-aaf03bdceac799c89'789 const addresses = useAddresses();
Projects: what a workspace builds and runs, first on every page790 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 API791 const moving = agentsLive.length > 0 || columns.working.length + columns.checking.length > 0;
792 useLiveRefresh(moving);
793 const ages = health.ages;
794 const oldest = ages ? Math.max(1, ...ages.map((a) => a.count)) : 1;
Projects: what a workspace builds and runs, first on every page795
796 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API797 <div className="space-y-8">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look798 {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 API799 {/* What the project is, running, and where its code is. */}
800 <section className="overflow-hidden rounded-2xl border border-line bg-surface">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97801 {project && (
802 <div className="flex flex-wrap items-center gap-x-4 gap-y-2 border-b border-line px-5 py-2.5 text-xs sm:px-6">
803 <KindMenu project={project} label={kindLabel(project)} canChange={canChange} />
804 {stripLinks.map((link) => (
805 <a key={link.key} href={link.url} rel="noopener noreferrer" className="inline-flex min-w-0 items-center gap-1.5 text-muted hover:text-fg">
806 {link.type === "docs" ? <BookOpen size={12} className="shrink-0 text-faint" /> : <Globe size={12} className="shrink-0 text-faint" />}
807 <span className="truncate">{link.type === "docs" ? `Docs · ${bare(link.url)}` : link.label}</span>
808 </a>
809 ))}
810 </div>
811 )}
812 {plan === "elsewhere" && project ? (
813 <ElsewhereHead
814 project={project}
815 base={base}
816 canChange={canChange}
817 canDeploy={member && loaderData.can.manage_integrations}
818 deployment={loaderData.deployment}
819 commit={commit}
Merge checks: statuses and check runs on every commit820 checks={commit ? <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" /> : undefined}
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97821 />
822 ) : plan === "unknown" && project ? (
823 <WhereItRuns project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
824 ) : plan === "docs" && project ? (
825 <DocsHead project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
826 ) : plan === "other" && project ? (
827 <OtherHead project={project} base={base} canChange={canChange} />
828 ) : library ? (
829 <LibraryHead library={library} project={project} base={base} workspace={params.owner} />
A project is an app or a library: libraries show their package and how to ship a release, not production830 ) : (
831 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
832 {member && (production || settings?.enabled) && (
833 <ProductionShot
834 src={production ? `${base}/production.jpg?v=${shotVersion(production)}` : null}
835 href={production ? (productionUrl ?? production.url) : null}
836 label={host(production ? (productionUrl ?? production.url) : (settings?.productionUrl ?? ""))}
837 className="w-full shrink-0 sm:w-60 lg:w-72"
838 />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API839 )}
A project is an app or a library: libraries show their package and how to ship a release, not production840 <div className="flex min-w-0 grow flex-wrap items-start justify-between gap-4">
841 <div className="min-w-0">
842 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
843 <Rocket size={13} className="text-accent" />
844 Production
845 </p>
846 {production ? (
847 <>
848 <a
849 href={productionUrl ?? production.url}
850 className="mt-2 flex items-center gap-1.5 truncate font-mono text-lg font-medium hover:text-accent"
851 >
852 {host(productionUrl ?? production.url)}
853 <ArrowUpRight size={16} className="shrink-0 text-faint" />
854 </a>
855 <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted">
856 {latestProduction && <StatusDot status={latestProduction.status} label={latestProduction.status === "ready" ? "Live" : undefined} />}
857 <span className="inline-flex items-center gap-1 font-mono">
858 <GitCommitHorizontal size={13} className="text-faint" />
859 {production.commit.slice(0, 7)}
860 </span>
861 <span>
862 deployed <TimeAgo at={production.deployedAt} />
863 </span>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97864 {settings?.primaryDomain && <span className="text-faint">also at {host(production.url)}</span>} </p>
A project is an app or a library: libraries show their package and how to ship a release, not production865 </>
866 ) : settings?.enabled ? (
867 <>
868 <p className="mt-2 font-mono text-lg text-muted">{host(settings.productionUrl)}</p>
869 <p className="mt-1.5 text-xs text-muted">
870 {latestProduction ? (
871 <StatusDot status={latestProduction.status} label={latestProduction.status === "failed" ? "The last build failed" : undefined} />
872 ) : (
873 "Not deployed yet. Push to the default branch, or deploy it now."
874 )}
875 </p>
876 </>
877 ) : (
878 <>
879 <p className="mt-2 text-lg font-medium">Deployments are off</p>
880 <p className="mt-1 max-w-lg text-sm text-muted">
881 Nothing builds or runs until you turn them on. Then {project?.name ?? params.repo} goes up on g1t.page:
882 production from the default branch, and a live preview for every pull request. It runs only while
883 someone visits.
884 </p>
885 </>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API886 )}
Projects: what a workspace builds and runs, first on every page887 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production888 {member && (
889 <div className="flex shrink-0 items-center gap-2">
890 {production && (
891 <ButtonLink to={productionUrl ?? production.url} variant="accent" reloadDocument>
892 Visit
893 <ArrowUpRight size={14} />
894 </ButtonLink>
895 )}
896 {settings?.enabled ? (
897 loaderData.can.run && <Form method="post">
Merge branch 'worktree-agent-ae1299e92e4462012'898 <Hint label="Build production again from the default branch">
899 <SubmitButton variant="quiet" name="intent" value="redeploy" pending="Redeploying…">
900 <RotateCw size={14} />
901 Redeploy
902 </SubmitButton>
903 </Hint>
A project is an app or a library: libraries show their package and how to ship a release, not production904 </Form>
905 ) : loaderData.can.manage_integrations && (
906 <ButtonLink to={`${base}/settings/deployments`} variant="accent">
907 <Rocket size={14} />
908 Turn on deployments
909 </ButtonLink>
910 )}
911 </div>
912 )}
913 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look914 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production915 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API916 {actionData && (
Merge branch 'worktree-agent-ad7c6d88d93adc817'917 <p className={`border-t border-line px-6 py-2.5 text-sm ${"error" in actionData ? "text-danger" : "text-success"}`}>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API918 {"error" in actionData ? actionData.error : actionData.notice}
919 </p>
920 )}
921 <div className="grid grid-cols-2 divide-line border-t border-line text-sm sm:grid-cols-4 sm:divide-x">
922 <Stat
923 label="Source"
924 to={`${base}/code`}
925 value={
926 source ? (
927 <span className="inline-flex items-center gap-1.5 font-mono">
928 {project?.private ? <Lock size={12} className="text-faint" /> : <Code2 size={12} className="text-faint" />}
929 {source.repo.name}
930 {source.rootDir ? `/${source.rootDir}` : ""}
931 </span>
932 ) : (
933 "—"
934 )
935 }
936 />
937 <Stat
938 label="Default branch"
939 to={`${base}/commits`}
940 value={
941 <span className="inline-flex items-center gap-1.5 font-mono">
942 <GitBranch size={12} className="text-faint" />
943 {source?.defaultBranch ?? "main"}
944 </span>
945 }
946 />
947 <Stat
948 label="Latest commit"
949 value={
950 commit ? (
Merge checks: statuses and check runs on every commit951 <span className="flex min-w-0 items-center gap-1">
952 <Hint label={commit.message.split("\n")[0]}>
953 <Link to={`${base}/commit/${commit.hash}`} className="min-w-0 truncate hover:text-accent">
954 <span className="font-mono">{commit.hash.slice(0, 7)}</span>{" "}
955 <span className="font-normal text-muted">
956 <TimeAgo at={commit.authoredAt} />
957 </span>
958 </Link>
959 </Hint>
960 {/* A button, so beside the link rather than in it. */}
961 <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" />
962 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API963 ) : (
964 "No commits yet"
965 )
966 }
967 />
968 <Stat
A project is an app or a library: libraries show their package and how to ship a release, not production969 label={member && settings?.enabled && !library ? "Last deploy" : "Open"}
970 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 API971 value={
A project is an app or a library: libraries show their package and how to ship a release, not production972 member && settings?.enabled && !library ? (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API973 latestProduction ? (
974 <span className="inline-flex items-center gap-2">
975 <StatusDot status={latestProduction.status} />
976 <span className="font-normal text-muted">
977 <TimeAgo at={latestProduction.createdAt} />
Projects: what a workspace builds and runs, first on every page978 </span>
979 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API980 ) : (
981 "None yet"
982 )
983 ) : (
984 `${open.pulls} pull requests · ${open.issues} issues`
985 )
986 }
987 />
988 </div>
989 {commit && (
990 <p className="truncate border-t border-line px-5 py-2.5 text-xs text-muted sm:px-6">
991 <GitCommitHorizontal size={12} className="mr-1.5 inline text-faint" />
992 {commit.message.split("\n")[0]} <span className="text-faint">· {commit.author.name}</span>
993 </p>
994 )}
995 </section>
996
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97997 {checklist && <ProductionChecklist base={base} items={checklist} title={loaderData.checklistTitle} />}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look998
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API999 <Panel
1000 title="Right now"
Merge branch 'worktree-agent-ad7c6d88d93adc817'1001 icon={<span className={`block size-2 rounded-full ${agentsLive.length > 0 ? "animate-pulse bg-accent" : "bg-line-strong"}`} />}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1002 count={agentsLive.length}
1003 all={{ to: `${base}/agents`, label: "Agents" }}
1004 >
1005 {agentsLive.length > 0 && (
1006 <ul className="mb-3 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
1007 {agentsLive.map((run) => (
Merge branch 'worktree-agent-ad7c6d88d93adc817'1008 <li key={run.id} className="min-w-0 rounded-xl border border-accent/30 bg-surface px-3.5 py-3">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1009 <div className="flex items-center gap-2 text-sm">
1010 <Avatar name={run.agent} size={18} />
1011 <span className="font-medium">{run.agent}</span>
1012 <span className="text-xs text-muted">{RUN_KIND_LABEL[run.kind].toLowerCase()}</span>
1013 <span className="grow" />
1014 <span className="text-xs text-muted">
1015 <Elapsed from={run.startedAt ?? run.createdAt} />
Projects: what a workspace builds and runs, first on every page1016 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1017 </div>
1018 <Link
1019 to={run.number != null ? `${base}/pull/${run.number}` : `${base}/agents/runs/${run.id}`}
1020 className="mt-1 block truncate text-[0.8125rem] hover:text-accent"
1021 >
1022 {run.title ?? "A run"} {run.number != null && <span className="text-faint">#{run.number}</span>}
1023 </Link>
1024 {run.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'1025 <Hint label={run.step}>
1026 <p className="mt-1 truncate font-mono text-[0.6875rem] text-fg/70">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1027 <span className="mr-1.5 inline-block size-1.5 animate-pulse rounded-full bg-accent align-middle" />
Merge branch 'worktree-agent-ae1299e92e4462012'1028 {run.step}
1029 </p>
1030 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1031 )}
1032 <p className="mt-1.5 flex items-center gap-3 text-[0.6875rem] text-faint">
1033 {formatCost(run.costUsd) && <span>{formatCost(run.costUsd)} so far</span>}
1034 <Link to={`${base}/agents/runs/${run.id}`} className="hover:text-fg">
1035 {run.stepCount} steps
1036 </Link>
1037 {run.number != null && (
1038 <Link to={`${base}/sessions/${run.number}`} className="hover:text-fg">
1039 Session
1040 </Link>
1041 )}
1042 </p>
1043 </li>
1044 ))}
1045 </ul>
1046 )}
1047 {!loaderData.pullsLoaded ? (
1048 <Unavailable what="Pull requests" />
1049 ) : Object.values(columns).every((c) => c.length === 0) ? (
1050 <Quiet
1051 action={
1052 member ? (
1053 <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">
1054 <Bot size={12} /> Open an issue for an agent
1055 </Link>
1056 ) : null
1057 }
1058 >
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1059 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 API1060 </Quiet>
1061 ) : (
1062 <Pipeline columns={columns} base={base} />
Projects: what a workspace builds and runs, first on every page1063 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1064 </Panel>
Projects: what a workspace builds and runs, first on every page1065
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1066 <div className="grid gap-x-8 gap-y-8 lg:grid-cols-[minmax(0,1fr)_20rem]">
1067 <div className="min-w-0 space-y-8">
1068 {needs.length > 0 && (
1069 <Panel title="Needs you" icon={<Hand size={14} className="text-warn" />} count={needs.length}>
1070 <NeedsList needs={needs} limit={5} />
1071 </Panel>
1072 )}
1073
Fast pages, required checks on the branch, self-hosted runners, honest incidents1074 <Suspense
1075 fallback={
1076 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1077 <div aria-busy="true">
1078 <SkeletonRows rows={3} rowClassName="h-12" />
1079 </div>
1080 </Panel>
1081 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1082 >
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971083 <Await
1084 resolve={branches}
1085 errorElement={
1086 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1087 <Unavailable what="Branches" />
1088 </Panel>
1089 }
1090 >
Fast pages, required checks on the branch, self-hosted runners, honest incidents1091 {(branches) => (
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971092 <Panel
1093 title="Active branches"
1094 icon={<GitBranch size={14} />}
1095 count={branches && branches !== "slow" ? branches.total || null : null}
1096 all={{ to: `${base}/branches`, label: "All branches" }}
1097 >
1098 {branches === "slow" ? (
1099 <Quiet>
1100 Its branches are taking a while to compare.{" "}
1101 <Link to={`${base}/branches`} className="text-accent hover:underline">
1102 See them on Branches
1103 </Link>
1104 .
1105 </Quiet>
1106 ) : !branches ? (
Fast pages, required checks on the branch, self-hosted runners, honest incidents1107 <Unavailable what="Branches" />
1108 ) : branches.shown.length === 0 ? (
1109 <Quiet>
1110 Only {branches.main} so far. Branches pushed here show with how far each has moved from {branches.main}, and
1111 its pull request and preview.
1112 </Quiet>
1113 ) : (
1114 <>
Merge checks: statuses and check runs on every commit1115 <ActiveBranches branches={branches.shown} base={base} main={branches.main} checks={checks} />
Fast pages, required checks on the branch, self-hosted runners, honest incidents1116 {branches.total > branches.shown.length && (
1117 <p className="mt-2 px-1 text-xs text-faint">
1118 The {branches.shown.length} most recently changed of {branches.total} branches.
1119 </p>
1120 )}
1121 </>
1122 )}
1123 </Panel>
1124 )}
1125 </Await>
1126 </Suspense>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1127
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1128 <Panel title="Recent changes" icon={<GitMerge size={14} />} all={{ to: `${base}/pulls?state=closed`, label: "All landed" }}>
1129 {landed.length === 0 ? (
1130 <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 page1131 ) : (
1132 <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 API1133 {landed.map((pull) => {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1134 const byAgent = pull.runtime === "hosted" || madeByG1t(pull);
Projects: what a workspace builds and runs, first on every page1135 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1136 <li key={pull.id} className="flex items-start gap-3 px-4 py-3">
1137 <GitMerge size={15} className="mt-0.5 shrink-0 text-merged" />
1138 <span className="min-w-0 grow">
1139 <Link to={`${base}/pull/${pull.number}`} prefetch="intent" className="block truncate text-sm font-medium hover:text-accent">
1140 {pull.title}
1141 </Link>
1142 <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted">
1143 <span className="font-mono text-faint">#{pull.number}</span>
1144 <span>·</span>
1145 <span className="inline-flex items-center gap-1">
1146 <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 rights1147 {pull.requestedBy
1148 ? `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-agent1149 : byAgent
1150 ? `made by ${pull.agent}`
1151 : `by ${pull.author.username}`}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1152 </span>
1153 {pull.issue != null && (
1154 <>
1155 <span>· for</span>
1156 <Link to={`${base}/issues/${pull.issue}`} className="hover:text-fg">
1157 #{pull.issue}
1158 </Link>
1159 </>
1160 )}
1161 {pull.mergedBy && <span>· landed by {pull.mergedBy}</span>}
1162 <span>
1163 · <TimeAgo at={pull.mergedAt ?? pull.updatedAt} />
Projects: what a workspace builds and runs, first on every page1164 </span>
1165 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1166 </span>
1167 <span className="hidden shrink-0 text-xs text-faint sm:block">
1168 <ChangeSize files={pull.files} />
1169 </span>
1170 {byAgent && (
1171 <Link to={`${base}/sessions/${pull.number}`} className="shrink-0 text-xs text-muted hover:text-fg">
1172 Session
1173 </Link>
1174 )}
Projects: what a workspace builds and runs, first on every page1175 </li>
1176 );
1177 })}
1178 </ul>
1179 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1180 </Panel>
Projects: what a workspace builds and runs, first on every page1181
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971182 {loaderData.commits.length > 0 && (
1183 <Panel
1184 title={`Latest on ${source?.defaultBranch ?? "main"}`}
1185 icon={<GitCommitHorizontal size={14} />}
1186 all={{ to: `${base}/commits`, label: "All commits" }}
1187 >
1188 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1189 {loaderData.commits.map((one) => (
1190 <li key={one.hash} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1191 <Avatar name={one.author} size={18} />
1192 <span className="min-w-0 grow">
1193 <Link to={`${base}/commit/${one.hash}`} className="block truncate hover:text-accent">
1194 {one.message}
1195 </Link>
1196 <span className="text-xs text-muted">{one.author}</span>
1197 </span>
1198 <Link to={`${base}/commit/${one.hash}`} className="hidden shrink-0 font-mono text-xs text-faint hover:text-fg sm:block">
1199 {one.hash.slice(0, 7)}
1200 </Link>
1201 <span className="w-16 shrink-0 text-right text-xs text-faint">
1202 <TimeAgo at={one.at} />
1203 </span>
1204 </li>
1205 ))}
1206 </ul>
1207 </Panel>
1208 )}
1209
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1210 <Panel title="Activity" icon={<ActivityIcon size={14} />}>
1211 {loaderData.eventsLoaded ? (
1212 <ActivityFeed groups={groups} showRepo={false} limit={8} empty="Nothing has happened here yet." />
1213 ) : (
1214 <Unavailable what="Activity" />
1215 )}
1216 </Panel>
1217
1218 {previews.length > 0 && (
1219 <Panel title="Previews" icon={<GitBranch size={14} />} count={previews.length} all={{ to: `${base}/deployments`, label: "Deployments" }}>
1220 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1221 {previews.map((app) => (
1222 <li key={app.url} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1223 <span className="min-w-0 grow">
1224 <a href={app.url} className="block truncate font-mono text-[0.8125rem] hover:text-accent">
1225 {host(app.url)}
1226 </a>
1227 <span className="text-xs text-muted">
1228 {app.branch}
1229 {app.number != null && (
1230 <>
1231 {" · "}
1232 <Link to={`${base}/pull/${app.number}`} className="hover:underline">
1233 #{app.number}
1234 </Link>
1235 </>
1236 )}
1237 </span>
Projects: what a workspace builds and runs, first on every page1238 </span>
1239 <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 API1240 <TimeAgo at={app.deployedAt} />
Projects: what a workspace builds and runs, first on every page1241 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1242 </li>
1243 ))}
1244 </ul>
1245 </Panel>
1246 )}
1247 </div>
Projects: what a workspace builds and runs, first on every page1248
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1249 <aside className="space-y-4 text-sm">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971250 {/* Deployments panel (deployments-panel.tsx): each environment's latest deployment. */}
1251 <DeploymentsPanel base={base} summary={loaderData.environments} className="rounded-xl border border-line bg-surface p-5" />
1252 {project && (
1253 <section className="rounded-xl border border-line bg-surface p-5">
1254 <div className="flex items-center justify-between">
1255 <h2 className="text-sm font-semibold">About</h2>
1256 {canChange && <AboutEditor project={project} />}
1257 </div>
1258 <p className={`mt-2.5 text-sm ${project.description ? "text-fg-soft" : "text-faint"}`}>
1259 {project.description ?? "No description."}
1260 </p>
1261 <LinkList links={project.links} className="mt-3" />
1262 <ul className="mt-4 space-y-2 text-xs text-muted">
1263 <li className="flex items-center gap-2">
1264 {project.kind === "library" || project.kind === "tool" ? (
1265 <PackageGlyph size={13} className="shrink-0 text-faint" />
1266 ) : project.kind === "docs" ? (
1267 <BookOpen size={13} className="shrink-0 text-faint" />
1268 ) : (
1269 <Rocket size={13} className="shrink-0 text-faint" />
1270 )}
1271 <Hint label={project.kindReason.detail}>
1272 <span>{kindLabel(project)}</span>
1273 </Hint>
1274 </li>
1275 {loaderData.release && (
1276 <li className="flex items-center gap-2">
1277 <Tag size={13} className="shrink-0 text-faint" />
1278 <Link to={`${base}/tags`} className="hover:text-fg">
1279 <span className="font-mono text-fg-soft">{loaderData.release.name}</span>
1280 {loaderData.release.at && (
1281 <span className="text-faint">
1282 {" · "}
1283 <TimeAgo at={loaderData.release.at} />
1284 </span>
1285 )}
1286 </Link>
1287 </li>
1288 )}
1289 {loaderData.languages && loaderData.languages.length > 0 && (
1290 <li className="flex items-center gap-2">
1291 <Code2 size={13} className="shrink-0 text-faint" />
1292 <span>
1293 {loaderData.languages
1294 .slice(0, 3)
1295 .map((language) => `${language.name} ${Math.round(language.share * 100)}%`)
1296 .join(" · ")}
1297 </span>
1298 </li>
1299 )}
1300 </ul>
1301 {loaderData.contributors && loaderData.contributors.length > 0 && (
1302 <div className="mt-4 flex flex-wrap gap-1">
1303 {loaderData.contributors.slice(0, 12).map((person) => (
1304 <Hint key={person.username} label={person.username}>
1305 <Link to={`/${person.username}`}>
1306 <Avatar name={person.username} size={22} />
1307 </Link>
1308 </Hint>
1309 ))}
1310 </div>
1311 )}
1312 </section>
1313 )}
1314
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1315 {member && (
1316 <section className="rounded-xl border border-line bg-surface p-5">
1317 <div className="flex items-center justify-between">
1318 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1319 <Brain size={14} className="text-accent" />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1320 What agents know here
1321 </h2>
1322 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1323 Memory
1324 </Link>
Projects: what a workspace builds and runs, first on every page1325 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1326 {!loaderData.memoriesLoaded ? (
1327 <p className="mt-3 text-xs text-muted">Memory could not be loaded just now.</p>
1328 ) : knows.length === 0 ? (
1329 <p className="mt-3 text-xs leading-5 text-muted">
1330 Nothing yet. Agents note conventions, decisions and traps as they work, and every agent starting here reads them.
1331 </p>
1332 ) : (
1333 <ul className="mt-3 space-y-2.5">
1334 {knows.map((memory) => (
1335 <li key={memory.id} className="text-xs leading-5">
1336 <p className="line-clamp-3 text-fg-soft">
1337 {memory.pinned && <Pin size={11} className="mr-1 inline text-accent" />}
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1338 <InlineMarkdown text={memory.text} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1339 </p>
1340 <p className="text-faint">
1341 {memory.kind} · {memory.createdBy} · <TimeAgo at={memory.createdAt} />
1342 </p>
1343 </li>
1344 ))}
1345 {loaderData.memoryCount > knows.length && (
1346 <li>
1347 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1348 {loaderData.memoryCount - knows.length} more
1349 </Link>
1350 </li>
1351 )}
1352 </ul>
1353 )}
1354 </section>
Projects: what a workspace builds and runs, first on every page1355 )}
1356
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1357 <section className="rounded-xl border border-line bg-surface p-5">
1358 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1359 <HeartPulse size={14} className="text-faint" />
1360 Health
Project dependencies: addresses, preview stacks, Affects, and agents who know1361 </h2>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1362 <div className="mt-4 space-y-4 text-xs">
1363 <div>
1364 <div className="flex items-baseline justify-between">
1365 <span className="text-muted">Checks passing</span>
1366 <span className="font-medium tabular-nums">{percent(health.passRate)}</span>
1367 </div>
1368 <div className="mt-1.5">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1369 <Meter value={health.passRate} tone={health.passRate != null && health.passRate < 0.7 ? "bg-warn" : "bg-success"} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1370 </div>
1371 <p className="mt-1 text-faint">
1372 {health.checkRuns
1373 ? `${health.checkRuns} recent runs · ${percent(health.firstPass.rate)} pass on the first try`
1374 : "No checks have run recently."}
1375 </p>
1376 </div>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971377 {member && (plan === "production" || builds.length > 0) && (
Project dependencies: addresses, preview stacks, Affects, and agents who know1378 <div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1379 <div className="flex items-baseline justify-between">
1380 <span className="text-muted">Deploys</span>
1381 <Link to={`${base}/deployments`} className="text-faint hover:text-fg">
1382 {builds.length ? `last ${Math.min(builds.length, 20)}` : "none yet"}
1383 </Link>
1384 </div>
1385 <div className="mt-1.5">
1386 <DeployStrip builds={builds} base={base} />
1387 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1388 </div>
1389 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1390 <div>
1391 <div className="flex items-baseline justify-between">
1392 <span className="text-muted">Open issues by age</span>
1393 <Link to={`${base}/issues`} className="font-medium tabular-nums hover:text-accent">
1394 {open.issues}
1395 </Link>
1396 </div>
1397 {ages ? (
1398 <ul className="mt-2 space-y-1.5">
1399 {ages.map((bucket) => (
1400 <li key={bucket.label} className="grid grid-cols-[6.5rem_1fr_1.5rem] items-center gap-2">
1401 <span className="text-faint">{bucket.label}</span>
1402 <span className="block h-1.5 overflow-hidden rounded-full bg-line">
1403 <span
1404 className={`block h-full rounded-full ${bucket.label === "Older" ? "bg-warn" : "bg-fg-soft/60"}`}
1405 style={{ width: `${(bucket.count / oldest) * 100}%` }}
1406 />
1407 </span>
1408 <span className="text-right tabular-nums text-muted">{bucket.count}</span>
Project dependencies: addresses, preview stacks, Affects, and agents who know1409 </li>
1410 ))}
1411 </ul>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1412 ) : (
1413 <p className="mt-1 text-faint">Issues could not be loaded just now.</p>
1414 )}
1415 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1416 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1417 </section>
Project dependencies: addresses, preview stacks, Affects, and agents who know1418
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1419 <section className="rounded-xl border border-line bg-surface p-5">
1420 <div className="flex items-center justify-between">
1421 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1422 <Network size={14} className="text-faint" />
1423 Dependencies
1424 </h2>
1425 {member && (
1426 <Link to={`${base}/settings/dependencies`} className="text-xs text-muted hover:text-fg">
1427 Manage
1428 </Link>
1429 )}
1430 </div>
1431 {!dependencies ? (
1432 <p className="mt-3 text-xs text-muted">Dependencies could not be loaded just now.</p>
1433 ) : dependencies.dependsOn.length === 0 && dependencies.usedBy.length === 0 ? (
1434 <p className="mt-3 text-xs leading-5 text-muted">
1435 Uses no other project, and none uses it. Declare one, and builds get its address and agents know what depends on
1436 what.
1437 </p>
1438 ) : (
1439 <div className="mt-3 space-y-3 text-xs">
1440 {dependencies.dependsOn.length > 0 && (
1441 <div>
1442 <p className="flex items-center gap-1 text-muted">
1443 <ArrowUpRight size={12} /> Uses
1444 </p>
1445 <ul className="mt-1.5 space-y-1">
1446 {dependencies.dependsOn.map((d) => (
1447 <li key={d.slug} className="flex items-center justify-between gap-2">
1448 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1449 {d.name}
1450 </Link>
1451 {d.as && <code className="font-mono text-faint">{d.as}</code>}
1452 </li>
1453 ))}
1454 </ul>
1455 </div>
1456 )}
1457 {dependencies.usedBy.length > 0 && (
1458 <div>
1459 <p className="flex items-center gap-1 text-muted">
1460 <ArrowDownLeft size={12} /> Used by
1461 </p>
1462 <ul className="mt-1.5 space-y-1">
1463 {dependencies.usedBy.map((d) => (
1464 <li key={d.slug}>
1465 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1466 {d.name}
1467 </Link>
1468 </li>
1469 ))}
1470 </ul>
1471 </div>
1472 )}
1473 </div>
1474 )}
Projects: what a workspace builds and runs, first on every page1475 </section>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1476
1477 {source && (
1478 <section className="rounded-xl border border-line bg-surface p-5">
1479 <h2 className="text-sm font-semibold">Clone</h2>
1480 <div className="mt-3">
Merge branch 'worktree-agent-aaf03bdceac799c89'1481 <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 API1482 </div>
1483 </section>
1484 )}
1485 </aside>
1486 </div>
Projects: what a workspace builds and runs, first on every page1487 </div>
1488 );
1489}

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