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.
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 1 | /** |
| 2 | * Pieces of the teams pages: a team in a list, and its badges. | |
| 3 | */ | |
| 4 | import { EyeOff, UsersRound } from "lucide-react"; | |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 5 | import { Form, Link, useOutletContext } from "react-router"; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 6 | |
| 7 | import { type Team, teamHandle } from "@g1t/contracts"; | |
| 8 | ||
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 9 | import type { AgentTeams, PeopleAgent, TeamChange } from "../lib/people"; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 10 | import { teamCounts, teamPath } from "../lib/teams"; |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 11 | import { ErrorText, SubmitButton } from "./ui"; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 12 | import { Badge } from "./ui/badge"; |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 13 | import { SelectField } from "./ui/select"; |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 14 | import { TeamKindMark } from "./people"; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 15 | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 16 | /** |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 17 | * What each page of a team gets from the team's layout: the team and the |
| 18 | * agents on it, members as its people are. | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 19 | */ |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 20 | export type TeamContext = { team: Team; agents: PeopleAgent[] }; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 21 | |
| 22 | /** The team whose page this is. */ | |
| 23 | export function useTeam(): Team { | |
| 24 | return useOutletContext<TeamContext>().team; | |
| 25 | } | |
| 26 | ||
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 27 | /** The agents on the team whose page this is. */ |
| 28 | export function useTeamAgents(): { agents: PeopleAgent[] } { | |
| 29 | const { agents } = useOutletContext<TeamContext>(); | |
| 30 | return { agents }; | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 31 | } |
| 32 | ||
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 33 | /** Secret, and the viewer's place in the team. */ |
| 34 | export function TeamBadges({ team }: { team: Pick<Team, "visibility" | "viewer_role"> }) { | |
| 35 | return ( | |
| 36 | <> | |
| 37 | {team.visibility === "secret" && ( | |
| 38 | <Badge> | |
| 39 | <EyeOff size={10} /> | |
| 40 | Secret | |
| 41 | </Badge> | |
| 42 | )} | |
| 43 | {team.viewer_role === "maintainer" && <Badge tone="accent">Maintainer</Badge>} | |
| 44 | {team.viewer_role === "member" && <Badge>Member</Badge>} | |
| 45 | </> | |
| 46 | ); | |
| 47 | } | |
| 48 | ||
| 49 | /** One team in a list: its name, handle, what it is for, and how big it is. */ | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 50 | export function TeamRow({ team, agents, children }: { team: Team; agents?: number; children?: React.ReactNode }) { |
| 51 | const agentsHere = agents ?? team.agents_count ?? 0; | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 52 | return ( |
| 53 | <li className="flex flex-wrap items-start gap-3 px-4 py-3 transition-colors hover:bg-surface/60 sm:flex-nowrap"> | |
| 54 | <span className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md bg-raised text-muted ring-1 ring-line"> | |
| 55 | <UsersRound size={14} /> | |
| 56 | </span> | |
| 57 | <div className="min-w-0 grow basis-48"> | |
| 58 | <div className="flex flex-wrap items-center gap-x-2 gap-y-1"> | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 59 | <TeamKindMark people={team.members_count} agents={agentsHere} /> |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 60 | <Link to={teamPath(team.workspace, team.slug)} prefetch="intent" className="font-medium hover:underline"> |
| 61 | {team.name} | |
| 62 | </Link> | |
| 63 | <span className="font-mono text-xs text-faint">{teamHandle(team)}</span> | |
| 64 | <TeamBadges team={team} /> | |
| 65 | </div> | |
| 66 | {team.description && <p className="mt-0.5 line-clamp-2 text-sm text-muted">{team.description}</p>} | |
| 67 | <p className="mt-1 text-xs text-faint"> | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 68 | {teamCounts(team, agentsHere)} |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 69 | {team.parent && ( |
| 70 | <> | |
| 71 | {" · in "} | |
| 72 | <Link to={teamPath(team.workspace, team.parent.slug)} className="hover:text-fg hover:underline"> | |
| 73 | {team.parent.name} | |
| 74 | </Link> | |
| 75 | </> | |
| 76 | )} | |
| 77 | </p> | |
| 78 | </div> | |
| 79 | {children} | |
| 80 | </li> | |
| 81 | ); | |
| 82 | } | |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 83 | |
| 84 | /** | |
| 85 | * An agent's teams on its profile, as a person's are: each team it is on, | |
| 86 | * with Remove where the viewer manages the team, and Add to a team. Posts | |
| 87 | * `join-team` and `leave-team` to the page's action (lib/agent-teams.server.ts). | |
| 88 | */ | |
| 89 | export function AgentTeamsEditor({ | |
| 90 | slug, | |
| 91 | name, | |
| 92 | teams, | |
| 93 | personal = false, | |
| 94 | change, | |
| 95 | }: { | |
| 96 | slug: string; | |
| 97 | /** The agent's name, for the empty line. */ | |
| 98 | name: string; | |
| 99 | /** Null when its teams couldn't be read. */ | |
| 100 | teams: AgentTeams | null; | |
| 101 | /** A personal agent is on no team. */ | |
| 102 | personal?: boolean; | |
| 103 | /** The last change's answer, for its error. */ | |
| 104 | change?: TeamChange | null; | |
| 105 | }) { | |
| 106 | if (personal) return <p className="text-sm text-faint">A personal agent is on no team. Once an owner promotes it, add it to teams like anyone.</p>; | |
| 107 | if (!teams) return <p className="text-sm text-faint">Its teams couldn't be read just now.</p>; | |
| 108 | const error = (team: string) => (change?.team === team && change.intent === "leave-team" ? change.error : null); | |
| 109 | return ( | |
| 110 | <div className="space-y-3"> | |
| 111 | {teams.on.length ? ( | |
| 112 | <ul className="space-y-1 text-sm"> | |
| 113 | {teams.on.map((team) => ( | |
| 114 | <li key={team.slug}> | |
| 115 | <div className="flex min-h-9 flex-wrap items-center gap-2"> | |
| 116 | <TeamKindMark people={team.people} agents={team.agents} /> | |
| 117 | <Link to={teamPath(slug, team.slug)} className="min-w-0 truncate font-medium hover:text-accent"> | |
| 118 | {team.name} | |
| 119 | </Link> | |
| 120 | {team.lead && <Badge tone="accent">Lead</Badge>} | |
| 121 | {team.can_manage && ( | |
| 122 | <Form method="post" className="ml-auto"> | |
| 123 | <input type="hidden" name="intent" value="leave-team" /> | |
| 124 | <input type="hidden" name="team" value={team.slug} /> | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 125 | <SubmitButton variant="outline" match={{ intent: "leave-team", team: team.slug }} pending="Removing…"> |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 126 | Remove |
| 127 | </SubmitButton> | |
| 128 | </Form> | |
| 129 | )} | |
| 130 | </div> | |
| 131 | <ErrorText>{error(team.slug)}</ErrorText> | |
| 132 | </li> | |
| 133 | ))} | |
| 134 | </ul> | |
| 135 | ) : ( | |
| 136 | <p className="text-sm text-faint">{name} isn't on a team yet.</p> | |
| 137 | )} | |
| 138 | {teams.addable.length > 0 && ( | |
| 139 | <Form method="post" key={`teams:${teams.on.length}`} className="flex flex-col gap-2 sm:flex-row sm:items-center"> | |
| 140 | <input type="hidden" name="intent" value="join-team" /> | |
| 141 | <SelectField | |
| 142 | name="team" | |
| 143 | aria-label="Add to a team" | |
| 144 | defaultValue={teams.addable[0]!.slug} | |
| 145 | className="w-full sm:w-56" | |
| 146 | options={teams.addable.map((team) => ({ value: team.slug, label: team.name }))} | |
| 147 | /> | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 148 | <SubmitButton variant="outline" match={{ intent: "join-team" }} pending="Adding…"> |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 149 | Add to team |
| 150 | </SubmitButton> | |
| 151 | </Form> | |
| 152 | )} | |
| 153 | {change?.intent === "join-team" && <ErrorText>{change.error}</ErrorText>} | |
| 154 | </div> | |
| 155 | ); | |
| 156 | } |