Skip to content
1,495 linesCodeBlameRaw

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

Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1import {
2 Activity as ActivityIcon,
3 ArrowDownLeft,
4 ArrowRight,
5 ArrowUpRight,
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,
Site: a project's activity names people, not their account ids63 actorIds,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API64 ageBuckets,
65 eventItem,
66 firstPassRate,
67 groupActivity,
Site: a project's activity names people, not their account ids68 nameActor,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API69 passRate,
70 pipelineStage,
71 queuedNumbers,
72 rankNeeds,
73 stuckMinutes,
74} from "../../lib/mission";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9775import { 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 production76import { ECOSYSTEM_LABEL, installCommands } from "../../lib/packages";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9777import {
78 PUBLISH_GUIDES,
79 bare,
80 hasRelease,
81 kindLabel,
82 linksToShow,
83 latestTag,
84 libraryPackages,
85 packageName,
86 packagePath,
87 projectChanges,
88 publishGuide,
89} from "../../lib/project-kind";
90import { AboutEditor, KindMenu, LinkList } from "../../components/project-about";
91import { DocsHead, ElsewhereHead, type ExternalDeployment, OtherHead, WhereItRuns } from "../../components/project-head";
Site: a project's activity names people, not their account ids92import { actions, agents, deployments, events as eventLog, identity, 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-agent93import { madeByG1t } from "../../lib/opened-by";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look94import { assertSameOrigin, getViewer, requireUser } from "../../lib/session.server";
Fast pages, required checks on the branch, self-hosted runners, honest incidents95import { accessTo, countsFor, refusal, repoFor } from "../../lib/access.server";
Production thumbnails follow each deploy, not each commit, and old ones are cleared daily96import { shotVersion } from "./production-screenshot";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9797import { DeploymentsPanel } from "../../components/deployments-panel";
98import { environmentUrl, productionEnvironment } from "../../lib/deployments";
Merge branch 'worktree-agent-aaf03bdceac799c89'99import { cloneUrl, useAddresses } from "../../lib/addresses";
Branches and Tags pages, each file's last commit, and the branch menu on files100import { readBranches } from "../../lib/branches.server";
Merge checks: statuses and check runs on every commit101import { commitChecksFor } from "../../lib/commit-checks.server";
102import { CommitChecksBadge } from "../../components/commit-checks";
Projects: what a workspace builds and runs, first on every page103
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API104const MAX_LANDED = 6;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look105/** Branches read for the Active branches list, and shown. */
106const BRANCHES_READ = 10;
107const BRANCHES_SHOWN = 5;
A project is an app or a library: libraries show their package and how to ship a release, not production108/** A library's packages shown on its overview; the rest are a link away. */
109const PACKAGES_SHOWN = 3;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97110/** The default branch's latest commits listed. */
111const COMMITS_SHOWN = 5;
112/** How long Active branches may take before the section links to Branches instead. */
113const BRANCHES_WAIT_MS = 3_500;
Projects: what a workspace builds and runs, first on every page114
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily115/**
116 * The overview streams: the layout's header and tabs (one repository
117 * lookup, one project) go out at once with a skeleton below them, and
118 * everything here, seventeen calls across six services, follows in the
119 * same response as it settles. Crawlers wait for all of it
120 * (entry.server.tsx). Before, the first byte waited on the slowest of them.
121 */
122export function loader({ params, context }: Route.LoaderArgs) {
123 return { overview: overviewData({ params, context }) };
124}
125
126async function overviewData({ params, context }: Pick<Route.LoaderArgs, "params" | "context">) {
Projects: what a workspace builds and runs, first on every page127 const viewer = getViewer(context);
128 const path = { namespace: params.owner, name: params.repo };
129 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 API130 const now = Date.now();
131 // A section whose service fails shows its own empty state; the page stays up.
132 const soft = <T,>(promise: Promise<T> | null): Promise<T | null> =>
133 promise ? promise.catch((error) => (console.warn("overview:", error), null)) : Promise.resolve(null);
Fast pages, required checks on the branch, self-hosted runners, honest incidents134 // People with a role of their own here see its running parts: deployments,
135 // memory, setup; everyone who can read it sees the rest. Not awaited: only
136 // those sections wait on the repository lookup, which the layout makes in
137 // this request too.
138 const accessP = accessTo(context, params);
139 const memberP = accessP.then((access) => access.insider);
140 const forMembers = <T,>(start: () => Promise<T>): Promise<T | null> =>
141 memberP.then((member) => (member ? soft(start()) : null));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look142 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 production143 const projectP = soft(projects.get(params.owner, params.repo, viewer));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97144 // A library or a tool shows its packages where an app shows production,
145 // and every project g1t does not deploy counts a workflow in its checklist.
146 // Deployments from anywhere (g1t.page, g1t Actions, the API), by environment, for anyone who can read it.
147 const environmentsP = projectP.then((found) =>
148 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 production149 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97150 const planP = projectP.then((project) => (project?.ok ? checklistPlan(project.value).plan : null));
151 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 production152 const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97153 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 API154 const eventsP = repoP.then((repo) => (repo?.ok ? soft(eventLog.list({ repoId: repo.value.id, limit: 150 })) : null));
155 const minePullsP = repoP.then((repo) =>
156 repo?.ok && viewer
157 ? soft(work.listActivePulls(viewer)).then((list) => (list ?? []).filter((item) => item.pull.repoId === repo.value.id))
158 : null,
159 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look160 const openP = soft(work.listPulls(path, viewer, "open"));
161 // Where its code came from on GitHub, for members.
Fast pages, required checks on the branch, self-hosted runners, honest incidents162 const githubP = repoP.then((repo) => (repo?.ok ? forMembers(() => githubApp.link(repo.value.id)) : null));
163 const listP = forMembers(() => deployments.list(ref, viewer));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look164 // Active branches: the newest few besides the default, with how far each
165 // has moved. Branches with an open pull request are read first.
166 const branchesP = Promise.all([repoP, soft(repos.branches(path, viewer)), openP, listP]).then(
167 async ([repo, branchList, pulls, deploys]): Promise<{ main: string; total: number; shown: ActiveBranch[] } | null> => {
168 if (!repo?.ok || !branchList?.ok) return null;
169 const main = repo.value.defaultBranch;
Branches and Tags pages, each file's last commit, and the branch menu on files170 if (branchList.value.every((branch) => branch.name === main)) return { main, total: 0, shown: [] };
171 const read = await readBranches(
172 path,
173 viewer,
174 {
175 defaultBranch: main,
176 branches: branchList.value,
177 pulls: pulls?.ok ? pulls.value : [],
178 previews: deploys?.ok ? deploys.value.live.filter((app) => app.kind === "preview") : [],
179 },
180 BRANCHES_READ,
181 );
182 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 look183 },
184 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97185 // Active branches read several logs each: streamed, so the rest shows
186 // first. Bounded well inside the response's stream timeout
187 // (entry.server.tsx): a promise still pending when the stream ends never
188 // settles in the browser, and its skeleton would stay.
189 const branches = Promise.race([
190 branchesP.catch(() => null),
191 new Promise<"slow">((resolve) => setTimeout(() => resolve("slow"), BRANCHES_WAIT_MS)),
192 ]);
193 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 incidents194 accessP,
A project is an app or a library: libraries show their package and how to ship a release, not production195 projectP,
Fast pages, required checks on the branch, self-hosted runners, honest incidents196 forMembers(() => deployments.settings(ref, viewer)),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look197 listP,
198 openP,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API199 soft(work.listPulls(path, viewer, "closed")),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97200 soft(repos.log(path, viewer, null, COMMITS_SHOWN)),
Fast pages, required checks on the branch, self-hosted runners, honest incidents201 soft(countsFor(context, params)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API202 soft(projects.dependencies(params.owner, params.repo, viewer)),
203 soft(agents.listRuns(viewer, { repo: path, limit: 60 })),
204 soft(work.queue(path, viewer)),
205 soft(work.listIssues(path, viewer, { state: "open" })),
Fast pages, required checks on the branch, self-hosted runners, honest incidents206 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 API207 eventsP,
208 minePullsP,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look209 // For the checklist, which only members see.
Fast pages, required checks on the branch, self-hosted runners, honest incidents210 forMembers(() => deployments.domains(ref, viewer)),
211 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 production212 packagesP,
213 workflowsP,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97214 // The latest release, for About.
215 soft(repos.tags(path, viewer)),
Projects: what a workspace builds and runs, first on every page216 ]);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API217 const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
218
219 const openPulls = ok(open) ?? [];
220 const runList = ok(runs) ?? [];
221 const live = runList.filter((run) => isActiveRun(run.status));
222 const builds = ok(list)?.deployments ?? [];
223 const landed = (ok(closed) ?? [])
224 .filter((pull) => pull.status === "merged")
225 .sort((a, b) => Date.parse(b.mergedAt ?? b.updatedAt) - Date.parse(a.mergedAt ?? a.updatedAt));
226 const queued = queuedNumbers(ok(queue)?.active ?? []);
227 const runOn = new Map<number, AgentRun>();
228 for (const run of live) if (run.number != null && !runOn.has(run.number)) runOn.set(run.number, run);
229 const lifecycleOf = new Map((mine ?? []).map((item) => [item.pull.number, item.lifecycle]));
230
231 // --- The pipeline -----------------------------------------------------------
232 type Card = {
233 number: number;
234 title: string;
235 agent: string;
236 status: Pull["status"];
237 checkStatus: Pull["checkStatus"];
238 step: string | null;
239 runKind: string | null;
240 runStarted: string | null;
241 needsYou: boolean;
242 at: string;
243 };
244 const columns: Record<PipelineStage, Card[]> = { working: [], checking: [], reviewing: [], queue: [], landed: [] };
245 for (const pull of openPulls.slice(0, 40)) {
246 const run = runOn.get(pull.number);
Fast pages, required checks on the branch, self-hosted runners, honest incidents247 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 API248 number: pull.number,
249 title: pull.title,
250 agent: pull.agent,
251 status: pull.status,
252 checkStatus: pull.checkStatus,
253 step: run?.step ?? null,
254 runKind: run ? RUN_KIND_LABEL[run.kind] : null,
255 runStarted: run ? (run.startedAt ?? run.createdAt) : null,
256 needsYou: lifecycleOf.get(pull.number)?.stage === "needs_you",
257 at: pull.updatedAt,
258 });
259 }
260 for (const pull of landed.filter((p) => now - Date.parse(p.mergedAt ?? p.updatedAt) < 3 * TIME.DAY).slice(0, 5)) {
261 columns.landed.push({
262 number: pull.number,
263 title: pull.title,
264 agent: pull.agent,
265 status: pull.status,
266 checkStatus: pull.checkStatus,
267 step: null,
268 runKind: null,
269 runStarted: null,
270 needsYou: false,
271 at: pull.mergedAt ?? pull.updatedAt,
272 });
273 }
274
275 // --- Needs you ---------------------------------------------------------------
276 const base = `/${params.owner}/${params.repo}`;
277 const needs: Need[] = [];
278 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
279 if (latestProduction?.status === "failed") {
280 needs.push({
281 key: "deploy",
282 kind: "deploy",
283 title: "The production build failed",
284 detail: latestProduction.error ?? "Production still serves the build before it.",
285 to: `${base}/deployments/${latestProduction.id}`,
286 action: "See the build",
287 at: Date.parse(latestProduction.finishedAt ?? latestProduction.createdAt),
288 where: latestProduction.commit.slice(0, 7),
289 });
290 }
291 const me = viewer?.username.toLowerCase();
292 for (const pull of openPulls) {
293 const to = `${base}/pull/${pull.number}`;
294 const where = `#${pull.number}`;
295 const lifecycle = lifecycleOf.get(pull.number);
296 if (lifecycle?.stage === "needs_you") {
297 const conflict = /conflict/i.test(lifecycle.detail);
298 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 });
299 } else if (lifecycle?.stage === "ready") {
300 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 });
301 }
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights302 // Never your own, nor one g1t made for you; whoever it is for asked.
303 const owner = workOwner(pull).username;
304 if (me && pull.status === "open" && owner.toLowerCase() !== me && pull.reviewers.some((name) => name.toLowerCase() === me)) {
305 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 API306 }
307 if (member && pull.status === "open" && pull.checkStatus === "failed" && !runOn.has(pull.number) && !lifecycle) {
Fast pages, required checks on the branch, self-hosted runners, honest incidents308 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 API309 }
310 }
311 for (const run of live) {
312 const minutes = stuckMinutes(run, now);
313 if (minutes == null) continue;
314 needs.push({
315 key: `run:${run.id}`,
316 kind: "stuck",
317 title: run.title ?? `${run.agent}'s run`,
318 detail: `${run.agent} has reported nothing for ${minutes} min${run.step ? `. Last: ${run.step}` : ""}.`,
319 to: `${base}/agents/runs/${run.id}`,
320 action: "Look",
321 at: Date.parse(run.updatedAt),
322 where: run.number != null ? `#${run.number}` : null,
323 });
324 }
325
326 // --- Activity and health -------------------------------------------------------
327 const eventList: G1tEvent[] = recent ?? [];
Site: a project's activity names people, not their account ids328 const logged = eventList
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API329 .map((event) => eventItem(event, path))
330 .filter((item): item is ActivityItem => item != null);
Site: a project's activity names people, not their account ids331 // The log names people by account id: their usernames, in one lookup.
332 const ids = actorIds(logged.map((item) => item.actor));
333 const names = ids.length > 0 ? await soft(identity.usernames(ids)) : {};
334 const items = logged.map((item) => ({ ...item, actor: nameActor(item.actor, names) }));
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API335 const checkEvents = eventList.flatMap((event) =>
336 event.type === "checks.completed" ? [{ repo: "", number: event.data.number, at: Date.parse(event.time), passed: event.data.status === "passed" }] : [],
337 );
338 const memoryList = ok(memories)?.project ?? [];
Usage, Billing settings and prepaid AI credit; fixes from the UX audit339 // Pinned first, then the newest; a fact remembered twice, once.
340 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 API341 const openIssues = ok(issues);
342
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look343 // --- Getting to production ----------------------------------------------------
344 const wentLive = (status: string) => status === "ready" || status === "replaced" || status === "down";
345 const liveApps = ok(list)?.live ?? [];
346 const commitNow = ok(log)?.[0] ?? null;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97347 // Production deployed somewhere other than g1t.page (by g1t Actions, or
348 // reported through the API): the slot an app deployed elsewhere shows.
349 const environments = await environmentsP;
350 const environmentList = environments?.ok ? environments.value : null;
351 const reported = productionEnvironment(environmentList?.environments ?? []);
352 const deployment: ExternalDeployment | null =
353 reported?.latest && reported.latest.source !== "g1t_page"
354 ? {
355 environment: reported.name,
356 url: environmentUrl(reported),
357 state: reported.latest.state,
358 sha: reported.latest.sha,
359 created_at: reported.latest.updated_at,
360 }
361 : null;
362 // An app that has not said where it runs, but reports production from
363 // elsewhere, runs elsewhere.
364 const found = ok(project);
365 const projectValue = found && deployment && found.kind === "app" && found.runs == null ? { ...found, runs: "elsewhere" as const } : found;
366 const plan = projectValue ? checklistPlan(projectValue) : { plan: "production" as const, title: "Get to production" };
367 const isLibrary = plan.plan === "release";
A project is an app or a library: libraries show their package and how to ship a release, not production368 const published = libraryPackages(ok(packageList) ?? []);
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97369 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 production370 const firstSteps = {
371 base,
372 hasCode: commitNow != null || projectValue?.source.kind === "mirror",
373 instructions: commitNow == null ? false : root?.ok ? hasInstructions(root.value.entries.map((entry) => entry.name)) : null,
374 agentAssigned: agentWasAssigned({
375 runAgents: runList.map((run) => run.agent),
376 pullAgents: [...openPulls, ...(ok(closed) ?? [])].map((pull) => pull.agent),
377 issues: openIssues ?? [],
378 }),
379 };
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97380 const links = projectValue?.links;
A project is an app or a library: libraries show their package and how to ship a release, not production381 const checklist = !member
382 ? null
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97383 : plan.plan !== "production" && plan.plan !== "release"
384 ? startChecklist(plan.plan, {
385 ...firstSteps,
386 hasWorkflow,
387 productionUrl: projectValue?.productionUrl ?? null,
388 hasLinks: !!links && (links.homepage != null || links.docs != null || links.custom.length > 0),
389 hasDocsLink: !!links && (links.docs != null || links.homepage != null),
390 })
A project is an app or a library: libraries show their package and how to ship a release, not production391 : isLibrary
392 ? releaseChecklist({
393 ...firstSteps,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97394 hasWorkflow,
A project is an app or a library: libraries show their package and how to ship a release, not production395 released: hasRelease(published),
396 releaseTo: published[0]
397 ? packagePath(published[0])
398 : (publishGuide(projectValue?.ecosystem ?? null)?.guide ?? "https://docs.g1t.sh/guides/packages/"),
399 })
400 : productionChecklist({
401 ...firstSteps,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look402 deploysEnabled: ok(settings)?.enabled ?? false,
403 productionDeployed:
404 liveApps.some((app) => app.kind === "production") ||
405 builds.some((build) => build.kind === "production" && wentLive(build.status)),
406 domains: ok(domains)?.domains.length ?? null,
407 previewOpened:
408 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 production409 });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look410
Merge checks: statuses and check runs on every commit411 // The checks on the latest commit and each active branch's head, in
412 // one call once the branches are read: streamed in beside them.
413 const checks = branches.then((found) =>
414 commitChecksFor(path, viewer, [commitNow?.hash, ...(found && found !== "slow" ? found.shown : []).map((branch) => branch.commit?.hash)]),
415 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look416 const github = await githubP;
Projects: what a workspace builds and runs, first on every page417 return {
418 member,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look419 can,
420 github,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97421 project: projectValue,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API422 settings: ok(settings),
423 builds: builds.slice(0, 30),
424 live: ok(list)?.live ?? [],
425 commit: ok(log)?.[0] ?? null,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97426 commits: (ok(log) ?? []).map((one) => ({ hash: one.hash, message: one.message.split("\n")[0] ?? "", author: one.author.name, at: one.authoredAt })),
427 release: latestTag(ok(tagList) ?? []),
428 checklistTitle: plan.title,
429 // Production as reported from outside g1t, for an app deployed elsewhere.
430 deployment,
431 // Every environment's latest deployment, for the Deployments panel.
432 environments: environmentList,
433 // About's languages and contributors, filled in by the files' About work.
434 languages: null as { name: string; share: number }[] | null,
435 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 API436 open: ok(counts) ?? { issues: openIssues?.length ?? 0, pulls: openPulls.length },
437 dependencies: ok(deps),
438 agentsLive: live.slice(0, 6),
439 runsLoaded: runs?.ok ?? false,
440 pullsLoaded: open?.ok ?? false,
441 columns,
442 needs: rankNeeds(needs),
443 landed: landed.slice(0, MAX_LANDED),
444 groups: groupActivity(items).slice(0, 30),
445 eventsLoaded: recent != null,
446 health: {
447 passRate: passRate(checkEvents.map((c) => c.passed)),
448 firstPass: firstPassRate(checkEvents),
449 checkRuns: checkEvents.length,
450 ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null,
451 },
452 knows: knows as Memory[],
453 memoryCount: memoryList.length,
454 memoriesLoaded: memories?.ok ?? false,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look455 checklist,
A project is an app or a library: libraries show their package and how to ship a release, not production456 library: isLibrary
457 ? { packages: published.slice(0, PACKAGES_SHOWN), total: published.length, loaded: packageList?.ok ?? false }
458 : null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look459 branches,
Merge checks: statuses and check runs on every commit460 checks,
Projects: what a workspace builds and runs, first on every page461 };
462}
463
464export async function action({ request, params, context }: Route.ActionArgs) {
465 assertSameOrigin(request);
466 const user = requireUser(context, request);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look467 const form = await request.formData().catch(() => null);
468 const intent = form?.get("intent");
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97469 // What it is, where it runs, its description and links: its settings.
470 if (form && (intent === "kind" || intent === "about")) {
471 const refused = await refusal(context, params, "manage_settings");
472 if (refused) return { error: refused };
473 const saved = await projects.update(user, params.owner, params.repo, projectChanges(form));
474 return saved.ok ? { notice: "Saved." } : { error: saved.error.message };
475 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look476 // Syncing from GitHub is pushing; stopping it is an integration; deploying is compute.
477 const refused = await refusal(context, params, intent === "github-sync" ? "push" : intent === "github-stop" ? "manage_integrations" : "run");
478 if (refused) return { error: refused };
479 if (intent === "github-sync" || intent === "github-stop") {
480 const repo = await repos.get({ namespace: params.owner, name: params.repo }, user);
481 if (!repo.ok) return { error: repo.error.message };
482 const done =
483 intent === "github-sync" ? await githubApp.sync(user, repo.value.id) : await githubApp.unlinkRepo(user, repo.value.id);
484 return done.ok
485 ? { notice: intent === "github-sync" ? "Synced with GitHub." : "It is no longer kept in step with GitHub." }
486 : { error: done.error.message };
487 }
Projects: what a workspace builds and runs, first on every page488 const started = await deployments.redeploy(user, { workspace: params.owner, slug: params.repo }, null);
489 return started.ok ? { notice: "Production is building." } : { error: started.error.message };
490}
491
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily492type Loaded = Awaited<ReturnType<typeof overviewData>>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API493type Card = Loaded["columns"]["working"][number];
494
495const STAGE_TONE: Record<PipelineStage, string> = {
Merge branch 'worktree-agent-ad7c6d88d93adc817'496 working: "bg-accent",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API497 checking: "bg-info",
498 reviewing: "bg-warn",
Merge branch 'worktree-agent-ad7c6d88d93adc817'499 queue: "bg-success-dim",
500 landed: "bg-success",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API501};
502
503function PipelineCard({ card, base, stage }: { card: Card; base: string; stage: PipelineStage }) {
504 return (
505 <li>
506 <Link
507 to={`${base}/pull/${card.number}`}
508 prefetch="intent"
509 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'510 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 API511 }`}
512 >
513 <span className="line-clamp-2 text-[0.8125rem] leading-snug font-medium">{card.title}</span>
514 <span className="mt-1.5 flex items-center gap-1.5 text-[0.6875rem] text-muted">
515 <Avatar name={card.agent} size={14} />
516 <span className="font-mono text-faint">#{card.number}</span>
517 <span className="grow" />
518 {card.needsYou ? (
Merge branch 'worktree-agent-ae1299e92e4462012'519 <Hint label="Stopped: a person decides what happens next">
520 <span className="inline-flex items-center gap-1 text-warn">
521 <Hand size={11} /> you
522 <span className="sr-only">: stopped, a person decides what happens next</span>
523 </span>
524 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API525 ) : stage === "landed" ? (
526 <TimeAgo at={card.at} />
527 ) : card.runStarted ? (
Merge branch 'worktree-agent-ad7c6d88d93adc817'528 <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 API529 <Loader2 size={11} className="animate-spin" />
530 <Elapsed from={card.runStarted} />
531 </span>
532 ) : (
533 <CheckBadge status={card.checkStatus} />
534 )}
535 </span>
536 {card.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'537 <Hint label={card.step}>
538 <span className="mt-1.5 block truncate font-mono text-[0.6875rem] text-fg/70">
539 {card.runKind}: {card.step}
540 </span>
541 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API542 )}
543 </Link>
544 </li>
545 );
546}
547
548/** Open pull requests moving through Working → Checking → Reviewing → Queue → Landed. */
549function Pipeline({ columns, base }: { columns: Loaded["columns"]; base: string }) {
550 return (
551 <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 look552 {/* Each stage as tall as what is in it: an empty one stays short
553 instead of stretching to the busiest. */}
554 <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 API555 {PIPELINE.map(({ stage, label }, index) => {
556 const cards = columns[stage];
557 return (
558 <li key={stage} className="flex min-w-0 flex-col rounded-xl border border-line bg-surface p-2">
559 <p className="flex items-center gap-2 px-1 pt-0.5 pb-2 text-xs font-medium text-muted">
560 <span className={`size-1.5 rounded-full ${STAGE_TONE[stage]} ${cards.length && stage !== "landed" ? "animate-pulse" : ""}`} />
561 {label}
562 <span className="tabular-nums text-faint">{cards.length}</span>
563 {index < PIPELINE.length - 1 && <ArrowRight size={11} className="ml-auto text-line-strong" />}
564 </p>
565 {cards.length === 0 ? (
566 <p className="rounded-lg border border-dashed border-line/70 px-2 py-3 text-center text-[0.6875rem] text-faint">Empty</p>
567 ) : (
568 <ul className="space-y-1.5">
569 {cards.slice(0, 6).map((card) => (
570 <PipelineCard key={card.number} card={card} base={base} stage={stage} />
571 ))}
572 {cards.length > 6 && (
573 <li className="px-1 text-[0.6875rem] text-muted">
574 <Link to={`${base}/pulls`} className="hover:text-fg">
575 {cards.length - 6} more
576 </Link>
577 </li>
578 )}
579 </ul>
580 )}
581 </li>
582 );
583 })}
584 </ol>
585 </div>
586 );
587}
588
A project is an app or a library: libraries show their package and how to ship a release, not production589/**
590 * Where an app's overview shows production, a library's shows what it
591 * publishes: each package with its latest version and how to install it,
592 * or, before there is one, how to publish a first version.
593 */
594function LibraryHead({
595 library,
596 project,
597 base,
598 workspace,
599}: {
600 library: { packages: PackageSummary[]; total: number; loaded: boolean };
601 project: Project | null;
602 base: string;
603 workspace: string;
604}) {
605 const guide = publishGuide(project?.ecosystem ?? null);
606 const name = project?.name ?? base.split("/").pop();
607 return (
608 <div className="p-5 sm:p-6">
609 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
610 <PackageGlyph size={13} className="text-accent" />
611 Packages
612 </p>
613 {library.packages.length > 0 ? (
614 <>
615 <ul className="mt-3 space-y-4">
616 {library.packages.map((pkg) => {
617 const commands = installCommands(pkg, pkg.latest, "you");
618 return (
619 <li key={pkg.id} className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:gap-5">
620 <Link to={packagePath(pkg)} className="group flex min-w-0 items-center gap-3 lg:w-72 lg:shrink-0">
621 <PackageIcon ecosystem={pkg.ecosystem} size={32} />
622 <span className="min-w-0">
623 <span className="block truncate font-mono text-[0.9375rem] font-medium group-hover:text-accent">{packageName(pkg)}</span>
624 <span className="block text-xs text-muted">
625 {ECOSYSTEM_LABEL[pkg.ecosystem]} · {pkg.latest ?? "no versions yet"}
626 {pkg.versions > 1 && ` · ${pkg.versions} versions`}
627 </span>
628 </span>
629 </Link>
630 <div className="min-w-0 grow space-y-1.5">
631 {/* The workspace's registry first, or the tool installs a package of the same name from elsewhere. */}
632 {commands.registry && <CopyLine prompt text={commands.registry} />}
633 <CopyLine prompt text={commands.install} />
634 </div>
635 </li>
636 );
637 })}
638 </ul>
639 {library.total > library.packages.length && (
640 <Link to={`/${workspace}/-/packages`} className="mt-3 inline-block text-xs text-muted hover:text-fg">
641 All {library.total} packages
642 </Link>
643 )}
644 </>
645 ) : (
646 <>
647 <p className="mt-2 text-lg font-medium">{library.loaded ? "Not published yet" : "Packages could not be loaded just now"}</p>
648 {guide ? (
649 <>
650 <p className="mt-1 max-w-lg text-sm text-muted">
651 Publish a first version of {name} to the workspace's {guide.label} registry, for others to install
652 {guide.start ? ":" : "."}
653 </p>
654 {guide.start && (
655 <div className="mt-3 max-w-lg">
656 <CopyLine prompt text={guide.start} />
657 </div>
658 )}
659 <a href={guide.guide} className="mt-3 inline-flex items-center gap-1 text-sm text-accent hover:underline">
660 Publishing to {guide.label} <ArrowUpRight size={13} />
661 </a>
662 </>
663 ) : (
664 <>
665 <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>
666 <p className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm">
667 {PUBLISH_GUIDES.map((one) => (
668 <a key={one.label} href={one.guide} className="inline-flex items-center gap-1 text-accent hover:underline">
669 {one.label} <ArrowUpRight size={13} />
670 </a>
671 ))}
672 </p>
673 </>
674 )}
675 </>
676 )}
677 </div>
678 );
679}
680
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API681function Stat({ label, value, to }: { label: string; value: ReactNode; to?: string }) {
682 const body = (
683 <>
684 <span className="block text-[0.6875rem] text-faint">{label}</span>
685 <span className="mt-0.5 block truncate text-[0.8125rem] font-medium">{value}</span>
686 </>
687 );
688 return to ? (
689 <Link to={to} className="min-w-0 px-5 py-3 transition-colors hover:bg-raised/50">
690 {body}
691 </Link>
692 ) : (
693 <div className="min-w-0 px-5 py-3">{body}</div>
694 );
695}
696
Projects: what a workspace builds and runs, first on every page697export default function ProjectOverview({ loaderData, actionData, params }: Route.ComponentProps) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily698 return (
699 <Suspense fallback={<OverviewSkeleton />}>
700 <Await resolve={loaderData.overview} errorElement={<OverviewFailed />}>
Merge branch 'worktree-agent-aca4790ea223aa235'701 {(overview) => (
702 // Faded in over its outline, not switched for it.
703 <div className="animate-fade-in">
704 <Overview loaderData={overview} actionData={actionData} params={params} />
705 </div>
706 )}
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily707 </Await>
708 </Suspense>
709 );
710}
711
712/** The overview's sections as they will sit, while they stream in. */
713function OverviewSkeleton() {
714 return (
Merge branch 'worktree-agent-aca4790ea223aa235'715 <Loading className="space-y-8" aria-label="Loading the overview">
716 {/* Production: its screenshot beside where it is live, then the facts under them. */}
717 <section aria-hidden="true" className="overflow-hidden rounded-2xl border border-line bg-surface">
718 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
719 <Skeleton className="aspect-[16/10] w-full shrink-0 rounded-lg sm:w-72" />
720 <div className="min-w-0 grow">
721 <Skeleton className="h-3 w-24" />
722 <Skeleton className="mt-3 h-5 w-72 max-w-full" />
723 <Skeleton className="mt-3 h-3 w-48 max-w-full" />
724 </div>
725 </div>
726 <div className="grid grid-cols-2 gap-px border-t border-line bg-line sm:grid-cols-4">
727 {Array.from({ length: 4 }, (_, index) => (
728 <div key={index} className="bg-surface px-5 py-4">
729 <Skeleton className="h-3 w-16" />
730 <Skeleton className="mt-2.5 h-4 w-28" />
731 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily732 ))}
733 </div>
734 </section>
Merge branch 'worktree-agent-aca4790ea223aa235'735 {/* Right now: the work, column by column. */}
736 <div aria-hidden="true" className="grid grid-cols-2 gap-2 sm:grid-cols-5">
737 {Array.from({ length: 5 }, (_, index) => (
738 <div key={index} className="rounded-xl border border-line bg-surface p-3">
739 <Skeleton className="h-3 w-20" />
740 <Skeleton className="mt-3 h-14 rounded-lg" />
741 </div>
742 ))}
743 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily744 <div className="grid gap-6 lg:grid-cols-2">
745 <section className="rounded-2xl border border-line bg-surface">
746 <SkeletonRows rows={4} />
747 </section>
748 <section className="rounded-2xl border border-line bg-surface">
749 <SkeletonRows rows={4} />
750 </section>
751 </div>
Merge branch 'worktree-agent-aca4790ea223aa235'752 </Loading>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily753 );
754}
755
756function OverviewFailed() {
757 return (
758 <p className="rounded-2xl border border-line bg-surface p-6 text-sm text-muted">
759 The overview could not be loaded just now. Reload the page to try again.
760 </p>
761 );
762}
763
764function Overview({
765 loaderData,
766 actionData,
767 params,
768}: {
769 loaderData: Loaded;
770 actionData: Route.ComponentProps["actionData"];
771 params: Route.ComponentProps["params"];
772}) {
Merge checks: statuses and check runs on every commit773 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 look774 loaderData;
Projects: what a workspace builds and runs, first on every page775 const base = `/${params.owner}/${params.repo}`;
776 const production = live.find((app) => app.kind === "production") ?? null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains777 // The project's own domain, once it is active, is where production is visited.
778 const productionUrl = production ? (settings?.primaryDomain ? `https://${settings.primaryDomain}` : production.url) : null;
Projects: what a workspace builds and runs, first on every page779 const previews = live.filter((app) => app.kind === "preview");
780 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97781 const plan = project ? checklistPlan(project).plan : "production";
782 const canChange = member && loaderData.can.manage_settings;
783 // Its homepage and docs beside what it is, unless the card below already links there.
784 const headUrl =
785 plan === "production"
786 ? (productionUrl ?? settings?.productionUrl)
787 : plan === "elsewhere"
788 ? (loaderData.deployment?.url ?? project?.productionUrl)
789 : plan === "docs"
790 ? (project?.links.docs ?? project?.productionUrl ?? project?.links.homepage)
791 : plan === "other"
792 ? (project?.links.homepage ?? project?.links.docs)
793 : null;
794 const stripLinks = project ? linksToShow({ ...project.links, custom: [] }, [headUrl]) : [];
Merge branch 'worktree-agent-aaf03bdceac799c89'795 const addresses = useAddresses();
Projects: what a workspace builds and runs, first on every page796 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 API797 const moving = agentsLive.length > 0 || columns.working.length + columns.checking.length > 0;
798 useLiveRefresh(moving);
799 const ages = health.ages;
800 const oldest = ages ? Math.max(1, ...ages.map((a) => a.count)) : 1;
Projects: what a workspace builds and runs, first on every page801
802 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API803 <div className="space-y-8">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look804 {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 API805 {/* What the project is, running, and where its code is. */}
806 <section className="overflow-hidden rounded-2xl border border-line bg-surface">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97807 {project && (
808 <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">
809 <KindMenu project={project} label={kindLabel(project)} canChange={canChange} />
810 {stripLinks.map((link) => (
811 <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">
812 {link.type === "docs" ? <BookOpen size={12} className="shrink-0 text-faint" /> : <Globe size={12} className="shrink-0 text-faint" />}
813 <span className="truncate">{link.type === "docs" ? `Docs · ${bare(link.url)}` : link.label}</span>
814 </a>
815 ))}
816 </div>
817 )}
818 {plan === "elsewhere" && project ? (
819 <ElsewhereHead
820 project={project}
821 base={base}
822 canChange={canChange}
823 canDeploy={member && loaderData.can.manage_integrations}
824 deployment={loaderData.deployment}
825 commit={commit}
Merge checks: statuses and check runs on every commit826 checks={commit ? <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" /> : undefined}
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97827 />
828 ) : plan === "unknown" && project ? (
829 <WhereItRuns project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
830 ) : plan === "docs" && project ? (
831 <DocsHead project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
832 ) : plan === "other" && project ? (
833 <OtherHead project={project} base={base} canChange={canChange} />
834 ) : library ? (
835 <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 production836 ) : (
837 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
838 {member && (production || settings?.enabled) && (
839 <ProductionShot
840 src={production ? `${base}/production.jpg?v=${shotVersion(production)}` : null}
841 href={production ? (productionUrl ?? production.url) : null}
842 label={host(production ? (productionUrl ?? production.url) : (settings?.productionUrl ?? ""))}
843 className="w-full shrink-0 sm:w-60 lg:w-72"
844 />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API845 )}
A project is an app or a library: libraries show their package and how to ship a release, not production846 <div className="flex min-w-0 grow flex-wrap items-start justify-between gap-4">
847 <div className="min-w-0">
848 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
849 <Rocket size={13} className="text-accent" />
850 Production
851 </p>
852 {production ? (
853 <>
854 <a
855 href={productionUrl ?? production.url}
856 className="mt-2 flex items-center gap-1.5 truncate font-mono text-lg font-medium hover:text-accent"
857 >
858 {host(productionUrl ?? production.url)}
859 <ArrowUpRight size={16} className="shrink-0 text-faint" />
860 </a>
861 <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted">
862 {latestProduction && <StatusDot status={latestProduction.status} label={latestProduction.status === "ready" ? "Live" : undefined} />}
863 <span className="inline-flex items-center gap-1 font-mono">
864 <GitCommitHorizontal size={13} className="text-faint" />
865 {production.commit.slice(0, 7)}
866 </span>
867 <span>
868 deployed <TimeAgo at={production.deployedAt} />
869 </span>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97870 {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 production871 </>
872 ) : settings?.enabled ? (
873 <>
874 <p className="mt-2 font-mono text-lg text-muted">{host(settings.productionUrl)}</p>
875 <p className="mt-1.5 text-xs text-muted">
876 {latestProduction ? (
877 <StatusDot status={latestProduction.status} label={latestProduction.status === "failed" ? "The last build failed" : undefined} />
878 ) : (
879 "Not deployed yet. Push to the default branch, or deploy it now."
880 )}
881 </p>
882 </>
883 ) : (
884 <>
885 <p className="mt-2 text-lg font-medium">Deployments are off</p>
886 <p className="mt-1 max-w-lg text-sm text-muted">
887 Nothing builds or runs until you turn them on. Then {project?.name ?? params.repo} goes up on g1t.page:
888 production from the default branch, and a live preview for every pull request. It runs only while
889 someone visits.
890 </p>
891 </>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API892 )}
Projects: what a workspace builds and runs, first on every page893 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production894 {member && (
895 <div className="flex shrink-0 items-center gap-2">
896 {production && (
897 <ButtonLink to={productionUrl ?? production.url} variant="accent" reloadDocument>
898 Visit
899 <ArrowUpRight size={14} />
900 </ButtonLink>
901 )}
902 {settings?.enabled ? (
903 loaderData.can.run && <Form method="post">
Merge branch 'worktree-agent-ae1299e92e4462012'904 <Hint label="Build production again from the default branch">
905 <SubmitButton variant="quiet" name="intent" value="redeploy" pending="Redeploying…">
906 <RotateCw size={14} />
907 Redeploy
908 </SubmitButton>
909 </Hint>
A project is an app or a library: libraries show their package and how to ship a release, not production910 </Form>
911 ) : loaderData.can.manage_integrations && (
912 <ButtonLink to={`${base}/settings/deployments`} variant="accent">
913 <Rocket size={14} />
914 Turn on deployments
915 </ButtonLink>
916 )}
917 </div>
918 )}
919 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look920 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production921 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API922 {actionData && (
Merge branch 'worktree-agent-ad7c6d88d93adc817'923 <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 API924 {"error" in actionData ? actionData.error : actionData.notice}
925 </p>
926 )}
927 <div className="grid grid-cols-2 divide-line border-t border-line text-sm sm:grid-cols-4 sm:divide-x">
928 <Stat
929 label="Source"
930 to={`${base}/code`}
931 value={
932 source ? (
933 <span className="inline-flex items-center gap-1.5 font-mono">
934 {project?.private ? <Lock size={12} className="text-faint" /> : <Code2 size={12} className="text-faint" />}
935 {source.repo.name}
936 {source.rootDir ? `/${source.rootDir}` : ""}
937 </span>
938 ) : (
939 "—"
940 )
941 }
942 />
943 <Stat
944 label="Default branch"
945 to={`${base}/commits`}
946 value={
947 <span className="inline-flex items-center gap-1.5 font-mono">
948 <GitBranch size={12} className="text-faint" />
949 {source?.defaultBranch ?? "main"}
950 </span>
951 }
952 />
953 <Stat
954 label="Latest commit"
955 value={
956 commit ? (
Merge checks: statuses and check runs on every commit957 <span className="flex min-w-0 items-center gap-1">
958 <Hint label={commit.message.split("\n")[0]}>
959 <Link to={`${base}/commit/${commit.hash}`} className="min-w-0 truncate hover:text-accent">
960 <span className="font-mono">{commit.hash.slice(0, 7)}</span>{" "}
961 <span className="font-normal text-muted">
962 <TimeAgo at={commit.authoredAt} />
963 </span>
964 </Link>
965 </Hint>
966 {/* A button, so beside the link rather than in it. */}
967 <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" />
968 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API969 ) : (
970 "No commits yet"
971 )
972 }
973 />
974 <Stat
A project is an app or a library: libraries show their package and how to ship a release, not production975 label={member && settings?.enabled && !library ? "Last deploy" : "Open"}
976 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 API977 value={
A project is an app or a library: libraries show their package and how to ship a release, not production978 member && settings?.enabled && !library ? (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API979 latestProduction ? (
980 <span className="inline-flex items-center gap-2">
981 <StatusDot status={latestProduction.status} />
982 <span className="font-normal text-muted">
983 <TimeAgo at={latestProduction.createdAt} />
Projects: what a workspace builds and runs, first on every page984 </span>
985 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API986 ) : (
987 "None yet"
988 )
989 ) : (
990 `${open.pulls} pull requests · ${open.issues} issues`
991 )
992 }
993 />
994 </div>
995 {commit && (
996 <p className="truncate border-t border-line px-5 py-2.5 text-xs text-muted sm:px-6">
997 <GitCommitHorizontal size={12} className="mr-1.5 inline text-faint" />
998 {commit.message.split("\n")[0]} <span className="text-faint">· {commit.author.name}</span>
999 </p>
1000 )}
1001 </section>
1002
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971003 {checklist && <ProductionChecklist base={base} items={checklist} title={loaderData.checklistTitle} />}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1004
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1005 <Panel
1006 title="Right now"
Merge branch 'worktree-agent-ad7c6d88d93adc817'1007 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 API1008 count={agentsLive.length}
1009 all={{ to: `${base}/agents`, label: "Agents" }}
1010 >
1011 {agentsLive.length > 0 && (
1012 <ul className="mb-3 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
1013 {agentsLive.map((run) => (
Merge branch 'worktree-agent-ad7c6d88d93adc817'1014 <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 API1015 <div className="flex items-center gap-2 text-sm">
1016 <Avatar name={run.agent} size={18} />
1017 <span className="font-medium">{run.agent}</span>
1018 <span className="text-xs text-muted">{RUN_KIND_LABEL[run.kind].toLowerCase()}</span>
1019 <span className="grow" />
1020 <span className="text-xs text-muted">
1021 <Elapsed from={run.startedAt ?? run.createdAt} />
Projects: what a workspace builds and runs, first on every page1022 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1023 </div>
1024 <Link
1025 to={run.number != null ? `${base}/pull/${run.number}` : `${base}/agents/runs/${run.id}`}
1026 className="mt-1 block truncate text-[0.8125rem] hover:text-accent"
1027 >
1028 {run.title ?? "A run"} {run.number != null && <span className="text-faint">#{run.number}</span>}
1029 </Link>
1030 {run.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'1031 <Hint label={run.step}>
1032 <p className="mt-1 truncate font-mono text-[0.6875rem] text-fg/70">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1033 <span className="mr-1.5 inline-block size-1.5 animate-pulse rounded-full bg-accent align-middle" />
Merge branch 'worktree-agent-ae1299e92e4462012'1034 {run.step}
1035 </p>
1036 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1037 )}
1038 <p className="mt-1.5 flex items-center gap-3 text-[0.6875rem] text-faint">
1039 {formatCost(run.costUsd) && <span>{formatCost(run.costUsd)} so far</span>}
1040 <Link to={`${base}/agents/runs/${run.id}`} className="hover:text-fg">
1041 {run.stepCount} steps
1042 </Link>
1043 {run.number != null && (
1044 <Link to={`${base}/sessions/${run.number}`} className="hover:text-fg">
1045 Session
1046 </Link>
1047 )}
1048 </p>
1049 </li>
1050 ))}
1051 </ul>
1052 )}
1053 {!loaderData.pullsLoaded ? (
1054 <Unavailable what="Pull requests" />
1055 ) : Object.values(columns).every((c) => c.length === 0) ? (
1056 <Quiet
1057 action={
1058 member ? (
1059 <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">
1060 <Bot size={12} /> Open an issue for an agent
1061 </Link>
1062 ) : null
1063 }
1064 >
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1065 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 API1066 </Quiet>
1067 ) : (
1068 <Pipeline columns={columns} base={base} />
Projects: what a workspace builds and runs, first on every page1069 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1070 </Panel>
Projects: what a workspace builds and runs, first on every page1071
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1072 <div className="grid gap-x-8 gap-y-8 lg:grid-cols-[minmax(0,1fr)_20rem]">
1073 <div className="min-w-0 space-y-8">
1074 {needs.length > 0 && (
1075 <Panel title="Needs you" icon={<Hand size={14} className="text-warn" />} count={needs.length}>
1076 <NeedsList needs={needs} limit={5} />
1077 </Panel>
1078 )}
1079
Fast pages, required checks on the branch, self-hosted runners, honest incidents1080 <Suspense
1081 fallback={
1082 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1083 <div aria-busy="true">
1084 <SkeletonRows rows={3} rowClassName="h-12" />
1085 </div>
1086 </Panel>
1087 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1088 >
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971089 <Await
1090 resolve={branches}
1091 errorElement={
1092 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1093 <Unavailable what="Branches" />
1094 </Panel>
1095 }
1096 >
Fast pages, required checks on the branch, self-hosted runners, honest incidents1097 {(branches) => (
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971098 <Panel
1099 title="Active branches"
1100 icon={<GitBranch size={14} />}
1101 count={branches && branches !== "slow" ? branches.total || null : null}
1102 all={{ to: `${base}/branches`, label: "All branches" }}
1103 >
1104 {branches === "slow" ? (
1105 <Quiet>
1106 Its branches are taking a while to compare.{" "}
1107 <Link to={`${base}/branches`} className="text-accent hover:underline">
1108 See them on Branches
1109 </Link>
1110 .
1111 </Quiet>
1112 ) : !branches ? (
Fast pages, required checks on the branch, self-hosted runners, honest incidents1113 <Unavailable what="Branches" />
1114 ) : branches.shown.length === 0 ? (
1115 <Quiet>
1116 Only {branches.main} so far. Branches pushed here show with how far each has moved from {branches.main}, and
1117 its pull request and preview.
1118 </Quiet>
1119 ) : (
1120 <>
Merge checks: statuses and check runs on every commit1121 <ActiveBranches branches={branches.shown} base={base} main={branches.main} checks={checks} />
Fast pages, required checks on the branch, self-hosted runners, honest incidents1122 {branches.total > branches.shown.length && (
1123 <p className="mt-2 px-1 text-xs text-faint">
1124 The {branches.shown.length} most recently changed of {branches.total} branches.
1125 </p>
1126 )}
1127 </>
1128 )}
1129 </Panel>
1130 )}
1131 </Await>
1132 </Suspense>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1133
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1134 <Panel title="Recent changes" icon={<GitMerge size={14} />} all={{ to: `${base}/pulls?state=closed`, label: "All landed" }}>
1135 {landed.length === 0 ? (
1136 <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 page1137 ) : (
1138 <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 API1139 {landed.map((pull) => {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1140 const byAgent = pull.runtime === "hosted" || madeByG1t(pull);
Projects: what a workspace builds and runs, first on every page1141 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1142 <li key={pull.id} className="flex items-start gap-3 px-4 py-3">
1143 <GitMerge size={15} className="mt-0.5 shrink-0 text-merged" />
1144 <span className="min-w-0 grow">
1145 <Link to={`${base}/pull/${pull.number}`} prefetch="intent" className="block truncate text-sm font-medium hover:text-accent">
1146 {pull.title}
1147 </Link>
1148 <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted">
1149 <span className="font-mono text-faint">#{pull.number}</span>
1150 <span>·</span>
1151 <span className="inline-flex items-center gap-1">
1152 <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 rights1153 {pull.requestedBy
1154 ? `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-agent1155 : byAgent
1156 ? `made by ${pull.agent}`
1157 : `by ${pull.author.username}`}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1158 </span>
1159 {pull.issue != null && (
1160 <>
1161 <span>· for</span>
1162 <Link to={`${base}/issues/${pull.issue}`} className="hover:text-fg">
1163 #{pull.issue}
1164 </Link>
1165 </>
1166 )}
1167 {pull.mergedBy && <span>· landed by {pull.mergedBy}</span>}
1168 <span>
1169 · <TimeAgo at={pull.mergedAt ?? pull.updatedAt} />
Projects: what a workspace builds and runs, first on every page1170 </span>
1171 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1172 </span>
1173 <span className="hidden shrink-0 text-xs text-faint sm:block">
1174 <ChangeSize files={pull.files} />
1175 </span>
1176 {byAgent && (
1177 <Link to={`${base}/sessions/${pull.number}`} className="shrink-0 text-xs text-muted hover:text-fg">
1178 Session
1179 </Link>
1180 )}
Projects: what a workspace builds and runs, first on every page1181 </li>
1182 );
1183 })}
1184 </ul>
1185 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1186 </Panel>
Projects: what a workspace builds and runs, first on every page1187
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971188 {loaderData.commits.length > 0 && (
1189 <Panel
1190 title={`Latest on ${source?.defaultBranch ?? "main"}`}
1191 icon={<GitCommitHorizontal size={14} />}
1192 all={{ to: `${base}/commits`, label: "All commits" }}
1193 >
1194 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1195 {loaderData.commits.map((one) => (
1196 <li key={one.hash} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1197 <Avatar name={one.author} size={18} />
1198 <span className="min-w-0 grow">
1199 <Link to={`${base}/commit/${one.hash}`} className="block truncate hover:text-accent">
1200 {one.message}
1201 </Link>
1202 <span className="text-xs text-muted">{one.author}</span>
1203 </span>
1204 <Link to={`${base}/commit/${one.hash}`} className="hidden shrink-0 font-mono text-xs text-faint hover:text-fg sm:block">
1205 {one.hash.slice(0, 7)}
1206 </Link>
1207 <span className="w-16 shrink-0 text-right text-xs text-faint">
1208 <TimeAgo at={one.at} />
1209 </span>
1210 </li>
1211 ))}
1212 </ul>
1213 </Panel>
1214 )}
1215
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1216 <Panel title="Activity" icon={<ActivityIcon size={14} />}>
1217 {loaderData.eventsLoaded ? (
1218 <ActivityFeed groups={groups} showRepo={false} limit={8} empty="Nothing has happened here yet." />
1219 ) : (
1220 <Unavailable what="Activity" />
1221 )}
1222 </Panel>
1223
1224 {previews.length > 0 && (
1225 <Panel title="Previews" icon={<GitBranch size={14} />} count={previews.length} all={{ to: `${base}/deployments`, label: "Deployments" }}>
1226 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1227 {previews.map((app) => (
1228 <li key={app.url} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1229 <span className="min-w-0 grow">
1230 <a href={app.url} className="block truncate font-mono text-[0.8125rem] hover:text-accent">
1231 {host(app.url)}
1232 </a>
1233 <span className="text-xs text-muted">
1234 {app.branch}
1235 {app.number != null && (
1236 <>
1237 {" · "}
1238 <Link to={`${base}/pull/${app.number}`} className="hover:underline">
1239 #{app.number}
1240 </Link>
1241 </>
1242 )}
1243 </span>
Projects: what a workspace builds and runs, first on every page1244 </span>
1245 <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 API1246 <TimeAgo at={app.deployedAt} />
Projects: what a workspace builds and runs, first on every page1247 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1248 </li>
1249 ))}
1250 </ul>
1251 </Panel>
1252 )}
1253 </div>
Projects: what a workspace builds and runs, first on every page1254
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1255 <aside className="space-y-4 text-sm">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971256 {/* Deployments panel (deployments-panel.tsx): each environment's latest deployment. */}
1257 <DeploymentsPanel base={base} summary={loaderData.environments} className="rounded-xl border border-line bg-surface p-5" />
1258 {project && (
1259 <section className="rounded-xl border border-line bg-surface p-5">
1260 <div className="flex items-center justify-between">
1261 <h2 className="text-sm font-semibold">About</h2>
1262 {canChange && <AboutEditor project={project} />}
1263 </div>
1264 <p className={`mt-2.5 text-sm ${project.description ? "text-fg-soft" : "text-faint"}`}>
1265 {project.description ?? "No description."}
1266 </p>
1267 <LinkList links={project.links} className="mt-3" />
1268 <ul className="mt-4 space-y-2 text-xs text-muted">
1269 <li className="flex items-center gap-2">
1270 {project.kind === "library" || project.kind === "tool" ? (
1271 <PackageGlyph size={13} className="shrink-0 text-faint" />
1272 ) : project.kind === "docs" ? (
1273 <BookOpen size={13} className="shrink-0 text-faint" />
1274 ) : (
1275 <Rocket size={13} className="shrink-0 text-faint" />
1276 )}
1277 <Hint label={project.kindReason.detail}>
1278 <span>{kindLabel(project)}</span>
1279 </Hint>
1280 </li>
1281 {loaderData.release && (
1282 <li className="flex items-center gap-2">
1283 <Tag size={13} className="shrink-0 text-faint" />
1284 <Link to={`${base}/tags`} className="hover:text-fg">
1285 <span className="font-mono text-fg-soft">{loaderData.release.name}</span>
1286 {loaderData.release.at && (
1287 <span className="text-faint">
1288 {" · "}
1289 <TimeAgo at={loaderData.release.at} />
1290 </span>
1291 )}
1292 </Link>
1293 </li>
1294 )}
1295 {loaderData.languages && loaderData.languages.length > 0 && (
1296 <li className="flex items-center gap-2">
1297 <Code2 size={13} className="shrink-0 text-faint" />
1298 <span>
1299 {loaderData.languages
1300 .slice(0, 3)
1301 .map((language) => `${language.name} ${Math.round(language.share * 100)}%`)
1302 .join(" · ")}
1303 </span>
1304 </li>
1305 )}
1306 </ul>
1307 {loaderData.contributors && loaderData.contributors.length > 0 && (
1308 <div className="mt-4 flex flex-wrap gap-1">
1309 {loaderData.contributors.slice(0, 12).map((person) => (
1310 <Hint key={person.username} label={person.username}>
1311 <Link to={`/${person.username}`}>
1312 <Avatar name={person.username} size={22} />
1313 </Link>
1314 </Hint>
1315 ))}
1316 </div>
1317 )}
1318 </section>
1319 )}
1320
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1321 {member && (
1322 <section className="rounded-xl border border-line bg-surface p-5">
1323 <div className="flex items-center justify-between">
1324 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1325 <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 API1326 What agents know here
1327 </h2>
1328 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1329 Memory
1330 </Link>
Projects: what a workspace builds and runs, first on every page1331 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1332 {!loaderData.memoriesLoaded ? (
1333 <p className="mt-3 text-xs text-muted">Memory could not be loaded just now.</p>
1334 ) : knows.length === 0 ? (
1335 <p className="mt-3 text-xs leading-5 text-muted">
1336 Nothing yet. Agents note conventions, decisions and traps as they work, and every agent starting here reads them.
1337 </p>
1338 ) : (
1339 <ul className="mt-3 space-y-2.5">
1340 {knows.map((memory) => (
1341 <li key={memory.id} className="text-xs leading-5">
1342 <p className="line-clamp-3 text-fg-soft">
1343 {memory.pinned && <Pin size={11} className="mr-1 inline text-accent" />}
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1344 <InlineMarkdown text={memory.text} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1345 </p>
1346 <p className="text-faint">
1347 {memory.kind} · {memory.createdBy} · <TimeAgo at={memory.createdAt} />
1348 </p>
1349 </li>
1350 ))}
1351 {loaderData.memoryCount > knows.length && (
1352 <li>
1353 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1354 {loaderData.memoryCount - knows.length} more
1355 </Link>
1356 </li>
1357 )}
1358 </ul>
1359 )}
1360 </section>
Projects: what a workspace builds and runs, first on every page1361 )}
1362
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1363 <section className="rounded-xl border border-line bg-surface p-5">
1364 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1365 <HeartPulse size={14} className="text-faint" />
1366 Health
Project dependencies: addresses, preview stacks, Affects, and agents who know1367 </h2>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1368 <div className="mt-4 space-y-4 text-xs">
1369 <div>
1370 <div className="flex items-baseline justify-between">
1371 <span className="text-muted">Checks passing</span>
1372 <span className="font-medium tabular-nums">{percent(health.passRate)}</span>
1373 </div>
1374 <div className="mt-1.5">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1375 <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 API1376 </div>
1377 <p className="mt-1 text-faint">
1378 {health.checkRuns
1379 ? `${health.checkRuns} recent runs · ${percent(health.firstPass.rate)} pass on the first try`
1380 : "No checks have run recently."}
1381 </p>
1382 </div>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971383 {member && (plan === "production" || builds.length > 0) && (
Project dependencies: addresses, preview stacks, Affects, and agents who know1384 <div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1385 <div className="flex items-baseline justify-between">
1386 <span className="text-muted">Deploys</span>
1387 <Link to={`${base}/deployments`} className="text-faint hover:text-fg">
1388 {builds.length ? `last ${Math.min(builds.length, 20)}` : "none yet"}
1389 </Link>
1390 </div>
1391 <div className="mt-1.5">
1392 <DeployStrip builds={builds} base={base} />
1393 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1394 </div>
1395 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1396 <div>
1397 <div className="flex items-baseline justify-between">
1398 <span className="text-muted">Open issues by age</span>
1399 <Link to={`${base}/issues`} className="font-medium tabular-nums hover:text-accent">
1400 {open.issues}
1401 </Link>
1402 </div>
1403 {ages ? (
1404 <ul className="mt-2 space-y-1.5">
1405 {ages.map((bucket) => (
1406 <li key={bucket.label} className="grid grid-cols-[6.5rem_1fr_1.5rem] items-center gap-2">
1407 <span className="text-faint">{bucket.label}</span>
1408 <span className="block h-1.5 overflow-hidden rounded-full bg-line">
1409 <span
1410 className={`block h-full rounded-full ${bucket.label === "Older" ? "bg-warn" : "bg-fg-soft/60"}`}
1411 style={{ width: `${(bucket.count / oldest) * 100}%` }}
1412 />
1413 </span>
1414 <span className="text-right tabular-nums text-muted">{bucket.count}</span>
Project dependencies: addresses, preview stacks, Affects, and agents who know1415 </li>
1416 ))}
1417 </ul>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1418 ) : (
1419 <p className="mt-1 text-faint">Issues could not be loaded just now.</p>
1420 )}
1421 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1422 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1423 </section>
Project dependencies: addresses, preview stacks, Affects, and agents who know1424
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1425 <section className="rounded-xl border border-line bg-surface p-5">
1426 <div className="flex items-center justify-between">
1427 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1428 <Network size={14} className="text-faint" />
1429 Dependencies
1430 </h2>
1431 {member && (
1432 <Link to={`${base}/settings/dependencies`} className="text-xs text-muted hover:text-fg">
1433 Manage
1434 </Link>
1435 )}
1436 </div>
1437 {!dependencies ? (
1438 <p className="mt-3 text-xs text-muted">Dependencies could not be loaded just now.</p>
1439 ) : dependencies.dependsOn.length === 0 && dependencies.usedBy.length === 0 ? (
1440 <p className="mt-3 text-xs leading-5 text-muted">
1441 Uses no other project, and none uses it. Declare one, and builds get its address and agents know what depends on
1442 what.
1443 </p>
1444 ) : (
1445 <div className="mt-3 space-y-3 text-xs">
1446 {dependencies.dependsOn.length > 0 && (
1447 <div>
1448 <p className="flex items-center gap-1 text-muted">
1449 <ArrowUpRight size={12} /> Uses
1450 </p>
1451 <ul className="mt-1.5 space-y-1">
1452 {dependencies.dependsOn.map((d) => (
1453 <li key={d.slug} className="flex items-center justify-between gap-2">
1454 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1455 {d.name}
1456 </Link>
1457 {d.as && <code className="font-mono text-faint">{d.as}</code>}
1458 </li>
1459 ))}
1460 </ul>
1461 </div>
1462 )}
1463 {dependencies.usedBy.length > 0 && (
1464 <div>
1465 <p className="flex items-center gap-1 text-muted">
1466 <ArrowDownLeft size={12} /> Used by
1467 </p>
1468 <ul className="mt-1.5 space-y-1">
1469 {dependencies.usedBy.map((d) => (
1470 <li key={d.slug}>
1471 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1472 {d.name}
1473 </Link>
1474 </li>
1475 ))}
1476 </ul>
1477 </div>
1478 )}
1479 </div>
1480 )}
Projects: what a workspace builds and runs, first on every page1481 </section>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1482
1483 {source && (
1484 <section className="rounded-xl border border-line bg-surface p-5">
1485 <h2 className="text-sm font-semibold">Clone</h2>
1486 <div className="mt-3">
Merge branch 'worktree-agent-aaf03bdceac799c89'1487 <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 API1488 </div>
1489 </section>
1490 )}
1491 </aside>
1492 </div>
Projects: what a workspace builds and runs, first on every page1493 </div>
1494 );
1495}

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