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 API | 1 | import { |
| 2 | Activity as ActivityIcon, | |
| 3 | ArrowRight, | |
| 4 | ArrowUpRight, | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 5 | BookOpen, |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 6 | Bot, |
| 7 | Brain, | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 8 | Globe, |
| 9 | Tag, | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 10 | Code2, |
| 11 | GitBranch, | |
| 12 | GitCommitHorizontal, | |
| 13 | GitMerge, | |
| 14 | Hand, | |
| 15 | HeartPulse, | |
| 16 | Loader2, | |
| 17 | Lock, | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 18 | Package as PackageGlyph, |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 19 | Pin, |
| 20 | Rocket, | |
| 21 | RotateCw, | |
| 22 | } from "lucide-react"; | |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 23 | import { waitUntil } from "cloudflare:workers"; |
| 24 | import { isbot } from "isbot"; | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 25 | import { type ReactNode, Suspense } from "react"; |
| Merge branch 'worktree-agent-a985c01f43538af01' | 26 | import { Await, Form, Link } from "react-router"; |
| Projects: what a workspace builds and runs, first on every page | 27 | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 28 | import { |
| 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 API | 39 | |
| Projects: what a workspace builds and runs, first on every page | 40 | import type { Route } from "./+types/overview"; |
| Usage, Billing settings and prepaid AI credit; fixes from the UX audit | 41 | import { InlineMarkdown } from "../../components/inline-markdown"; |
| Merge branch 'site-fixes' into fast-push | 42 | import { DeployLink, host, StatusDot } from "../../components/deploy"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 43 | import { CheckBadge } from "../../components/checks"; |
| 44 | import { Elapsed, formatCost, useLiveRefresh } from "../../components/agents"; | |
| 45 | import { 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 look | 46 | import { type ActiveBranch, ActiveBranches } from "../../components/branches"; |
| 47 | import { ProductionChecklist } from "../../components/checklist"; | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 48 | import { PackageIcon } from "../../components/package-icon"; |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 49 | import { Hint } from "../../components/ui/hint"; |
| Merge branch 'worktree-agent-aca4790ea223aa235' | 50 | import { Loading, Skeleton, SkeletonRows } from "../../components/ui/skeleton"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 51 | import { ProductionShot } from "../../components/production-shot"; |
| 52 | import { GithubLinkStrip } from "../../components/github"; | |
| Merge branch 'mirroring' into artifacts-mode | 53 | import { MirrorOverviewNote, useRepoMirror } from "../../components/mirror"; |
| Usage, Billing settings and prepaid AI credit; fixes from the UX audit | 54 | import { distinctFacts } from "../../lib/memory-facts"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 55 | import { githubApp } from "../../lib/github.server"; |
| Merge branch 'worktree-agent-a985c01f43538af01' | 56 | import { Avatar, ButtonLink, CopyLine, SubmitButton, TimeAgo } from "../../components/ui"; |
| Overview: Recent changes show the avatar of whoever the line names, and every name opens its card | 57 | import { ChangeSize, PersonLink } from "../../components/work"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 58 | import { |
| 59 | type ActivityItem, | |
| 60 | type Need, | |
| 61 | type PipelineStage, | |
| 62 | PIPELINE, | |
| 63 | TIME, | |
| Site: a project's activity names people, not their account ids | 64 | actorIds, |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 65 | ageBuckets, |
| 66 | firstPassRate, | |
| 67 | groupActivity, | |
| Site: a project's activity names people, not their account ids | 68 | nameActor, |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 69 | passRate, |
| 70 | pipelineStage, | |
| Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md | 71 | PROJECT_FEED_EVENT_TYPES, |
| 72 | projectFeed, | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 73 | queuedNumbers, |
| 74 | rankNeeds, | |
| Merge branch 'browser-pass' into fast-push | 75 | runHealth, |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 76 | stuckMinutes, |
| 77 | } from "../../lib/mission"; | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 78 | import { 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 production | 79 | import { ECOSYSTEM_LABEL, installCommands } from "../../lib/packages"; |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 80 | import { |
| 81 | PUBLISH_GUIDES, | |
| 82 | bare, | |
| 83 | hasRelease, | |
| 84 | kindLabel, | |
| 85 | linksToShow, | |
| 86 | latestTag, | |
| 87 | libraryPackages, | |
| 88 | packageName, | |
| 89 | packagePath, | |
| 90 | projectChanges, | |
| 91 | publishGuide, | |
| 92 | } from "../../lib/project-kind"; | |
| 93 | import { AboutEditor, KindMenu, LinkList } from "../../components/project-about"; | |
| 94 | import { DocsHead, ElsewhereHead, type ExternalDeployment, OtherHead, WhereItRuns } from "../../components/project-head"; | |
| Site: a project's activity names people, not their account ids | 95 | import { 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-agent | 96 | import { madeByG1t } from "../../lib/opened-by"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 97 | import { assertSameOrigin, getViewer, requireUser } from "../../lib/session.server"; |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 98 | import { accessTo, countsFor, projectFor, refusal, repoFor } from "../../lib/access.server"; |
| Production thumbnails follow each deploy, not each commit, and old ones are cleared daily | 99 | import { shotVersion } from "./production-screenshot"; |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 100 | import { DeploymentsPanel } from "../../components/deployments-panel"; |
| 101 | import { environmentUrl, productionEnvironment } from "../../lib/deployments"; | |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 102 | import { cloneUrl, useAddresses } from "../../lib/addresses"; |
| Branches and Tags pages, each file's last commit, and the branch menu on files | 103 | import { readBranches } from "../../lib/branches.server"; |
| Merge checks: statuses and check runs on every commit | 104 | import { commitChecksFor } from "../../lib/commit-checks.server"; |
| Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016) | 105 | import { showCommits } from "../../lib/commit-people.server"; |
| 106 | import { CommitAvatars, CommitNames } from "../../components/commit-person"; | |
| Merge checks: statuses and check runs on every commit | 107 | import { CommitChecksBadge } from "../../components/commit-checks"; |
| Projects: what a workspace builds and runs, first on every page | 108 | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 109 | const MAX_LANDED = 6; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 110 | /** Branches read for the Active branches list, and shown. */ |
| 111 | const BRANCHES_READ = 10; | |
| 112 | const BRANCHES_SHOWN = 5; | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 113 | /** A library's packages shown on its overview; the rest are a link away. */ |
| 114 | const PACKAGES_SHOWN = 3; | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 115 | /** The default branch's latest commits listed. */ |
| 116 | const COMMITS_SHOWN = 5; | |
| 117 | /** How long Active branches may take before the section links to Branches instead. */ | |
| 118 | const BRANCHES_WAIT_MS = 3_500; | |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 119 | /** |
| 120 | * The same for a crawler, which gets the page only once everything in it | |
| 121 | * has settled (entry.server.tsx): past this, it gets the link to Branches. | |
| 122 | */ | |
| 123 | const BRANCHES_WAIT_CRAWLER_MS = 700; | |
| Projects: what a workspace builds and runs, first on every page | 124 | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 125 | /** |
| 126 | * The overview streams: the layout's header and tabs (one repository | |
| 127 | * lookup, one project) go out at once with a skeleton below them, and | |
| 128 | * everything here, seventeen calls across six services, follows in the | |
| 129 | * same response as it settles. Crawlers wait for all of it | |
| 130 | * (entry.server.tsx). Before, the first byte waited on the slowest of them. | |
| 131 | */ | |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 132 | export function loader({ params, context, request }: Route.LoaderArgs) { |
| 133 | const userAgent = request.headers.get("user-agent"); | |
| 134 | return { overview: overviewData({ params, context }, Boolean(userAgent && isbot(userAgent))) }; | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 135 | } |
| 136 | ||
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 137 | async function overviewData({ params, context }: Pick<Route.LoaderArgs, "params" | "context">, crawler: boolean) { |
| Projects: what a workspace builds and runs, first on every page | 138 | const viewer = getViewer(context); |
| 139 | const path = { namespace: params.owner, name: params.repo }; | |
| 140 | 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 API | 141 | const now = Date.now(); |
| 142 | // A section whose service fails shows its own empty state; the page stays up. | |
| 143 | const soft = <T,>(promise: Promise<T> | null): Promise<T | null> => | |
| 144 | promise ? promise.catch((error) => (console.warn("overview:", error), null)) : Promise.resolve(null); | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 145 | // People with a role of their own here see its running parts: deployments, |
| 146 | // memory, setup; everyone who can read it sees the rest. Not awaited: only | |
| 147 | // those sections wait on the repository lookup, which the layout makes in | |
| 148 | // this request too. | |
| 149 | const accessP = accessTo(context, params); | |
| 150 | const memberP = accessP.then((access) => access.insider); | |
| 151 | const forMembers = <T,>(start: () => Promise<T>): Promise<T | null> => | |
| 152 | memberP.then((member) => (member ? soft(start()) : null)); | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 153 | const repoP = soft(repoFor(context, params)); |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 154 | const projectP = soft(projectFor(context, params)); |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 155 | // A library or a tool shows its packages where an app shows production, |
| 156 | // and every project g1t does not deploy counts a workflow in its checklist. | |
| 157 | // Deployments from anywhere (g1t.page, g1t Actions, the API), by environment, for anyone who can read it. | |
| 158 | const environmentsP = projectP.then((found) => | |
| 159 | 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 production | 160 | ); |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 161 | const planP = projectP.then((project) => (project?.ok ? checklistPlan(project.value).plan : null)); |
| 162 | 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 production | 163 | const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null)); |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 164 | const workflowsP = planP.then((plan) => (plan && plan !== "production" ? forMembers(() => actions.workflows(path, viewer)) : null)); |
| Merge branch 'browser-pass' into fast-push | 165 | // Recent workflow runs, for Health when no pull request's checks have finished lately. |
| 166 | const workflowRunsP = soft(actions.runs(path, viewer, { limit: 30 })); | |
| Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md | 167 | // Only the kinds the feed shows: the newest events are mostly session |
| 168 | // steps and merge checks, which would otherwise crowd out everything. | |
| 169 | const eventsP = repoP.then((repo) => | |
| 170 | repo?.ok ? soft(eventLog.list({ repoId: repo.value.id, types: [...PROJECT_FEED_EVENT_TYPES], limit: 150 })) : null, | |
| 171 | ); | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 172 | const minePullsP = repoP.then((repo) => |
| 173 | repo?.ok && viewer | |
| 174 | ? soft(work.listActivePulls(viewer)).then((list) => (list ?? []).filter((item) => item.pull.repoId === repo.value.id)) | |
| 175 | : null, | |
| 176 | ); | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 177 | const openP = soft(work.listPulls(path, viewer, "open")); |
| 178 | // Where its code came from on GitHub, for members. | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 179 | const githubP = repoP.then((repo) => (repo?.ok ? forMembers(() => githubApp.link(repo.value.id)) : null)); |
| 180 | const listP = forMembers(() => deployments.list(ref, viewer)); | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 181 | // Active branches: the newest few besides the default, with how far each |
| 182 | // has moved. Branches with an open pull request are read first. | |
| 183 | const branchesP = Promise.all([repoP, soft(repos.branches(path, viewer)), openP, listP]).then( | |
| 184 | async ([repo, branchList, pulls, deploys]): Promise<{ main: string; total: number; shown: ActiveBranch[] } | null> => { | |
| 185 | if (!repo?.ok || !branchList?.ok) return null; | |
| 186 | const main = repo.value.defaultBranch; | |
| Branches and Tags pages, each file's last commit, and the branch menu on files | 187 | if (branchList.value.every((branch) => branch.name === main)) return { main, total: 0, shown: [] }; |
| 188 | const read = await readBranches( | |
| 189 | path, | |
| 190 | viewer, | |
| 191 | { | |
| 192 | defaultBranch: main, | |
| 193 | branches: branchList.value, | |
| 194 | pulls: pulls?.ok ? pulls.value : [], | |
| 195 | previews: deploys?.ok ? deploys.value.live.filter((app) => app.kind === "preview") : [], | |
| 196 | }, | |
| 197 | BRANCHES_READ, | |
| 198 | ); | |
| 199 | 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 look | 200 | }, |
| 201 | ); | |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 202 | // Active branches walk history when a branch or the default branch has |
| 203 | // moved: streamed, so the rest shows first. Bounded well inside the | |
| 204 | // response's stream timeout (entry.server.tsx): a promise still pending | |
| 205 | // when the stream ends never settles in the browser, and its skeleton | |
| 206 | // would stay. The walk finishes after the page if it must (waitUntil), | |
| 207 | // so repos keeps the answer and the next view has it. Crawlers, which | |
| 208 | // wait for the whole page, wait for it only briefly. | |
| 209 | waitUntil(branchesP.then(() => undefined, () => undefined)); | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 210 | const branches = Promise.race([ |
| 211 | branchesP.catch(() => null), | |
| Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25 | 212 | new Promise<"slow">((resolve) => setTimeout(() => resolve("slow"), crawler ? BRANCHES_WAIT_CRAWLER_MS : BRANCHES_WAIT_MS)), |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 213 | ]); |
| Projects no longer depend on each other: the dependsOn relation, its settings page (old links redirect), the overview card, a pull request's Affects panel and preview stacks, reference variables and agents' notes on what a project uses are gone; the table is dropped in a later deploy | 214 | const [{ insider: member, can }, project, settings, list, open, closed, log, counts, 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 incidents | 215 | accessP, |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 216 | projectP, |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 217 | forMembers(() => deployments.settings(ref, viewer)), |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 218 | listP, |
| 219 | openP, | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 220 | soft(work.listPulls(path, viewer, "closed")), |
| Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016) | 221 | // With their people, in one identity call. |
| 222 | soft(repos.log(path, viewer, null, COMMITS_SHOWN).then(async (found) => (found.ok ? { ok: true as const, value: await showCommits(found.value) } : found))), | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 223 | soft(countsFor(context, params)), |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 224 | soft(agents.listRuns(viewer, { repo: path, limit: 60 })), |
| 225 | soft(work.queue(path, viewer)), | |
| 226 | soft(work.listIssues(path, viewer, { state: "open" })), | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 227 | 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 API | 228 | eventsP, |
| 229 | minePullsP, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 230 | // For the checklist, which only members see. |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 231 | forMembers(() => deployments.domains(ref, viewer)), |
| 232 | 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 production | 233 | packagesP, |
| 234 | workflowsP, | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 235 | // The latest release, for About. |
| 236 | soft(repos.tags(path, viewer)), | |
| Projects: what a workspace builds and runs, first on every page | 237 | ]); |
| Merge branch 'browser-pass' into fast-push | 238 | const workflowRuns = await workflowRunsP; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 239 | const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null); |
| 240 | ||
| 241 | const openPulls = ok(open) ?? []; | |
| 242 | const runList = ok(runs) ?? []; | |
| 243 | const live = runList.filter((run) => isActiveRun(run.status)); | |
| 244 | const builds = ok(list)?.deployments ?? []; | |
| 245 | const landed = (ok(closed) ?? []) | |
| 246 | .filter((pull) => pull.status === "merged") | |
| 247 | .sort((a, b) => Date.parse(b.mergedAt ?? b.updatedAt) - Date.parse(a.mergedAt ?? a.updatedAt)); | |
| 248 | const queued = queuedNumbers(ok(queue)?.active ?? []); | |
| 249 | const runOn = new Map<number, AgentRun>(); | |
| 250 | for (const run of live) if (run.number != null && !runOn.has(run.number)) runOn.set(run.number, run); | |
| 251 | const lifecycleOf = new Map((mine ?? []).map((item) => [item.pull.number, item.lifecycle])); | |
| 252 | ||
| 253 | // --- The pipeline ----------------------------------------------------------- | |
| 254 | type Card = { | |
| 255 | number: number; | |
| 256 | title: string; | |
| 257 | agent: string; | |
| 258 | status: Pull["status"]; | |
| 259 | checkStatus: Pull["checkStatus"]; | |
| 260 | step: string | null; | |
| 261 | runKind: string | null; | |
| 262 | runStarted: string | null; | |
| 263 | needsYou: boolean; | |
| 264 | at: string; | |
| 265 | }; | |
| 266 | const columns: Record<PipelineStage, Card[]> = { working: [], checking: [], reviewing: [], queue: [], landed: [] }; | |
| 267 | for (const pull of openPulls.slice(0, 40)) { | |
| 268 | const run = runOn.get(pull.number); | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 269 | 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 API | 270 | number: pull.number, |
| 271 | title: pull.title, | |
| 272 | agent: pull.agent, | |
| 273 | status: pull.status, | |
| 274 | checkStatus: pull.checkStatus, | |
| 275 | step: run?.step ?? null, | |
| 276 | runKind: run ? RUN_KIND_LABEL[run.kind] : null, | |
| 277 | runStarted: run ? (run.startedAt ?? run.createdAt) : null, | |
| 278 | needsYou: lifecycleOf.get(pull.number)?.stage === "needs_you", | |
| 279 | at: pull.updatedAt, | |
| 280 | }); | |
| 281 | } | |
| 282 | for (const pull of landed.filter((p) => now - Date.parse(p.mergedAt ?? p.updatedAt) < 3 * TIME.DAY).slice(0, 5)) { | |
| 283 | columns.landed.push({ | |
| 284 | number: pull.number, | |
| 285 | title: pull.title, | |
| 286 | agent: pull.agent, | |
| 287 | status: pull.status, | |
| 288 | checkStatus: pull.checkStatus, | |
| 289 | step: null, | |
| 290 | runKind: null, | |
| 291 | runStarted: null, | |
| 292 | needsYou: false, | |
| 293 | at: pull.mergedAt ?? pull.updatedAt, | |
| 294 | }); | |
| 295 | } | |
| 296 | ||
| 297 | // --- Needs you --------------------------------------------------------------- | |
| 298 | const base = `/${params.owner}/${params.repo}`; | |
| 299 | const needs: Need[] = []; | |
| 300 | const latestProduction = builds.find((build) => build.kind === "production") ?? null; | |
| 301 | if (latestProduction?.status === "failed") { | |
| 302 | needs.push({ | |
| 303 | key: "deploy", | |
| 304 | kind: "deploy", | |
| 305 | title: "The production build failed", | |
| 306 | detail: latestProduction.error ?? "Production still serves the build before it.", | |
| 307 | to: `${base}/deployments/${latestProduction.id}`, | |
| 308 | action: "See the build", | |
| 309 | at: Date.parse(latestProduction.finishedAt ?? latestProduction.createdAt), | |
| 310 | where: latestProduction.commit.slice(0, 7), | |
| 311 | }); | |
| 312 | } | |
| 313 | const me = viewer?.username.toLowerCase(); | |
| 314 | for (const pull of openPulls) { | |
| 315 | const to = `${base}/pull/${pull.number}`; | |
| 316 | const where = `#${pull.number}`; | |
| 317 | const lifecycle = lifecycleOf.get(pull.number); | |
| 318 | if (lifecycle?.stage === "needs_you") { | |
| 319 | const conflict = /conflict/i.test(lifecycle.detail); | |
| 320 | 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 }); | |
| 321 | } else if (lifecycle?.stage === "ready") { | |
| 322 | 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 }); | |
| 323 | } | |
| g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights | 324 | // Never your own, nor one g1t made for you; whoever it is for asked. |
| 325 | const owner = workOwner(pull).username; | |
| 326 | if (me && pull.status === "open" && owner.toLowerCase() !== me && pull.reviewers.some((name) => name.toLowerCase() === me)) { | |
| 327 | 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 API | 328 | } |
| 329 | if (member && pull.status === "open" && pull.checkStatus === "failed" && !runOn.has(pull.number) && !lifecycle) { | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 330 | 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 API | 331 | } |
| 332 | } | |
| 333 | for (const run of live) { | |
| 334 | const minutes = stuckMinutes(run, now); | |
| 335 | if (minutes == null) continue; | |
| 336 | needs.push({ | |
| 337 | key: `run:${run.id}`, | |
| 338 | kind: "stuck", | |
| 339 | title: run.title ?? `${run.agent}'s run`, | |
| 340 | detail: `${run.agent} has reported nothing for ${minutes} min${run.step ? `. Last: ${run.step}` : ""}.`, | |
| 341 | to: `${base}/agents/runs/${run.id}`, | |
| 342 | action: "Look", | |
| 343 | at: Date.parse(run.updatedAt), | |
| 344 | where: run.number != null ? `#${run.number}` : null, | |
| 345 | }); | |
| 346 | } | |
| 347 | ||
| 348 | // --- Activity and health ------------------------------------------------------- | |
| 349 | const eventList: G1tEvent[] = recent ?? []; | |
| Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md | 350 | const logged: ActivityItem[] = projectFeed(eventList, path); |
| Site: a project's activity names people, not their account ids | 351 | // The log names people by account id: their usernames, in one lookup. |
| 352 | const ids = actorIds(logged.map((item) => item.actor)); | |
| 353 | const names = ids.length > 0 ? await soft(identity.usernames(ids)) : {}; | |
| 354 | 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 API | 355 | const checkEvents = eventList.flatMap((event) => |
| 356 | event.type === "checks.completed" ? [{ repo: "", number: event.data.number, at: Date.parse(event.time), passed: event.data.status === "passed" }] : [], | |
| 357 | ); | |
| 358 | const memoryList = ok(memories)?.project ?? []; | |
| Usage, Billing settings and prepaid AI credit; fixes from the UX audit | 359 | // Pinned first, then the newest; a fact remembered twice, once. |
| 360 | 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 API | 361 | const openIssues = ok(issues); |
| 362 | ||
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 363 | // --- Getting to production ---------------------------------------------------- |
| 364 | const wentLive = (status: string) => status === "ready" || status === "replaced" || status === "down"; | |
| 365 | const liveApps = ok(list)?.live ?? []; | |
| 366 | const commitNow = ok(log)?.[0] ?? null; | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 367 | // Production deployed somewhere other than g1t.page (by g1t Actions, or |
| 368 | // reported through the API): the slot an app deployed elsewhere shows. | |
| 369 | const environments = await environmentsP; | |
| 370 | const environmentList = environments?.ok ? environments.value : null; | |
| 371 | const reported = productionEnvironment(environmentList?.environments ?? []); | |
| 372 | const deployment: ExternalDeployment | null = | |
| 373 | reported?.latest && reported.latest.source !== "g1t_page" | |
| 374 | ? { | |
| 375 | environment: reported.name, | |
| 376 | url: environmentUrl(reported), | |
| 377 | state: reported.latest.state, | |
| 378 | sha: reported.latest.sha, | |
| 379 | created_at: reported.latest.updated_at, | |
| 380 | } | |
| 381 | : null; | |
| 382 | // An app that has not said where it runs, but reports production from | |
| 383 | // elsewhere, runs elsewhere. | |
| 384 | const found = ok(project); | |
| 385 | const projectValue = found && deployment && found.kind === "app" && found.runs == null ? { ...found, runs: "elsewhere" as const } : found; | |
| 386 | const plan = projectValue ? checklistPlan(projectValue) : { plan: "production" as const, title: "Get to production" }; | |
| 387 | const isLibrary = plan.plan === "release"; | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 388 | const published = libraryPackages(ok(packageList) ?? []); |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 389 | 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 production | 390 | const firstSteps = { |
| 391 | base, | |
| 392 | hasCode: commitNow != null || projectValue?.source.kind === "mirror", | |
| 393 | instructions: commitNow == null ? false : root?.ok ? hasInstructions(root.value.entries.map((entry) => entry.name)) : null, | |
| 394 | agentAssigned: agentWasAssigned({ | |
| 395 | runAgents: runList.map((run) => run.agent), | |
| 396 | pullAgents: [...openPulls, ...(ok(closed) ?? [])].map((pull) => pull.agent), | |
| 397 | issues: openIssues ?? [], | |
| 398 | }), | |
| 399 | }; | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 400 | const links = projectValue?.links; |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 401 | const checklist = !member |
| 402 | ? null | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 403 | : plan.plan !== "production" && plan.plan !== "release" |
| 404 | ? startChecklist(plan.plan, { | |
| 405 | ...firstSteps, | |
| 406 | hasWorkflow, | |
| 407 | productionUrl: projectValue?.productionUrl ?? null, | |
| 408 | hasLinks: !!links && (links.homepage != null || links.docs != null || links.custom.length > 0), | |
| 409 | hasDocsLink: !!links && (links.docs != null || links.homepage != null), | |
| 410 | }) | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 411 | : isLibrary |
| 412 | ? releaseChecklist({ | |
| 413 | ...firstSteps, | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 414 | hasWorkflow, |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 415 | released: hasRelease(published), |
| 416 | releaseTo: published[0] | |
| 417 | ? packagePath(published[0]) | |
| 418 | : (publishGuide(projectValue?.ecosystem ?? null)?.guide ?? "https://docs.g1t.sh/guides/packages/"), | |
| 419 | }) | |
| 420 | : productionChecklist({ | |
| 421 | ...firstSteps, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 422 | deploysEnabled: ok(settings)?.enabled ?? false, |
| 423 | productionDeployed: | |
| 424 | liveApps.some((app) => app.kind === "production") || | |
| 425 | builds.some((build) => build.kind === "production" && wentLive(build.status)), | |
| 426 | domains: ok(domains)?.domains.length ?? null, | |
| 427 | previewOpened: | |
| 428 | 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 production | 429 | }); |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 430 | |
| Merge checks: statuses and check runs on every commit | 431 | // The checks on the latest commit and each active branch's head, in |
| 432 | // one call once the branches are read: streamed in beside them. | |
| 433 | const checks = branches.then((found) => | |
| 434 | commitChecksFor(path, viewer, [commitNow?.hash, ...(found && found !== "slow" ? found.shown : []).map((branch) => branch.commit?.hash)]), | |
| 435 | ); | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 436 | const github = await githubP; |
| Projects: what a workspace builds and runs, first on every page | 437 | return { |
| 438 | member, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 439 | can, |
| 440 | github, | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 441 | project: projectValue, |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 442 | settings: ok(settings), |
| 443 | builds: builds.slice(0, 30), | |
| 444 | live: ok(list)?.live ?? [], | |
| 445 | commit: ok(log)?.[0] ?? null, | |
| Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016) | 446 | commits: (ok(log) ?? []).map((one) => ({ hash: one.hash, message: one.message.split("\n")[0] ?? "", author: one.author, coAuthors: one.coAuthors, at: one.authoredAt })), |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 447 | release: latestTag(ok(tagList) ?? []), |
| 448 | checklistTitle: plan.title, | |
| 449 | // Production as reported from outside g1t, for an app deployed elsewhere. | |
| 450 | deployment, | |
| 451 | // Every environment's latest deployment, for the Deployments panel. | |
| 452 | environments: environmentList, | |
| 453 | // About's languages and contributors, filled in by the files' About work. | |
| 454 | languages: null as { name: string; share: number }[] | null, | |
| 455 | 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 API | 456 | open: ok(counts) ?? { issues: openIssues?.length ?? 0, pulls: openPulls.length }, |
| 457 | agentsLive: live.slice(0, 6), | |
| 458 | runsLoaded: runs?.ok ?? false, | |
| 459 | pullsLoaded: open?.ok ?? false, | |
| 460 | columns, | |
| 461 | needs: rankNeeds(needs), | |
| 462 | landed: landed.slice(0, MAX_LANDED), | |
| 463 | groups: groupActivity(items).slice(0, 30), | |
| 464 | eventsLoaded: recent != null, | |
| 465 | health: { | |
| Merge branch 'browser-pass' into fast-push | 466 | // A pull request's checks when any finished lately; otherwise the |
| 467 | // project's workflow runs, for one that ships straight to its branch. | |
| 468 | ...(checkEvents.length > 0 || !workflowRuns?.ok | |
| 469 | ? { passRate: passRate(checkEvents.map((c) => c.passed)), firstPass: firstPassRate(checkEvents), checkRuns: checkEvents.length } | |
| 470 | : runHealth(workflowRuns.value)), | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 471 | ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null, |
| 472 | }, | |
| 473 | knows: knows as Memory[], | |
| 474 | memoryCount: memoryList.length, | |
| 475 | memoriesLoaded: memories?.ok ?? false, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 476 | checklist, |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 477 | library: isLibrary |
| 478 | ? { packages: published.slice(0, PACKAGES_SHOWN), total: published.length, loaded: packageList?.ok ?? false } | |
| 479 | : null, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 480 | branches, |
| Merge checks: statuses and check runs on every commit | 481 | checks, |
| Projects: what a workspace builds and runs, first on every page | 482 | }; |
| 483 | } | |
| 484 | ||
| 485 | export async function action({ request, params, context }: Route.ActionArgs) { | |
| 486 | assertSameOrigin(request); | |
| 487 | const user = requireUser(context, request); | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 488 | const form = await request.formData().catch(() => null); |
| 489 | const intent = form?.get("intent"); | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 490 | // What it is, where it runs, its description and links: its settings. |
| 491 | if (form && (intent === "kind" || intent === "about")) { | |
| 492 | const refused = await refusal(context, params, "manage_settings"); | |
| 493 | if (refused) return { error: refused }; | |
| 494 | const saved = await projects.update(user, params.owner, params.repo, projectChanges(form)); | |
| 495 | return saved.ok ? { notice: "Saved." } : { error: saved.error.message }; | |
| 496 | } | |
| Merge branch 'mirroring' into artifacts-mode | 497 | // Deploying is compute. Syncing with GitHub is under Settings → Mirroring. |
| 498 | const refused = await refusal(context, params, "run"); | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 499 | if (refused) return { error: refused }; |
| Projects: what a workspace builds and runs, first on every page | 500 | const started = await deployments.redeploy(user, { workspace: params.owner, slug: params.repo }, null); |
| 501 | return started.ok ? { notice: "Production is building." } : { error: started.error.message }; | |
| 502 | } | |
| 503 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 504 | type Loaded = Awaited<ReturnType<typeof overviewData>>; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 505 | type Card = Loaded["columns"]["working"][number]; |
| 506 | ||
| 507 | const STAGE_TONE: Record<PipelineStage, string> = { | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 508 | working: "bg-accent", |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 509 | checking: "bg-info", |
| 510 | reviewing: "bg-warn", | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 511 | queue: "bg-success-dim", |
| 512 | landed: "bg-success", | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 513 | }; |
| 514 | ||
| 515 | function PipelineCard({ card, base, stage }: { card: Card; base: string; stage: PipelineStage }) { | |
| 516 | return ( | |
| 517 | <li> | |
| 518 | <Link | |
| 519 | to={`${base}/pull/${card.number}`} | |
| 520 | prefetch="intent" | |
| 521 | 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' | 522 | 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 API | 523 | }`} |
| 524 | > | |
| 525 | <span className="line-clamp-2 text-[0.8125rem] leading-snug font-medium">{card.title}</span> | |
| 526 | <span className="mt-1.5 flex items-center gap-1.5 text-[0.6875rem] text-muted"> | |
| 527 | <Avatar name={card.agent} size={14} /> | |
| 528 | <span className="font-mono text-faint">#{card.number}</span> | |
| 529 | <span className="grow" /> | |
| 530 | {card.needsYou ? ( | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 531 | <Hint label="Stopped: a person decides what happens next"> |
| 532 | <span className="inline-flex items-center gap-1 text-warn"> | |
| 533 | <Hand size={11} /> you | |
| 534 | <span className="sr-only">: stopped, a person decides what happens next</span> | |
| 535 | </span> | |
| 536 | </Hint> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 537 | ) : stage === "landed" ? ( |
| 538 | <TimeAgo at={card.at} /> | |
| 539 | ) : card.runStarted ? ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 540 | <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 API | 541 | <Loader2 size={11} className="animate-spin" /> |
| 542 | <Elapsed from={card.runStarted} /> | |
| 543 | </span> | |
| 544 | ) : ( | |
| 545 | <CheckBadge status={card.checkStatus} /> | |
| 546 | )} | |
| 547 | </span> | |
| 548 | {card.step && ( | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 549 | <Hint label={card.step}> |
| 550 | <span className="mt-1.5 block truncate font-mono text-[0.6875rem] text-fg/70"> | |
| 551 | {card.runKind}: {card.step} | |
| 552 | </span> | |
| 553 | </Hint> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 554 | )} |
| 555 | </Link> | |
| 556 | </li> | |
| 557 | ); | |
| 558 | } | |
| 559 | ||
| 560 | /** Open pull requests moving through Working → Checking → Reviewing → Queue → Landed. */ | |
| 561 | function Pipeline({ columns, base }: { columns: Loaded["columns"]; base: string }) { | |
| 562 | return ( | |
| 563 | <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 look | 564 | {/* Each stage as tall as what is in it: an empty one stays short |
| 565 | instead of stretching to the busiest. */} | |
| 566 | <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 API | 567 | {PIPELINE.map(({ stage, label }, index) => { |
| 568 | const cards = columns[stage]; | |
| 569 | return ( | |
| 570 | <li key={stage} className="flex min-w-0 flex-col rounded-xl border border-line bg-surface p-2"> | |
| 571 | <p className="flex items-center gap-2 px-1 pt-0.5 pb-2 text-xs font-medium text-muted"> | |
| 572 | <span className={`size-1.5 rounded-full ${STAGE_TONE[stage]} ${cards.length && stage !== "landed" ? "animate-pulse" : ""}`} /> | |
| 573 | {label} | |
| 574 | <span className="tabular-nums text-faint">{cards.length}</span> | |
| 575 | {index < PIPELINE.length - 1 && <ArrowRight size={11} className="ml-auto text-line-strong" />} | |
| 576 | </p> | |
| 577 | {cards.length === 0 ? ( | |
| 578 | <p className="rounded-lg border border-dashed border-line/70 px-2 py-3 text-center text-[0.6875rem] text-faint">Empty</p> | |
| 579 | ) : ( | |
| 580 | <ul className="space-y-1.5"> | |
| 581 | {cards.slice(0, 6).map((card) => ( | |
| 582 | <PipelineCard key={card.number} card={card} base={base} stage={stage} /> | |
| 583 | ))} | |
| 584 | {cards.length > 6 && ( | |
| 585 | <li className="px-1 text-[0.6875rem] text-muted"> | |
| 586 | <Link to={`${base}/pulls`} className="hover:text-fg"> | |
| 587 | {cards.length - 6} more | |
| 588 | </Link> | |
| 589 | </li> | |
| 590 | )} | |
| 591 | </ul> | |
| 592 | )} | |
| 593 | </li> | |
| 594 | ); | |
| 595 | })} | |
| 596 | </ol> | |
| 597 | </div> | |
| 598 | ); | |
| 599 | } | |
| 600 | ||
| A project is an app or a library: libraries show their package and how to ship a release, not production | 601 | /** |
| 602 | * Where an app's overview shows production, a library's shows what it | |
| 603 | * publishes: each package with its latest version and how to install it, | |
| 604 | * or, before there is one, how to publish a first version. | |
| 605 | */ | |
| 606 | function LibraryHead({ | |
| 607 | library, | |
| 608 | project, | |
| 609 | base, | |
| 610 | workspace, | |
| 611 | }: { | |
| 612 | library: { packages: PackageSummary[]; total: number; loaded: boolean }; | |
| 613 | project: Project | null; | |
| 614 | base: string; | |
| 615 | workspace: string; | |
| 616 | }) { | |
| 617 | const guide = publishGuide(project?.ecosystem ?? null); | |
| 618 | const name = project?.name ?? base.split("/").pop(); | |
| 619 | return ( | |
| 620 | <div className="p-5 sm:p-6"> | |
| 621 | <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase"> | |
| 622 | <PackageGlyph size={13} className="text-accent" /> | |
| 623 | Packages | |
| 624 | </p> | |
| 625 | {library.packages.length > 0 ? ( | |
| 626 | <> | |
| 627 | <ul className="mt-3 space-y-4"> | |
| 628 | {library.packages.map((pkg) => { | |
| 629 | const commands = installCommands(pkg, pkg.latest, "you"); | |
| 630 | return ( | |
| 631 | <li key={pkg.id} className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:gap-5"> | |
| 632 | <Link to={packagePath(pkg)} className="group flex min-w-0 items-center gap-3 lg:w-72 lg:shrink-0"> | |
| 633 | <PackageIcon ecosystem={pkg.ecosystem} size={32} /> | |
| 634 | <span className="min-w-0"> | |
| 635 | <span className="block truncate font-mono text-[0.9375rem] font-medium group-hover:text-accent">{packageName(pkg)}</span> | |
| 636 | <span className="block text-xs text-muted"> | |
| 637 | {ECOSYSTEM_LABEL[pkg.ecosystem]} · {pkg.latest ?? "no versions yet"} | |
| 638 | {pkg.versions > 1 && ` · ${pkg.versions} versions`} | |
| 639 | </span> | |
| 640 | </span> | |
| 641 | </Link> | |
| 642 | <div className="min-w-0 grow space-y-1.5"> | |
| 643 | {/* The workspace's registry first, or the tool installs a package of the same name from elsewhere. */} | |
| 644 | {commands.registry && <CopyLine prompt text={commands.registry} />} | |
| 645 | <CopyLine prompt text={commands.install} /> | |
| 646 | </div> | |
| 647 | </li> | |
| 648 | ); | |
| 649 | })} | |
| 650 | </ul> | |
| 651 | {library.total > library.packages.length && ( | |
| 652 | <Link to={`/${workspace}/-/packages`} className="mt-3 inline-block text-xs text-muted hover:text-fg"> | |
| 653 | All {library.total} packages | |
| 654 | </Link> | |
| 655 | )} | |
| 656 | </> | |
| 657 | ) : ( | |
| 658 | <> | |
| 659 | <p className="mt-2 text-lg font-medium">{library.loaded ? "Not published yet" : "Packages could not be loaded just now"}</p> | |
| 660 | {guide ? ( | |
| 661 | <> | |
| 662 | <p className="mt-1 max-w-lg text-sm text-muted"> | |
| 663 | Publish a first version of {name} to the workspace's {guide.label} registry, for others to install | |
| 664 | {guide.start ? ":" : "."} | |
| 665 | </p> | |
| 666 | {guide.start && ( | |
| 667 | <div className="mt-3 max-w-lg"> | |
| 668 | <CopyLine prompt text={guide.start} /> | |
| 669 | </div> | |
| 670 | )} | |
| 671 | <a href={guide.guide} className="mt-3 inline-flex items-center gap-1 text-sm text-accent hover:underline"> | |
| 672 | Publishing to {guide.label} <ArrowUpRight size={13} /> | |
| 673 | </a> | |
| 674 | </> | |
| 675 | ) : ( | |
| 676 | <> | |
| 677 | <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> | |
| 678 | <p className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm"> | |
| 679 | {PUBLISH_GUIDES.map((one) => ( | |
| 680 | <a key={one.label} href={one.guide} className="inline-flex items-center gap-1 text-accent hover:underline"> | |
| 681 | {one.label} <ArrowUpRight size={13} /> | |
| 682 | </a> | |
| 683 | ))} | |
| 684 | </p> | |
| 685 | </> | |
| 686 | )} | |
| 687 | </> | |
| 688 | )} | |
| 689 | </div> | |
| 690 | ); | |
| 691 | } | |
| 692 | ||
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 693 | function Stat({ label, value, to }: { label: string; value: ReactNode; to?: string }) { |
| 694 | const body = ( | |
| 695 | <> | |
| 696 | <span className="block text-[0.6875rem] text-faint">{label}</span> | |
| 697 | <span className="mt-0.5 block truncate text-[0.8125rem] font-medium">{value}</span> | |
| 698 | </> | |
| 699 | ); | |
| 700 | return to ? ( | |
| 701 | <Link to={to} className="min-w-0 px-5 py-3 transition-colors hover:bg-raised/50"> | |
| 702 | {body} | |
| 703 | </Link> | |
| 704 | ) : ( | |
| 705 | <div className="min-w-0 px-5 py-3">{body}</div> | |
| 706 | ); | |
| 707 | } | |
| 708 | ||
| Projects: what a workspace builds and runs, first on every page | 709 | export default function ProjectOverview({ loaderData, actionData, params }: Route.ComponentProps) { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 710 | return ( |
| 711 | <Suspense fallback={<OverviewSkeleton />}> | |
| 712 | <Await resolve={loaderData.overview} errorElement={<OverviewFailed />}> | |
| Merge branch 'worktree-agent-aca4790ea223aa235' | 713 | {(overview) => ( |
| 714 | // Faded in over its outline, not switched for it. | |
| 715 | <div className="animate-fade-in"> | |
| 716 | <Overview loaderData={overview} actionData={actionData} params={params} /> | |
| 717 | </div> | |
| 718 | )} | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 719 | </Await> |
| 720 | </Suspense> | |
| 721 | ); | |
| 722 | } | |
| 723 | ||
| 724 | /** The overview's sections as they will sit, while they stream in. */ | |
| 725 | function OverviewSkeleton() { | |
| 726 | return ( | |
| Merge branch 'worktree-agent-aca4790ea223aa235' | 727 | <Loading className="space-y-8" aria-label="Loading the overview"> |
| 728 | {/* Production: its screenshot beside where it is live, then the facts under them. */} | |
| 729 | <section aria-hidden="true" className="overflow-hidden rounded-2xl border border-line bg-surface"> | |
| 730 | <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6"> | |
| 731 | <Skeleton className="aspect-[16/10] w-full shrink-0 rounded-lg sm:w-72" /> | |
| 732 | <div className="min-w-0 grow"> | |
| 733 | <Skeleton className="h-3 w-24" /> | |
| 734 | <Skeleton className="mt-3 h-5 w-72 max-w-full" /> | |
| 735 | <Skeleton className="mt-3 h-3 w-48 max-w-full" /> | |
| 736 | </div> | |
| 737 | </div> | |
| 738 | <div className="grid grid-cols-2 gap-px border-t border-line bg-line sm:grid-cols-4"> | |
| 739 | {Array.from({ length: 4 }, (_, index) => ( | |
| 740 | <div key={index} className="bg-surface px-5 py-4"> | |
| 741 | <Skeleton className="h-3 w-16" /> | |
| 742 | <Skeleton className="mt-2.5 h-4 w-28" /> | |
| 743 | </div> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 744 | ))} |
| 745 | </div> | |
| 746 | </section> | |
| Merge branch 'worktree-agent-aca4790ea223aa235' | 747 | {/* Right now: the work, column by column. */} |
| 748 | <div aria-hidden="true" className="grid grid-cols-2 gap-2 sm:grid-cols-5"> | |
| 749 | {Array.from({ length: 5 }, (_, index) => ( | |
| 750 | <div key={index} className="rounded-xl border border-line bg-surface p-3"> | |
| 751 | <Skeleton className="h-3 w-20" /> | |
| 752 | <Skeleton className="mt-3 h-14 rounded-lg" /> | |
| 753 | </div> | |
| 754 | ))} | |
| 755 | </div> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 756 | <div className="grid gap-6 lg:grid-cols-2"> |
| 757 | <section className="rounded-2xl border border-line bg-surface"> | |
| 758 | <SkeletonRows rows={4} /> | |
| 759 | </section> | |
| 760 | <section className="rounded-2xl border border-line bg-surface"> | |
| 761 | <SkeletonRows rows={4} /> | |
| 762 | </section> | |
| 763 | </div> | |
| Merge branch 'worktree-agent-aca4790ea223aa235' | 764 | </Loading> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 765 | ); |
| 766 | } | |
| 767 | ||
| 768 | function OverviewFailed() { | |
| 769 | return ( | |
| 770 | <p className="rounded-2xl border border-line bg-surface p-6 text-sm text-muted"> | |
| 771 | The overview could not be loaded just now. Reload the page to try again. | |
| 772 | </p> | |
| 773 | ); | |
| 774 | } | |
| 775 | ||
| 776 | function Overview({ | |
| 777 | loaderData, | |
| 778 | actionData, | |
| 779 | params, | |
| 780 | }: { | |
| 781 | loaderData: Loaded; | |
| 782 | actionData: Route.ComponentProps["actionData"]; | |
| 783 | params: Route.ComponentProps["params"]; | |
| 784 | }) { | |
| Projects no longer depend on each other: the dependsOn relation, its settings page (old links redirect), the overview card, a pull request's Affects panel and preview stacks, reference variables and agents' notes on what a project uses are gone; the table is dropped in a later deploy | 785 | const { member, project, settings, builds, live, commit, open, 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 look | 786 | loaderData; |
| Projects: what a workspace builds and runs, first on every page | 787 | const base = `/${params.owner}/${params.repo}`; |
| Merge branch 'mirroring' into artifacts-mode | 788 | // What it is to its remotes, from the repository layout. |
| 789 | const { repo: mirrorRepo, briefs: mirrorBriefs, admin: mirrorAdmin } = useRepoMirror(); | |
| Projects: what a workspace builds and runs, first on every page | 790 | const production = live.find((app) => app.kind === "production") ?? null; |
| Agents and memory, checks and conflicts, profiles, slug renames, custom domains | 791 | // The project's own domain, once it is active, is where production is visited. |
| 792 | const productionUrl = production ? (settings?.primaryDomain ? `https://${settings.primaryDomain}` : production.url) : null; | |
| Projects: what a workspace builds and runs, first on every page | 793 | const previews = live.filter((app) => app.kind === "preview"); |
| 794 | const latestProduction = builds.find((build) => build.kind === "production") ?? null; | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 795 | const plan = project ? checklistPlan(project).plan : "production"; |
| 796 | const canChange = member && loaderData.can.manage_settings; | |
| 797 | // Its homepage and docs beside what it is, unless the card below already links there. | |
| 798 | const headUrl = | |
| 799 | plan === "production" | |
| 800 | ? (productionUrl ?? settings?.productionUrl) | |
| 801 | : plan === "elsewhere" | |
| 802 | ? (loaderData.deployment?.url ?? project?.productionUrl) | |
| 803 | : plan === "docs" | |
| 804 | ? (project?.links.docs ?? project?.productionUrl ?? project?.links.homepage) | |
| 805 | : plan === "other" | |
| 806 | ? (project?.links.homepage ?? project?.links.docs) | |
| 807 | : null; | |
| 808 | const stripLinks = project ? linksToShow({ ...project.links, custom: [] }, [headUrl]) : []; | |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 809 | const addresses = useAddresses(); |
| Projects: what a workspace builds and runs, first on every page | 810 | 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 API | 811 | const moving = agentsLive.length > 0 || columns.working.length + columns.checking.length > 0; |
| 812 | useLiveRefresh(moving); | |
| 813 | const ages = health.ages; | |
| 814 | const oldest = ages ? Math.max(1, ...ages.map((a) => a.count)) : 1; | |
| Projects: what a workspace builds and runs, first on every page | 815 | |
| 816 | return ( | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 817 | <div className="space-y-8"> |
| Merge branch 'mirroring' into artifacts-mode | 818 | {loaderData.github?.mode === "import" ? ( |
| 819 | <GithubLinkStrip link={loaderData.github} /> | |
| 820 | ) : ( | |
| 821 | <MirrorOverviewNote | |
| 822 | base={base} | |
| 823 | mirror={mirrorRepo?.mirror} | |
| 824 | briefs={mirrorBriefs} | |
| 825 | admin={mirrorAdmin} | |
| 826 | syncedAt={loaderData.github?.syncedAt} | |
| 827 | lastError={loaderData.github?.lastError} | |
| 828 | /> | |
| 829 | )} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 830 | {/* What the project is, running, and where its code is. */} |
| 831 | <section className="overflow-hidden rounded-2xl border border-line bg-surface"> | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 832 | {project && ( |
| 833 | <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"> | |
| 834 | <KindMenu project={project} label={kindLabel(project)} canChange={canChange} /> | |
| 835 | {stripLinks.map((link) => ( | |
| Merge branch 'site-fixes' into fast-push | 836 | <DeployLink key={link.key} href={link.url} className="inline-flex min-w-0 items-center gap-1.5 text-muted hover:text-fg"> |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 837 | {link.type === "docs" ? <BookOpen size={12} className="shrink-0 text-faint" /> : <Globe size={12} className="shrink-0 text-faint" />} |
| 838 | <span className="truncate">{link.type === "docs" ? `Docs · ${bare(link.url)}` : link.label}</span> | |
| Merge branch 'site-fixes' into fast-push | 839 | </DeployLink> |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 840 | ))} |
| 841 | </div> | |
| 842 | )} | |
| 843 | {plan === "elsewhere" && project ? ( | |
| 844 | <ElsewhereHead | |
| 845 | project={project} | |
| 846 | base={base} | |
| 847 | canChange={canChange} | |
| 848 | canDeploy={member && loaderData.can.manage_integrations} | |
| 849 | deployment={loaderData.deployment} | |
| 850 | commit={commit} | |
| Merge checks: statuses and check runs on every commit | 851 | checks={commit ? <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" /> : undefined} |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 852 | /> |
| 853 | ) : plan === "unknown" && project ? ( | |
| 854 | <WhereItRuns project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} /> | |
| 855 | ) : plan === "docs" && project ? ( | |
| 856 | <DocsHead project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} /> | |
| 857 | ) : plan === "other" && project ? ( | |
| 858 | <OtherHead project={project} base={base} canChange={canChange} /> | |
| 859 | ) : library ? ( | |
| 860 | <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 production | 861 | ) : ( |
| 862 | <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6"> | |
| 863 | {member && (production || settings?.enabled) && ( | |
| 864 | <ProductionShot | |
| 865 | src={production ? `${base}/production.jpg?v=${shotVersion(production)}` : null} | |
| 866 | href={production ? (productionUrl ?? production.url) : null} | |
| 867 | label={host(production ? (productionUrl ?? production.url) : (settings?.productionUrl ?? ""))} | |
| 868 | className="w-full shrink-0 sm:w-60 lg:w-72" | |
| 869 | /> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 870 | )} |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 871 | <div className="flex min-w-0 grow flex-wrap items-start justify-between gap-4"> |
| 872 | <div className="min-w-0"> | |
| 873 | <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase"> | |
| 874 | <Rocket size={13} className="text-accent" /> | |
| 875 | Production | |
| 876 | </p> | |
| 877 | {production ? ( | |
| 878 | <> | |
| Merge branch 'site-fixes' into fast-push | 879 | <DeployLink |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 880 | href={productionUrl ?? production.url} |
| 881 | className="mt-2 flex items-center gap-1.5 truncate font-mono text-lg font-medium hover:text-accent" | |
| 882 | > | |
| 883 | {host(productionUrl ?? production.url)} | |
| 884 | <ArrowUpRight size={16} className="shrink-0 text-faint" /> | |
| Merge branch 'site-fixes' into fast-push | 885 | </DeployLink> |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 886 | <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted"> |
| 887 | {latestProduction && <StatusDot status={latestProduction.status} label={latestProduction.status === "ready" ? "Live" : undefined} />} | |
| 888 | <span className="inline-flex items-center gap-1 font-mono"> | |
| 889 | <GitCommitHorizontal size={13} className="text-faint" /> | |
| 890 | {production.commit.slice(0, 7)} | |
| 891 | </span> | |
| 892 | <span> | |
| 893 | deployed <TimeAgo at={production.deployedAt} /> | |
| 894 | </span> | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 895 | {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 production | 896 | </> |
| 897 | ) : settings?.enabled ? ( | |
| 898 | <> | |
| 899 | <p className="mt-2 font-mono text-lg text-muted">{host(settings.productionUrl)}</p> | |
| 900 | <p className="mt-1.5 text-xs text-muted"> | |
| 901 | {latestProduction ? ( | |
| 902 | <StatusDot status={latestProduction.status} label={latestProduction.status === "failed" ? "The last build failed" : undefined} /> | |
| 903 | ) : ( | |
| 904 | "Not deployed yet. Push to the default branch, or deploy it now." | |
| 905 | )} | |
| 906 | </p> | |
| 907 | </> | |
| 908 | ) : ( | |
| 909 | <> | |
| 910 | <p className="mt-2 text-lg font-medium">Deployments are off</p> | |
| 911 | <p className="mt-1 max-w-lg text-sm text-muted"> | |
| 912 | Nothing builds or runs until you turn them on. Then {project?.name ?? params.repo} goes up on g1t.page: | |
| 913 | production from the default branch, and a live preview for every pull request. It runs only while | |
| 914 | someone visits. | |
| 915 | </p> | |
| 916 | </> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 917 | )} |
| Projects: what a workspace builds and runs, first on every page | 918 | </div> |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 919 | {member && ( |
| 920 | <div className="flex shrink-0 items-center gap-2"> | |
| 921 | {production && ( | |
| Merge branch 'site-fixes' into fast-push | 922 | <ButtonLink to={productionUrl ?? production.url} variant="accent" reloadDocument target="_blank" rel="noopener noreferrer"> |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 923 | Visit |
| 924 | <ArrowUpRight size={14} /> | |
| 925 | </ButtonLink> | |
| 926 | )} | |
| 927 | {settings?.enabled ? ( | |
| 928 | loaderData.can.run && <Form method="post"> | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 929 | <Hint label="Build production again from the default branch"> |
| 930 | <SubmitButton variant="quiet" name="intent" value="redeploy" pending="Redeploying…"> | |
| 931 | <RotateCw size={14} /> | |
| 932 | Redeploy | |
| 933 | </SubmitButton> | |
| 934 | </Hint> | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 935 | </Form> |
| 936 | ) : loaderData.can.manage_integrations && ( | |
| 937 | <ButtonLink to={`${base}/settings/deployments`} variant="accent"> | |
| 938 | <Rocket size={14} /> | |
| 939 | Turn on deployments | |
| 940 | </ButtonLink> | |
| 941 | )} | |
| 942 | </div> | |
| 943 | )} | |
| 944 | </div> | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 945 | </div> |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 946 | )} |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 947 | {actionData && ( |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 948 | <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 API | 949 | {"error" in actionData ? actionData.error : actionData.notice} |
| 950 | </p> | |
| 951 | )} | |
| 952 | <div className="grid grid-cols-2 divide-line border-t border-line text-sm sm:grid-cols-4 sm:divide-x"> | |
| 953 | <Stat | |
| 954 | label="Source" | |
| 955 | to={`${base}/code`} | |
| 956 | value={ | |
| 957 | source ? ( | |
| 958 | <span className="inline-flex items-center gap-1.5 font-mono"> | |
| 959 | {project?.private ? <Lock size={12} className="text-faint" /> : <Code2 size={12} className="text-faint" />} | |
| 960 | {source.repo.name} | |
| 961 | {source.rootDir ? `/${source.rootDir}` : ""} | |
| 962 | </span> | |
| 963 | ) : ( | |
| 964 | "—" | |
| 965 | ) | |
| 966 | } | |
| 967 | /> | |
| 968 | <Stat | |
| 969 | label="Default branch" | |
| 970 | to={`${base}/commits`} | |
| 971 | value={ | |
| 972 | <span className="inline-flex items-center gap-1.5 font-mono"> | |
| 973 | <GitBranch size={12} className="text-faint" /> | |
| 974 | {source?.defaultBranch ?? "main"} | |
| 975 | </span> | |
| 976 | } | |
| 977 | /> | |
| 978 | <Stat | |
| 979 | label="Latest commit" | |
| 980 | value={ | |
| 981 | commit ? ( | |
| Merge checks: statuses and check runs on every commit | 982 | <span className="flex min-w-0 items-center gap-1"> |
| 983 | <Hint label={commit.message.split("\n")[0]}> | |
| 984 | <Link to={`${base}/commit/${commit.hash}`} className="min-w-0 truncate hover:text-accent"> | |
| 985 | <span className="font-mono">{commit.hash.slice(0, 7)}</span>{" "} | |
| 986 | <span className="font-normal text-muted"> | |
| 987 | <TimeAgo at={commit.authoredAt} /> | |
| 988 | </span> | |
| 989 | </Link> | |
| 990 | </Hint> | |
| 991 | {/* A button, so beside the link rather than in it. */} | |
| 992 | <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" /> | |
| 993 | </span> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 994 | ) : ( |
| 995 | "No commits yet" | |
| 996 | ) | |
| 997 | } | |
| 998 | /> | |
| 999 | <Stat | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 1000 | label={member && settings?.enabled && !library ? "Last deploy" : "Open"} |
| 1001 | 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 API | 1002 | value={ |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 1003 | member && settings?.enabled && !library ? ( |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1004 | latestProduction ? ( |
| 1005 | <span className="inline-flex items-center gap-2"> | |
| 1006 | <StatusDot status={latestProduction.status} /> | |
| 1007 | <span className="font-normal text-muted"> | |
| 1008 | <TimeAgo at={latestProduction.createdAt} /> | |
| Projects: what a workspace builds and runs, first on every page | 1009 | </span> |
| 1010 | </span> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1011 | ) : ( |
| 1012 | "None yet" | |
| 1013 | ) | |
| 1014 | ) : ( | |
| 1015 | `${open.pulls} pull requests · ${open.issues} issues` | |
| 1016 | ) | |
| 1017 | } | |
| 1018 | /> | |
| 1019 | </div> | |
| 1020 | {commit && ( | |
| 1021 | <p className="truncate border-t border-line px-5 py-2.5 text-xs text-muted sm:px-6"> | |
| 1022 | <GitCommitHorizontal size={12} className="mr-1.5 inline text-faint" /> | |
| Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016) | 1023 | {commit.message.split("\n")[0]}{" "} |
| 1024 | <span className="text-faint"> | |
| 1025 | · <CommitNames commit={commit} className="hover:text-fg" /> | |
| 1026 | </span> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1027 | </p> |
| 1028 | )} | |
| 1029 | </section> | |
| 1030 | ||
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1031 | {checklist && <ProductionChecklist base={base} items={checklist} title={loaderData.checklistTitle} />} |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 1032 | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1033 | <Panel |
| 1034 | title="Right now" | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 1035 | 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 API | 1036 | count={agentsLive.length} |
| 1037 | all={{ to: `${base}/agents`, label: "Agents" }} | |
| 1038 | > | |
| 1039 | {agentsLive.length > 0 && ( | |
| 1040 | <ul className="mb-3 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3"> | |
| 1041 | {agentsLive.map((run) => ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 1042 | <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 API | 1043 | <div className="flex items-center gap-2 text-sm"> |
| 1044 | <Avatar name={run.agent} size={18} /> | |
| 1045 | <span className="font-medium">{run.agent}</span> | |
| 1046 | <span className="text-xs text-muted">{RUN_KIND_LABEL[run.kind].toLowerCase()}</span> | |
| 1047 | <span className="grow" /> | |
| 1048 | <span className="text-xs text-muted"> | |
| 1049 | <Elapsed from={run.startedAt ?? run.createdAt} /> | |
| Projects: what a workspace builds and runs, first on every page | 1050 | </span> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1051 | </div> |
| 1052 | <Link | |
| 1053 | to={run.number != null ? `${base}/pull/${run.number}` : `${base}/agents/runs/${run.id}`} | |
| 1054 | className="mt-1 block truncate text-[0.8125rem] hover:text-accent" | |
| 1055 | > | |
| 1056 | {run.title ?? "A run"} {run.number != null && <span className="text-faint">#{run.number}</span>} | |
| 1057 | </Link> | |
| 1058 | {run.step && ( | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 1059 | <Hint label={run.step}> |
| 1060 | <p className="mt-1 truncate font-mono text-[0.6875rem] text-fg/70"> | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 1061 | <span className="mr-1.5 inline-block size-1.5 animate-pulse rounded-full bg-accent align-middle" /> |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 1062 | {run.step} |
| 1063 | </p> | |
| 1064 | </Hint> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1065 | )} |
| 1066 | <p className="mt-1.5 flex items-center gap-3 text-[0.6875rem] text-faint"> | |
| 1067 | {formatCost(run.costUsd) && <span>{formatCost(run.costUsd)} so far</span>} | |
| 1068 | <Link to={`${base}/agents/runs/${run.id}`} className="hover:text-fg"> | |
| 1069 | {run.stepCount} steps | |
| 1070 | </Link> | |
| 1071 | {run.number != null && ( | |
| 1072 | <Link to={`${base}/sessions/${run.number}`} className="hover:text-fg"> | |
| 1073 | Session | |
| 1074 | </Link> | |
| 1075 | )} | |
| 1076 | </p> | |
| 1077 | </li> | |
| 1078 | ))} | |
| 1079 | </ul> | |
| 1080 | )} | |
| 1081 | {!loaderData.pullsLoaded ? ( | |
| 1082 | <Unavailable what="Pull requests" /> | |
| 1083 | ) : Object.values(columns).every((c) => c.length === 0) ? ( | |
| 1084 | <Quiet | |
| 1085 | action={ | |
| 1086 | member ? ( | |
| You choose how g1t looks: Auto follows your system, Light and Dark stay put, set from the account menu, the phone's You and help sheet, Settings → Account or the command palette; the page is drawn in your choice from the first byte, every colour has a light value that reads at 4.5:1 or better, code and diffs are highlighted for both, diagrams and the document editor follow along, the product tour and the g1t mark's avatar stay dark, and Dark is what you see until you choose; the workspaces guide says how. | 1087 | <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-fg-hover"> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1088 | <Bot size={12} /> Open an issue for an agent |
| 1089 | </Link> | |
| 1090 | ) : null | |
| 1091 | } | |
| 1092 | > | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 1093 | 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 API | 1094 | </Quiet> |
| 1095 | ) : ( | |
| 1096 | <Pipeline columns={columns} base={base} /> | |
| Projects: what a workspace builds and runs, first on every page | 1097 | )} |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1098 | </Panel> |
| Projects: what a workspace builds and runs, first on every page | 1099 | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1100 | <div className="grid gap-x-8 gap-y-8 lg:grid-cols-[minmax(0,1fr)_20rem]"> |
| 1101 | <div className="min-w-0 space-y-8"> | |
| 1102 | {needs.length > 0 && ( | |
| 1103 | <Panel title="Needs you" icon={<Hand size={14} className="text-warn" />} count={needs.length}> | |
| 1104 | <NeedsList needs={needs} limit={5} /> | |
| 1105 | </Panel> | |
| 1106 | )} | |
| 1107 | ||
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 1108 | <Suspense |
| 1109 | fallback={ | |
| 1110 | <Panel title="Active branches" icon={<GitBranch size={14} />}> | |
| 1111 | <div aria-busy="true"> | |
| 1112 | <SkeletonRows rows={3} rowClassName="h-12" /> | |
| 1113 | </div> | |
| 1114 | </Panel> | |
| 1115 | } | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 1116 | > |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1117 | <Await |
| 1118 | resolve={branches} | |
| 1119 | errorElement={ | |
| 1120 | <Panel title="Active branches" icon={<GitBranch size={14} />}> | |
| 1121 | <Unavailable what="Branches" /> | |
| 1122 | </Panel> | |
| 1123 | } | |
| 1124 | > | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 1125 | {(branches) => ( |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1126 | <Panel |
| 1127 | title="Active branches" | |
| 1128 | icon={<GitBranch size={14} />} | |
| 1129 | count={branches && branches !== "slow" ? branches.total || null : null} | |
| 1130 | all={{ to: `${base}/branches`, label: "All branches" }} | |
| 1131 | > | |
| 1132 | {branches === "slow" ? ( | |
| 1133 | <Quiet> | |
| 1134 | Its branches are taking a while to compare.{" "} | |
| 1135 | <Link to={`${base}/branches`} className="text-accent hover:underline"> | |
| 1136 | See them on Branches | |
| 1137 | </Link> | |
| 1138 | . | |
| 1139 | </Quiet> | |
| 1140 | ) : !branches ? ( | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 1141 | <Unavailable what="Branches" /> |
| 1142 | ) : branches.shown.length === 0 ? ( | |
| 1143 | <Quiet> | |
| 1144 | Only {branches.main} so far. Branches pushed here show with how far each has moved from {branches.main}, and | |
| 1145 | its pull request and preview. | |
| 1146 | </Quiet> | |
| 1147 | ) : ( | |
| 1148 | <> | |
| Merge checks: statuses and check runs on every commit | 1149 | <ActiveBranches branches={branches.shown} base={base} main={branches.main} checks={checks} /> |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 1150 | {branches.total > branches.shown.length && ( |
| 1151 | <p className="mt-2 px-1 text-xs text-faint"> | |
| 1152 | The {branches.shown.length} most recently changed of {branches.total} branches. | |
| 1153 | </p> | |
| 1154 | )} | |
| 1155 | </> | |
| 1156 | )} | |
| 1157 | </Panel> | |
| 1158 | )} | |
| 1159 | </Await> | |
| 1160 | </Suspense> | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 1161 | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1162 | <Panel title="Recent changes" icon={<GitMerge size={14} />} all={{ to: `${base}/pulls?state=closed`, label: "All landed" }}> |
| 1163 | {landed.length === 0 ? ( | |
| 1164 | <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 page | 1165 | ) : ( |
| 1166 | <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 API | 1167 | {landed.map((pull) => { |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 1168 | const byAgent = pull.runtime === "hosted" || madeByG1t(pull); |
| Projects: what a workspace builds and runs, first on every page | 1169 | return ( |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1170 | <li key={pull.id} className="flex items-start gap-3 px-4 py-3"> |
| 1171 | <GitMerge size={15} className="mt-0.5 shrink-0 text-merged" /> | |
| 1172 | <span className="min-w-0 grow"> | |
| 1173 | <Link to={`${base}/pull/${pull.number}`} prefetch="intent" className="block truncate text-sm font-medium hover:text-accent"> | |
| 1174 | {pull.title} | |
| 1175 | </Link> | |
| 1176 | <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted"> | |
| 1177 | <span className="font-mono text-faint">#{pull.number}</span> | |
| 1178 | <span>·</span> | |
| Overview: Recent changes show the avatar of whoever the line names, and every name opens its card | 1179 | {/* The avatar is whoever the line names first: the |
| 1180 | agent that made it, or the person who wrote it | |
| 1181 | with their own tools. */} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1182 | <span className="inline-flex items-center gap-1"> |
| Overview: Recent changes show the avatar of whoever the line names, and every name opens its card | 1183 | {pull.requestedBy ? ( |
| 1184 | <> | |
| 1185 | <Avatar name={pull.author.username} size={13} /> | |
| 1186 | made by <PersonLink name={pull.author.username} className="hover:text-fg" /> for{" "} | |
| 1187 | <PersonLink name={pull.requestedBy.username} className="hover:text-fg" /> | |
| 1188 | </> | |
| 1189 | ) : byAgent ? ( | |
| 1190 | <> | |
| 1191 | <Avatar name={pull.agent} size={13} /> | |
| 1192 | made by {pull.agent} | |
| 1193 | </> | |
| 1194 | ) : ( | |
| 1195 | <> | |
| 1196 | <Avatar name={pull.author.username} size={13} /> | |
| 1197 | by <PersonLink name={pull.author.username} className="hover:text-fg" /> | |
| 1198 | </> | |
| 1199 | )} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1200 | </span> |
| 1201 | {pull.issue != null && ( | |
| 1202 | <> | |
| 1203 | <span>· for</span> | |
| 1204 | <Link to={`${base}/issues/${pull.issue}`} className="hover:text-fg"> | |
| 1205 | #{pull.issue} | |
| 1206 | </Link> | |
| 1207 | </> | |
| 1208 | )} | |
| Overview: Recent changes show the avatar of whoever the line names, and every name opens its card | 1209 | {pull.mergedBy && ( |
| 1210 | <span> | |
| 1211 | · landed by <PersonLink name={pull.mergedBy} className="hover:text-fg" /> | |
| 1212 | </span> | |
| 1213 | )} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1214 | <span> |
| 1215 | · <TimeAgo at={pull.mergedAt ?? pull.updatedAt} /> | |
| Projects: what a workspace builds and runs, first on every page | 1216 | </span> |
| 1217 | </span> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1218 | </span> |
| 1219 | <span className="hidden shrink-0 text-xs text-faint sm:block"> | |
| 1220 | <ChangeSize files={pull.files} /> | |
| 1221 | </span> | |
| 1222 | {byAgent && ( | |
| 1223 | <Link to={`${base}/sessions/${pull.number}`} className="shrink-0 text-xs text-muted hover:text-fg"> | |
| 1224 | Session | |
| 1225 | </Link> | |
| 1226 | )} | |
| Projects: what a workspace builds and runs, first on every page | 1227 | </li> |
| 1228 | ); | |
| 1229 | })} | |
| 1230 | </ul> | |
| 1231 | )} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1232 | </Panel> |
| Projects: what a workspace builds and runs, first on every page | 1233 | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1234 | {loaderData.commits.length > 0 && ( |
| 1235 | <Panel | |
| 1236 | title={`Latest on ${source?.defaultBranch ?? "main"}`} | |
| 1237 | icon={<GitCommitHorizontal size={14} />} | |
| 1238 | all={{ to: `${base}/commits`, label: "All commits" }} | |
| 1239 | > | |
| 1240 | <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface"> | |
| 1241 | {loaderData.commits.map((one) => ( | |
| 1242 | <li key={one.hash} className="flex items-center gap-3 px-4 py-2.5 text-sm"> | |
| Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016) | 1243 | <CommitAvatars commit={one} size={18} max={2} /> |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1244 | <span className="min-w-0 grow"> |
| 1245 | <Link to={`${base}/commit/${one.hash}`} className="block truncate hover:text-accent"> | |
| 1246 | {one.message} | |
| 1247 | </Link> | |
| Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016) | 1248 | <span className="text-xs text-muted"> |
| 1249 | <CommitNames commit={one} className="hover:text-fg" /> | |
| 1250 | </span> | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1251 | </span> |
| 1252 | <Link to={`${base}/commit/${one.hash}`} className="hidden shrink-0 font-mono text-xs text-faint hover:text-fg sm:block"> | |
| 1253 | {one.hash.slice(0, 7)} | |
| 1254 | </Link> | |
| 1255 | <span className="w-16 shrink-0 text-right text-xs text-faint"> | |
| 1256 | <TimeAgo at={one.at} /> | |
| 1257 | </span> | |
| 1258 | </li> | |
| 1259 | ))} | |
| 1260 | </ul> | |
| 1261 | </Panel> | |
| 1262 | )} | |
| 1263 | ||
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1264 | <Panel title="Activity" icon={<ActivityIcon size={14} />}> |
| 1265 | {loaderData.eventsLoaded ? ( | |
| 1266 | <ActivityFeed groups={groups} showRepo={false} limit={8} empty="Nothing has happened here yet." /> | |
| 1267 | ) : ( | |
| 1268 | <Unavailable what="Activity" /> | |
| 1269 | )} | |
| 1270 | </Panel> | |
| 1271 | ||
| 1272 | {previews.length > 0 && ( | |
| 1273 | <Panel title="Previews" icon={<GitBranch size={14} />} count={previews.length} all={{ to: `${base}/deployments`, label: "Deployments" }}> | |
| 1274 | <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface"> | |
| 1275 | {previews.map((app) => ( | |
| 1276 | <li key={app.url} className="flex items-center gap-3 px-4 py-2.5 text-sm"> | |
| 1277 | <span className="min-w-0 grow"> | |
| Merge branch 'site-fixes' into fast-push | 1278 | <DeployLink href={app.url} className="block truncate font-mono text-[0.8125rem] hover:text-accent"> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1279 | {host(app.url)} |
| Merge branch 'site-fixes' into fast-push | 1280 | </DeployLink> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1281 | <span className="text-xs text-muted"> |
| 1282 | {app.branch} | |
| 1283 | {app.number != null && ( | |
| 1284 | <> | |
| 1285 | {" · "} | |
| 1286 | <Link to={`${base}/pull/${app.number}`} className="hover:underline"> | |
| 1287 | #{app.number} | |
| 1288 | </Link> | |
| 1289 | </> | |
| 1290 | )} | |
| 1291 | </span> | |
| Projects: what a workspace builds and runs, first on every page | 1292 | </span> |
| 1293 | <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 API | 1294 | <TimeAgo at={app.deployedAt} /> |
| Projects: what a workspace builds and runs, first on every page | 1295 | </span> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1296 | </li> |
| 1297 | ))} | |
| 1298 | </ul> | |
| 1299 | </Panel> | |
| 1300 | )} | |
| 1301 | </div> | |
| Projects: what a workspace builds and runs, first on every page | 1302 | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1303 | <aside className="space-y-4 text-sm"> |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1304 | {/* Deployments panel (deployments-panel.tsx): each environment's latest deployment. */} |
| 1305 | <DeploymentsPanel base={base} summary={loaderData.environments} className="rounded-xl border border-line bg-surface p-5" /> | |
| 1306 | {project && ( | |
| 1307 | <section className="rounded-xl border border-line bg-surface p-5"> | |
| 1308 | <div className="flex items-center justify-between"> | |
| 1309 | <h2 className="text-sm font-semibold">About</h2> | |
| 1310 | {canChange && <AboutEditor project={project} />} | |
| 1311 | </div> | |
| 1312 | <p className={`mt-2.5 text-sm ${project.description ? "text-fg-soft" : "text-faint"}`}> | |
| 1313 | {project.description ?? "No description."} | |
| 1314 | </p> | |
| 1315 | <LinkList links={project.links} className="mt-3" /> | |
| 1316 | <ul className="mt-4 space-y-2 text-xs text-muted"> | |
| 1317 | <li className="flex items-center gap-2"> | |
| 1318 | {project.kind === "library" || project.kind === "tool" ? ( | |
| 1319 | <PackageGlyph size={13} className="shrink-0 text-faint" /> | |
| 1320 | ) : project.kind === "docs" ? ( | |
| 1321 | <BookOpen size={13} className="shrink-0 text-faint" /> | |
| 1322 | ) : ( | |
| 1323 | <Rocket size={13} className="shrink-0 text-faint" /> | |
| 1324 | )} | |
| 1325 | <Hint label={project.kindReason.detail}> | |
| 1326 | <span>{kindLabel(project)}</span> | |
| 1327 | </Hint> | |
| 1328 | </li> | |
| 1329 | {loaderData.release && ( | |
| 1330 | <li className="flex items-center gap-2"> | |
| 1331 | <Tag size={13} className="shrink-0 text-faint" /> | |
| 1332 | <Link to={`${base}/tags`} className="hover:text-fg"> | |
| 1333 | <span className="font-mono text-fg-soft">{loaderData.release.name}</span> | |
| 1334 | {loaderData.release.at && ( | |
| 1335 | <span className="text-faint"> | |
| 1336 | {" · "} | |
| 1337 | <TimeAgo at={loaderData.release.at} /> | |
| 1338 | </span> | |
| 1339 | )} | |
| 1340 | </Link> | |
| 1341 | </li> | |
| 1342 | )} | |
| 1343 | {loaderData.languages && loaderData.languages.length > 0 && ( | |
| 1344 | <li className="flex items-center gap-2"> | |
| 1345 | <Code2 size={13} className="shrink-0 text-faint" /> | |
| 1346 | <span> | |
| 1347 | {loaderData.languages | |
| 1348 | .slice(0, 3) | |
| 1349 | .map((language) => `${language.name} ${Math.round(language.share * 100)}%`) | |
| 1350 | .join(" · ")} | |
| 1351 | </span> | |
| 1352 | </li> | |
| 1353 | )} | |
| 1354 | </ul> | |
| 1355 | {loaderData.contributors && loaderData.contributors.length > 0 && ( | |
| 1356 | <div className="mt-4 flex flex-wrap gap-1"> | |
| 1357 | {loaderData.contributors.slice(0, 12).map((person) => ( | |
| 1358 | <Hint key={person.username} label={person.username}> | |
| 1359 | <Link to={`/${person.username}`}> | |
| 1360 | <Avatar name={person.username} size={22} /> | |
| 1361 | </Link> | |
| 1362 | </Hint> | |
| 1363 | ))} | |
| 1364 | </div> | |
| 1365 | )} | |
| 1366 | </section> | |
| 1367 | )} | |
| 1368 | ||
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1369 | {member && ( |
| 1370 | <section className="rounded-xl border border-line bg-surface p-5"> | |
| 1371 | <div className="flex items-center justify-between"> | |
| 1372 | <h2 className="flex items-center gap-1.5 text-sm font-semibold"> | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 1373 | <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 API | 1374 | What agents know here |
| 1375 | </h2> | |
| 1376 | <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg"> | |
| 1377 | Memory | |
| 1378 | </Link> | |
| Projects: what a workspace builds and runs, first on every page | 1379 | </div> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1380 | {!loaderData.memoriesLoaded ? ( |
| 1381 | <p className="mt-3 text-xs text-muted">Memory could not be loaded just now.</p> | |
| 1382 | ) : knows.length === 0 ? ( | |
| 1383 | <p className="mt-3 text-xs leading-5 text-muted"> | |
| 1384 | Nothing yet. Agents note conventions, decisions and traps as they work, and every agent starting here reads them. | |
| 1385 | </p> | |
| 1386 | ) : ( | |
| 1387 | <ul className="mt-3 space-y-2.5"> | |
| 1388 | {knows.map((memory) => ( | |
| 1389 | <li key={memory.id} className="text-xs leading-5"> | |
| 1390 | <p className="line-clamp-3 text-fg-soft"> | |
| 1391 | {memory.pinned && <Pin size={11} className="mr-1 inline text-accent" />} | |
| Usage, Billing settings and prepaid AI credit; fixes from the UX audit | 1392 | <InlineMarkdown text={memory.text} /> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1393 | </p> |
| 1394 | <p className="text-faint"> | |
| 1395 | {memory.kind} · {memory.createdBy} · <TimeAgo at={memory.createdAt} /> | |
| 1396 | </p> | |
| 1397 | </li> | |
| 1398 | ))} | |
| 1399 | {loaderData.memoryCount > knows.length && ( | |
| 1400 | <li> | |
| 1401 | <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg"> | |
| 1402 | {loaderData.memoryCount - knows.length} more | |
| 1403 | </Link> | |
| 1404 | </li> | |
| 1405 | )} | |
| 1406 | </ul> | |
| 1407 | )} | |
| 1408 | </section> | |
| Projects: what a workspace builds and runs, first on every page | 1409 | )} |
| 1410 | ||
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1411 | <section className="rounded-xl border border-line bg-surface p-5"> |
| 1412 | <h2 className="flex items-center gap-1.5 text-sm font-semibold"> | |
| 1413 | <HeartPulse size={14} className="text-faint" /> | |
| 1414 | Health | |
| Project dependencies: addresses, preview stacks, Affects, and agents who know | 1415 | </h2> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1416 | <div className="mt-4 space-y-4 text-xs"> |
| 1417 | <div> | |
| 1418 | <div className="flex items-baseline justify-between"> | |
| 1419 | <span className="text-muted">Checks passing</span> | |
| 1420 | <span className="font-medium tabular-nums">{percent(health.passRate)}</span> | |
| 1421 | </div> | |
| 1422 | <div className="mt-1.5"> | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 1423 | <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 API | 1424 | </div> |
| 1425 | <p className="mt-1 text-faint"> | |
| 1426 | {health.checkRuns | |
| 1427 | ? `${health.checkRuns} recent runs · ${percent(health.firstPass.rate)} pass on the first try` | |
| 1428 | : "No checks have run recently."} | |
| 1429 | </p> | |
| 1430 | </div> | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 1431 | {member && (plan === "production" || builds.length > 0) && ( |
| Project dependencies: addresses, preview stacks, Affects, and agents who know | 1432 | <div> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1433 | <div className="flex items-baseline justify-between"> |
| 1434 | <span className="text-muted">Deploys</span> | |
| 1435 | <Link to={`${base}/deployments`} className="text-faint hover:text-fg"> | |
| 1436 | {builds.length ? `last ${Math.min(builds.length, 20)}` : "none yet"} | |
| 1437 | </Link> | |
| 1438 | </div> | |
| 1439 | <div className="mt-1.5"> | |
| 1440 | <DeployStrip builds={builds} base={base} /> | |
| 1441 | </div> | |
| Project dependencies: addresses, preview stacks, Affects, and agents who know | 1442 | </div> |
| 1443 | )} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1444 | <div> |
| 1445 | <div className="flex items-baseline justify-between"> | |
| 1446 | <span className="text-muted">Open issues by age</span> | |
| 1447 | <Link to={`${base}/issues`} className="font-medium tabular-nums hover:text-accent"> | |
| 1448 | {open.issues} | |
| 1449 | </Link> | |
| 1450 | </div> | |
| 1451 | {ages ? ( | |
| 1452 | <ul className="mt-2 space-y-1.5"> | |
| 1453 | {ages.map((bucket) => ( | |
| 1454 | <li key={bucket.label} className="grid grid-cols-[6.5rem_1fr_1.5rem] items-center gap-2"> | |
| 1455 | <span className="text-faint">{bucket.label}</span> | |
| 1456 | <span className="block h-1.5 overflow-hidden rounded-full bg-line"> | |
| 1457 | <span | |
| 1458 | className={`block h-full rounded-full ${bucket.label === "Older" ? "bg-warn" : "bg-fg-soft/60"}`} | |
| 1459 | style={{ width: `${(bucket.count / oldest) * 100}%` }} | |
| 1460 | /> | |
| 1461 | </span> | |
| 1462 | <span className="text-right tabular-nums text-muted">{bucket.count}</span> | |
| Project dependencies: addresses, preview stacks, Affects, and agents who know | 1463 | </li> |
| 1464 | ))} | |
| 1465 | </ul> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1466 | ) : ( |
| 1467 | <p className="mt-1 text-faint">Issues could not be loaded just now.</p> | |
| 1468 | )} | |
| 1469 | </div> | |
| Project dependencies: addresses, preview stacks, Affects, and agents who know | 1470 | </div> |
| Projects: what a workspace builds and runs, first on every page | 1471 | </section> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1472 | |
| 1473 | {source && ( | |
| 1474 | <section className="rounded-xl border border-line bg-surface p-5"> | |
| 1475 | <h2 className="text-sm font-semibold">Clone</h2> | |
| 1476 | <div className="mt-3"> | |
| Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md | 1477 | <CopyLine breakAtSlashes 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 API | 1478 | </div> |
| 1479 | </section> | |
| 1480 | )} | |
| 1481 | </aside> | |
| 1482 | </div> | |
| Projects: what a workspace builds and runs, first on every page | 1483 | </div> |
| 1484 | ); | |
| 1485 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.