Skip to content

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

g1t.sh
202 linesCodeBlameRaw

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

Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1/**
2 * Teams: groups of a workspace's members, given roles on repositories
3 * together, mentioned together and asked to review together. Kept by the
4 * identity service; mirrors `crates/contracts/src/teams.rs`.
5 */
6import type { RepoRole } from "./access";
7import type { User } from "./identity";
8import type { Result } from "./result";
9
10export type TeamVisibility = "visible" | "secret";
Merge branch 'worktree-agent-ad7c6d88d93adc817'11
12/**
13 * Who may create a workspace's teams: any member (the default) or its
14 * owners only. Owners change it in the workspace's settings.
15 */
16export type TeamCreation = "members" | "owners";
17
18/** Whether someone with `role` in a workspace may create its teams. */
19export function mayCreateTeams(setting: TeamCreation | null | undefined, role: "owner" | "member" | null | undefined): boolean {
20 if (!role) return false;
21 return (setting ?? "members") === "members" || role === "owner";
22}
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar23export type TeamRole = "member" | "maintainer";
24export type ReviewAlgorithm = "round_robin" | "load_balance";
25
26export const TEAM_VISIBILITY_LABELS: Record<TeamVisibility, string> = {
27 visible: "Visible",
28 secret: "Secret",
29};
30
31export const TEAM_VISIBILITY_SUMMARIES: Record<TeamVisibility, string> = {
32 visible: "Every member of the workspace can see it and mention it.",
33 secret: "Only its own people and the workspace's owners can see it. Secret teams cannot be nested.",
34};
35
36export const REVIEW_ALGORITHM_LABELS: Record<ReviewAlgorithm, string> = {
37 round_robin: "Round robin",
38 load_balance: "Load balance",
39};
40
41export const REVIEW_ALGORITHM_SUMMARIES: Record<ReviewAlgorithm, string> = {
42 round_robin: "Whoever this team asked least recently goes first.",
43 load_balance: "Whoever has the fewest pull requests waiting on their review goes first.",
44};
45
46/** The most people review assignment picks for one request. */
47export const MAX_ASSIGNED = 10;
48
49export type ReviewAssignment = {
50 enabled: boolean;
51 algorithm: ReviewAlgorithm;
52 count: number;
53 skip_busy: boolean;
54 busy_at: number;
55 include_child_teams: boolean;
56 excluded: string[];
57 notify_team: boolean;
58};
59
60export const DEFAULT_REVIEW_ASSIGNMENT: ReviewAssignment = {
61 enabled: false,
62 algorithm: "round_robin",
63 count: 1,
64 skip_busy: false,
65 busy_at: 5,
66 include_child_teams: false,
67 excluded: [],
68 notify_team: false,
69};
70
71export type TeamRef = { slug: string; name: string };
72
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.73/**
74 * Who leads a team: a person on it. `agent` is a lead from g1t's retired
75 * hosted agents, kept only so old data parses; identity never answers with
76 * one and no team can be given one.
77 */
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.78export type TeamLead =
79 | { kind: "user"; username: string; name: string | null; avatar: string | null }
80 | { kind: "agent"; agent_id: string };
81
82/** A team's chat channel: its id, and its name (without `#`) when it was chosen. */
83export type TeamChannel = { id: string; name: string };
84
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.85/** How `update_team` takes a lead: `@username`; `""` for none. */
86export function leadInput(lead: { username: string } | null): string {
87 return lead ? `@${lead.username}` : "";
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.88}
89
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar90export type Team = {
91 id: string;
92 workspace: string;
93 slug: string;
94 name: string;
95 description: string | null;
96 visibility: TeamVisibility;
97 parent: TeamRef | null;
98 notify: boolean;
99 review_assignment: ReviewAssignment;
100 members_count: number;
101 repos_count: number;
102 child_teams_count: number;
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.103 lead: TeamLead | null;
104 channel: TeamChannel | null;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar105 viewer_role: TeamRole | null;
106 can_manage: boolean;
107 created_at: string;
108 updated_at: string;
109};
110
111export type TeamMember = {
112 username: string;
113 name: string | null;
114 avatar: string | null;
115 role: TeamRole;
116 /** The child team they are in, when listed through one. */
117 via: string | null;
118};
119
120export type TeamRepo = {
121 /** `workspace/name`. */
122 repo: string;
123 repo_id: string;
124 role: RepoRole;
125 /** The parent team it comes from, when inherited. */
126 inherited_from: string | null;
127};
128
129/** A team with a role on a repository, as its Access settings list it. */
130export type RepoTeam = {
131 slug: string;
132 name: string;
133 role: RepoRole;
134 members_count: number;
135 visibility: TeamVisibility;
136};
137
138export type NewTeam = {
139 name: string;
140 slug?: string | null;
141 description?: string | null;
142 visibility?: TeamVisibility | null;
143 parent?: string | null;
144 notify?: boolean | null;
145 members?: string[];
146};
147
148/** What changes; the rest stays. `parent: ""` takes the team out from under its parent. */
149export type TeamChanges = {
150 name?: string;
151 slug?: string;
152 description?: string;
153 visibility?: TeamVisibility;
154 parent?: string;
155 notify?: boolean;
156 review_assignment?: ReviewAssignment;
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.157 /** `@username`, someone on the team (see `leadInput`); `""` for no lead. */
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.158 lead?: string;
159 /** The chat channel's id; `""` for none. Given with `channel_name`. */
160 channel_id?: string;
161 channel_name?: string;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar162};
163
164/** `@acme/backend`, the way a team is mentioned. */
165export function teamHandle(team: { workspace: string; slug: string }): string {
166 return `@${team.workspace}/${team.slug}`;
167}
168
169/** A team's slug from its name, as identity makes it. Null when nothing is left. */
170export function teamSlug(name: string): string | null {
171 let slug = "";
172 for (const c of name.trim()) {
173 if (/[A-Za-z0-9]/.test(c)) slug += c.toLowerCase();
174 else if (slug && !slug.endsWith("-")) slug += "-";
175 }
176 slug = slug.replace(/-+$/, "").slice(0, 60).replace(/-+$/, "");
177 return slug ? slug : null;
178}
179
180/** One person's teams in a workspace, for the Members page. */
181export type MemberTeams = { username: string; teams: TeamRef[] };
182
183/** Identity's team methods. */
184export interface TeamsClient {
185 listTeams(viewer: User | null, workspace: string, query?: string | null): Promise<Result<Team[]>>;
186 getTeam(viewer: User | null, workspace: string, team: string): Promise<Result<Team>>;
187 createTeam(actor: User, workspace: string, team: NewTeam): Promise<Result<Team>>;
Merge branch 'worktree-agent-ad7c6d88d93adc817'188 /** Who may create the workspace's teams. Owners only. */
189 setTeamCreation(actor: User, slug: string, setting: TeamCreation): Promise<Result<TeamCreation>>;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar190 updateTeam(actor: User, workspace: string, team: string, changes: TeamChanges): Promise<Result<Team>>;
191 deleteTeam(actor: User, workspace: string, team: string): Promise<Result<boolean>>;
192 teamMembers(viewer: User | null, workspace: string, team: string, includeChildTeams?: boolean): Promise<Result<TeamMember[]>>;
193 setTeamMember(actor: User, workspace: string, team: string, username: string, role: TeamRole): Promise<Result<TeamMember>>;
194 removeTeamMember(actor: User, workspace: string, team: string, username: string): Promise<Result<boolean>>;
195 childTeams(viewer: User | null, workspace: string, team: string): Promise<Result<Team[]>>;
196 teamRepos(viewer: User | null, workspace: string, team: string): Promise<Result<TeamRepo[]>>;
197 setTeamRepo(actor: User, workspace: string, team: string, owner: string, name: string, role: RepoRole): Promise<Result<TeamRepo>>;
198 removeTeamRepo(actor: User, workspace: string, team: string, owner: string, name: string): Promise<Result<boolean>>;
199 userTeams(viewer: User | null, workspace: string, username: string): Promise<Result<Team[]>>;
200 /** Each member's teams the viewer can see. Members only. */
201 teamMemberships(viewer: User | null, workspace: string): Promise<Result<MemberTeams[]>>;
202}

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