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
403 linesCodeBlameRaw
1import type { Provider } from "./integrations";
2
3/**
4 * The connector catalog: every tool g1t connects to, or will, in one list.
5 * The workspace's Integrations page and your own Integrations settings are
6 * both drawn from it, so a new connector is added here, once.
7 *
8 * A connector is `available` when g1t can set it up today (its `href` is
9 * where), and `soon` while it is planned. `scopes` says who connects it:
10 *
11 * - `workspace`: an owner connects it once, for everyone in the workspace.
12 * - `personal`: each person connects their own account, and it follows them
13 * into every workspace they belong to (a calendar, a personal sign-in).
14 *
15 * A connector with both scopes can say something different for each, in
16 * `personal`: its own description, status, capabilities or setup page.
17 * `connectorView` resolves that into what one context shows.
18 *
19 * Paths in `href` are g1t's own pages. `:workspace` stands for the
20 * workspace's slug.
21 */
22
23export type ConnectorScope = "workspace" | "personal";
24export type ConnectorStatus = "available" | "soon";
25
26export type ConnectorCategory =
27 | "code"
28 | "issues"
29 | "chat"
30 | "calendar"
31 | "docs"
32 | "monitoring"
33 | "cloud"
34 | "ai"
35 | "data"
36 | "crm"
37 | "design"
38 | "security"
39 | "files";
40
41/** What a connector lets g1t do, as the small tags on its card. */
42export type ConnectorCapability =
43 | "Agents can read"
44 | "Agents can act"
45 | "Opens issues"
46 | "Writes back"
47 | "Imports repositories"
48 | "Signs you in"
49 | "Sends events"
50 | "Alerts into chat"
51 | "Sets your status"
52 | "AI provider"
53 | "Deploys"
54 | "Syncs people";
55
56export type Connector = {
57 /** Stable and unique: used in links and requests. */
58 id: string;
59 name: string;
60 category: ConnectorCategory;
61 /** One plain line of what it does in g1t, for a workspace (or for you, when it is only personal). */
62 description: string;
63 scopes: ConnectorScope[];
64 status: ConnectorStatus;
65 /** Where an available connector is set up, for each scope it has. */
66 href?: Partial<Record<ConnectorScope, string>>;
67 capabilities?: ConnectorCapability[];
68 /** The integration provider behind it, whose connections show it as connected. */
69 provider?: Provider;
70 /** What differs when you connect it for yourself. */
71 personal?: { description?: string; status?: ConnectorStatus; capabilities?: ConnectorCapability[] };
72 /** More words search should find it by. */
73 keywords?: string[];
74};
75
76/** A connector as one context (a workspace, or you) shows it. */
77export type ConnectorView = {
78 id: string;
79 name: string;
80 category: ConnectorCategory;
81 description: string;
82 status: ConnectorStatus;
83 href: string | null;
84 capabilities: ConnectorCapability[];
85 provider: Provider | null;
86 keywords: string[];
87 /** The other scope it can also be connected in. */
88 alsoIn: ConnectorScope | null;
89};
90
91/** The categories, in the order the directory lists them. */
92export const CONNECTOR_CATEGORIES: { id: ConnectorCategory; title: string }[] = [
93 { id: "code", title: "Code & CI" },
94 { id: "issues", title: "Issues & projects" },
95 { id: "chat", title: "Chat & meetings" },
96 { id: "calendar", title: "Calendar & email" },
97 { id: "docs", title: "Docs & knowledge" },
98 { id: "monitoring", title: "Monitoring & incidents" },
99 { id: "cloud", title: "Cloud & deploy" },
100 { id: "ai", title: "AI models" },
101 { id: "data", title: "Data & analytics" },
102 { id: "crm", title: "CRM & support" },
103 { id: "design", title: "Design" },
104 { id: "security", title: "Security & identity" },
105 { id: "files", title: "Files & storage" },
106];
107
108const W: ConnectorScope[] = ["workspace"];
109const P: ConnectorScope[] = ["personal"];
110const BOTH: ConnectorScope[] = ["workspace", "personal"];
111
112/** An AI provider: its credentials are kept on the workspace's AI providers page. */
113function model(provider: Provider, name: string, description: string, keywords: string[] = []): Connector {
114 return {
115 id: provider.replace(/_/g, "-"),
116 name,
117 category: "ai",
118 description,
119 scopes: W,
120 status: "available",
121 href: { workspace: `/:workspace/-/integrations/models?add=${provider}#add` },
122 capabilities: ["AI provider"],
123 provider,
124 keywords: ["model", "llm", ...keywords],
125 };
126}
127
128/** A planned connector. */
129function soon(
130 id: string,
131 name: string,
132 category: ConnectorCategory,
133 scopes: ConnectorScope[],
134 description: string,
135 capabilities: ConnectorCapability[] = [],
136 personal?: Connector["personal"],
137): Connector {
138 return { id, name, category, description, scopes, status: "soon", capabilities, ...(personal ? { personal } : {}) };
139}
140
141export const CONNECTORS: Connector[] = [
142 // Code & CI -------------------------------------------------------------
143 {
144 id: "github",
145 name: "GitHub",
146 category: "code",
147 description: "Import repositories, with their branches, tags and issues, through g1t's GitHub App.",
148 scopes: BOTH,
149 status: "available",
150 href: { workspace: "/new/github?workspace=:workspace", personal: "/settings/github" },
151 capabilities: ["Imports repositories"],
152 personal: {
153 description: "Sign in with your GitHub account, and reach the repositories you can see there to import them.",
154 capabilities: ["Signs you in", "Imports repositories"],
155 },
156 keywords: ["git", "import", "sign in"],
157 },
158 {
159 id: "webhooks",
160 name: "Webhooks",
161 category: "code",
162 description: "Every repository's events, sent to your own addresses as they happen, signed.",
163 scopes: W,
164 status: "available",
165 href: { workspace: "/:workspace/-/webhooks" },
166 capabilities: ["Sends events"],
167 keywords: ["http", "events", "outgoing"],
168 },
169 soon("gitlab", "GitLab", "code", BOTH, "Import projects, with their branches, tags and issues.", ["Imports repositories"], {
170 description: "Sign in with GitLab, and reach your own projects to import them.",
171 capabilities: ["Signs you in", "Imports repositories"],
172 }),
173 soon("bitbucket", "Bitbucket", "code", BOTH, "Import repositories, with their branches and tags.", ["Imports repositories"], {
174 description: "Reach your own Bitbucket repositories to import them.",
175 capabilities: ["Imports repositories"],
176 }),
177 soon("circleci", "CircleCI", "code", W, "Pipeline results as checks on pull requests, which agents read when a build fails.", ["Agents can read"]),
178 soon("buildkite", "Buildkite", "code", W, "Build results as checks on pull requests, with the failing step for agents to fix.", ["Agents can read"]),
179 soon("docker-hub", "Docker Hub", "code", W, "Push images your workflows build, and pull private base images.", ["Deploys"]),
180
181 // Issues & projects -----------------------------------------------------
182 {
183 id: "linear",
184 name: "Linear",
185 category: "issues",
186 description: "Agents read ENG-42 when work mentions it; import issues, and they hear back when the work lands.",
187 scopes: BOTH,
188 status: "available",
189 href: { workspace: "/:workspace/-/integrations/trackers?add=linear#add" },
190 capabilities: ["Agents can read", "Writes back"],
191 provider: "linear",
192 personal: {
193 description: "Issues assigned to you in Linear, in your g1t inbox beside everything else.",
194 status: "soon",
195 capabilities: ["Agents can read"],
196 },
197 keywords: ["tracker", "tickets"],
198 },
199 {
200 id: "jira",
201 name: "Jira",
202 category: "issues",
203 description: "Agents read TECH-1234 when work mentions it; import tickets, and they hear back when the work lands.",
204 scopes: W,
205 status: "available",
206 href: { workspace: "/:workspace/-/integrations/trackers?add=jira#add" },
207 capabilities: ["Agents can read", "Writes back"],
208 provider: "jira",
209 keywords: ["atlassian", "tracker", "tickets"],
210 },
211 soon("shortcut", "Shortcut", "issues", W, "Stories agents read when work mentions them, updated when the work lands.", ["Agents can read", "Writes back"]),
212 soon("clickup", "ClickUp", "issues", W, "Tasks agents can read and import, kept in step with their pull requests.", ["Agents can read", "Writes back"]),
213 soon("trello", "Trello", "issues", W, "Cards become issues, and move along their board as the work does.", ["Opens issues", "Writes back"]),
214
215 // Chat & meetings -------------------------------------------------------
216 soon("slack", "Slack", "chat", BOTH, "Alerts and updates in the channels your team already reads.", ["Alerts into chat"], {
217 description: "Your status in g1t follows the one you set in Slack, and the other way round.",
218 capabilities: ["Sets your status"],
219 }),
220 soon("microsoft-teams", "Microsoft Teams", "chat", BOTH, "Alerts and updates posted to Teams channels.", ["Alerts into chat"], {
221 description: "Your status in g1t follows your Teams presence.",
222 capabilities: ["Sets your status"],
223 }),
224 soon("discord", "Discord", "chat", W, "Releases, alerts and updates posted to a Discord server.", ["Alerts into chat"]),
225 soon("zoom", "Zoom", "chat", BOTH, "Meeting notes and recordings agents can read when work refers to them.", ["Agents can read"], {
226 description: "Sets your status to On a call while you are in a Zoom meeting.",
227 capabilities: ["Sets your status"],
228 }),
229 soon("google-meet", "Google Meet", "chat", P, "Sets your status to On a call while you are in a meeting, and starts a call from any channel.", ["Sets your status"]),
230
231 // Calendar & email ------------------------------------------------------
232 soon("google-calendar", "Google Calendar", "calendar", P, "Sets your status to In a meeting while you are in one.", ["Sets your status"]),
233 soon("outlook-calendar", "Outlook Calendar", "calendar", P, "Sets your status to In a meeting while you are in one.", ["Sets your status"]),
234 soon("gmail", "Gmail", "calendar", P, "Turn an email into an issue, from your own inbox.", ["Opens issues"]),
235 soon("outlook-mail", "Outlook Mail", "calendar", P, "Turn an email into an issue, from your own inbox.", ["Opens issues"]),
236
237 // Docs & knowledge ------------------------------------------------------
238 soon("google-docs", "Google Docs", "docs", BOTH, "Agents read the specs and plans linked from issues and chat.", ["Agents can read"], {
239 description: "Docs you share in chat open for agents as well as people.",
240 capabilities: ["Agents can read"],
241 }),
242 soon("coda", "Coda", "docs", W, "Agents read the docs and tables linked from issues and chat.", ["Agents can read"]),
243
244 // Monitoring & incidents ------------------------------------------------
245 {
246 id: "sentry",
247 name: "Sentry",
248 category: "monitoring",
249 description: "New errors open issues with the stack trace, and resolve in Sentry when the fix merges.",
250 scopes: W,
251 status: "available",
252 href: { workspace: "/:workspace/-/integrations/alerts?add=sentry#add" },
253 capabilities: ["Opens issues", "Agents can read", "Writes back"],
254 provider: "sentry",
255 keywords: ["errors", "alerts", "exceptions"],
256 },
257 {
258 id: "datadog",
259 name: "Datadog",
260 category: "monitoring",
261 description: "Monitors that trigger open issues, and recoveries are noted on them.",
262 scopes: W,
263 status: "available",
264 href: { workspace: "/:workspace/-/integrations/alerts?add=datadog#add" },
265 capabilities: ["Opens issues"],
266 provider: "datadog",
267 keywords: ["monitors", "alerts", "apm"],
268 },
269 {
270 id: "alerts-webhook",
271 name: "Alerts webhook",
272 category: "monitoring",
273 description: "Anything that can send signed JSON opens issues: your own scripts, or any monitoring tool.",
274 scopes: W,
275 status: "available",
276 href: { workspace: "/:workspace/-/integrations/alerts?add=webhook#add" },
277 capabilities: ["Opens issues"],
278 provider: "webhook",
279 keywords: ["incoming", "http", "alerts", "pagerduty", "grafana"],
280 },
281 soon("pagerduty", "PagerDuty", "monitoring", BOTH, "Incidents open issues, and resolve when the fix lands.", ["Opens issues", "Writes back"], {
282 description: "Sets your status to On call while you are on call.",
283 capabilities: ["Sets your status"],
284 }),
285 soon("opsgenie", "Opsgenie", "monitoring", W, "Alerts open issues, and close when the fix lands.", ["Opens issues", "Writes back"]),
286 soon("incident-io", "incident.io", "monitoring", W, "Incidents get a channel in g1t, with what changed alongside.", ["Opens issues", "Alerts into chat"]),
287 soon("grafana", "Grafana", "monitoring", W, "Alert rules open issues, and agents read the dashboards they link to.", ["Opens issues", "Agents can read"]),
288 soon("new-relic", "New Relic", "monitoring", W, "Alerts open issues, with the traces agents need to find the cause.", ["Opens issues", "Agents can read"]),
289 soon("honeycomb", "Honeycomb", "monitoring", W, "Triggers open issues, and agents query traces while they work.", ["Opens issues", "Agents can read"]),
290 soon("better-stack", "Better Stack", "monitoring", W, "Uptime incidents and log alerts open issues.", ["Opens issues"]),
291
292 // Cloud & deploy --------------------------------------------------------
293 soon("aws", "Amazon Web Services", "cloud", W, "Workflows and deployments reach AWS with short-lived credentials, no stored keys.", ["Deploys"]),
294 soon("google-cloud", "Google Cloud", "cloud", W, "Workflows and deployments reach Google Cloud with short-lived credentials.", ["Deploys"]),
295 soon("azure", "Microsoft Azure", "cloud", W, "Workflows and deployments reach Azure with short-lived credentials.", ["Deploys"]),
296 soon("netlify", "Netlify", "cloud", W, "Deploy previews as checks on pull requests.", ["Deploys", "Agents can read"]),
297 soon("fly-io", "Fly.io", "cloud", W, "Deploy apps from workflows, and roll back from the deployment's page.", ["Deploys"]),
298 soon("kubernetes", "Kubernetes", "cloud", W, "Roll out to your own clusters from workflows, with each rollout's status on g1t.", ["Deploys"]),
299
300 // AI models -------------------------------------------------------------
301 model("anthropic", "Anthropic", "Keep your team's Anthropic API key, sealed, for the workspace.", ["claude"]),
302 model("openai", "OpenAI", "Keep your team's OpenAI API key, sealed, for the workspace.", ["gpt"]),
303 model("gemini", "Google Gemini", "Keep your team's Google AI Studio key, sealed, for the workspace.", ["google"]),
304 model("xai", "xAI", "Keep your team's xAI API key, sealed, for the workspace.", ["grok"]),
305 model("mistral", "Mistral", "Keep your team's Mistral API key, sealed, for the workspace."),
306 model("deepseek", "DeepSeek", "Keep your team's DeepSeek API key, sealed, for the workspace."),
307 model("azure_openai", "Azure OpenAI", "Keep your Azure OpenAI resource, deployment and key for the workspace.", ["microsoft"]),
308 model("openrouter", "OpenRouter", "Keep your team's OpenRouter key, sealed, for the workspace."),
309 model("groq", "Groq", "Keep your team's Groq API key, sealed, for the workspace."),
310 model("together", "Together AI", "Keep your team's Together AI key, sealed, for the workspace."),
311 model("fireworks", "Fireworks AI", "Keep your team's Fireworks AI key, sealed, for the workspace."),
312 model("cerebras", "Cerebras", "Keep your team's Cerebras API key, sealed, for the workspace."),
313 model("anthropic_endpoint", "Anthropic-compatible endpoint", "Any server that speaks the Anthropic API, such as your own gateway or proxy.", ["self-hosted", "proxy"]),
314 model("openai_endpoint", "OpenAI-compatible endpoint", "Any server that speaks the OpenAI API, such as a model you host yourself.", ["self-hosted", "proxy", "ollama", "vllm"]),
315 {
316 id: "mcp",
317 name: "MCP clients",
318 category: "ai",
319 description: "Your own agent, connected to g1t's MCP server, works in g1t as you.",
320 scopes: P,
321 status: "available",
322 href: { personal: "/settings/applications" },
323 capabilities: ["Agents can act"],
324 keywords: ["model context protocol", "agent", "applications", "oauth"],
325 },
326 soon("amazon-bedrock", "Amazon Bedrock", "ai", W, "Models in your own AWS account, billed there.", ["AI provider"]),
327 soon("vertex-ai", "Google Vertex AI", "ai", W, "Models in your own Google Cloud project, billed there.", ["AI provider"]),
328
329 // Data & analytics ------------------------------------------------------
330 soon("postgresql", "PostgreSQL", "data", W, "Agents read your schema, and run read-only queries while they investigate.", ["Agents can read"]),
331 soon("snowflake", "Snowflake", "data", W, "Agents answer questions from your warehouse with read-only queries.", ["Agents can read"]),
332 soon("bigquery", "BigQuery", "data", W, "Agents answer questions from your datasets with read-only queries.", ["Agents can read"]),
333 soon("amplitude", "Amplitude", "data", W, "Agents read the charts that work refers to.", ["Agents can read"]),
334 soon("segment", "Segment", "data", W, "Agents read your tracking plan, and check new events against it.", ["Agents can read"]),
335
336 // CRM & support ---------------------------------------------------------
337 soon("zendesk", "Zendesk", "crm", W, "Turn a ticket into an issue, and tell the customer when the fix ships.", ["Opens issues", "Writes back"]),
338 soon("salesforce", "Salesforce", "crm", W, "Agents read the account and case behind a request.", ["Agents can read"]),
339
340 // Design ----------------------------------------------------------------
341 soon("miro", "Miro", "design", W, "Agents read the boards linked from issues and chat.", ["Agents can read"]),
342 soon("storybook", "Storybook", "design", W, "Each pull request's stories as a check, with what changed.", ["Agents can read"]),
343
344 // Security & identity ---------------------------------------------------
345 soon("okta", "Okta", "security", W, "Single sign-on, and people added and removed with your directory.", ["Signs you in", "Syncs people"]),
346 soon("entra-id", "Microsoft Entra ID", "security", W, "Single sign-on, and people added and removed with your directory.", ["Signs you in", "Syncs people"]),
347 soon("google-workspace", "Google Workspace", "security", BOTH, "Single sign-on, and people added and removed with your directory.", ["Signs you in", "Syncs people"], {
348 description: "Sign in to g1t with your Google account.",
349 capabilities: ["Signs you in"],
350 }),
351 soon("1password", "1Password", "security", W, "Secrets from a vault, read by workflows and deployments when they run.", ["Deploys"]),
352 soon("snyk", "Snyk", "security", W, "Findings open issues, and an agent opens the fix.", ["Opens issues"]),
353
354 // Files & storage -------------------------------------------------------
355 soon("google-drive", "Google Drive", "files", BOTH, "Agents read the files linked from issues and chat.", ["Agents can read"], {
356 description: "Attach files from your Drive in chat, without downloading them first.",
357 capabilities: ["Agents can read"],
358 }),
359 soon("dropbox", "Dropbox", "files", BOTH, "Agents read the files linked from issues and chat.", ["Agents can read"], {
360 description: "Attach files from your Dropbox in chat.",
361 capabilities: ["Agents can read"],
362 }),
363 soon("onedrive", "OneDrive and SharePoint", "files", BOTH, "Agents read the files linked from issues and chat.", ["Agents can read"], {
364 description: "Attach files from your OneDrive in chat.",
365 capabilities: ["Agents can read"],
366 }),
367 soon("box", "Box", "files", W, "Agents read the files linked from issues and chat.", ["Agents can read"]),
368];
369
370/** The connector by its id. */
371export function connectorById(id: string): Connector | undefined {
372 return CONNECTORS.find((connector) => connector.id === id);
373}
374
375/** A connector as `scope` shows it, or null when it cannot be connected there. */
376export function connectorView(connector: Connector, scope: ConnectorScope): ConnectorView | null {
377 if (!connector.scopes.includes(scope)) return null;
378 const own = scope === "personal" ? connector.personal : undefined;
379 const status = own?.status ?? connector.status;
380 const other: ConnectorScope = scope === "workspace" ? "personal" : "workspace";
381 return {
382 id: connector.id,
383 name: connector.name,
384 category: connector.category,
385 description: own?.description ?? connector.description,
386 status,
387 href: status === "available" ? (connector.href?.[scope] ?? null) : null,
388 capabilities: own?.capabilities ?? connector.capabilities ?? [],
389 provider: connector.provider ?? null,
390 keywords: connector.keywords ?? [],
391 alsoIn: connector.scopes.includes(other) ? other : null,
392 };
393}
394
395/** Every connector `scope` can show, in catalog order. */
396export function connectorsFor(scope: ConnectorScope): ConnectorView[] {
397 return CONNECTORS.map((connector) => connectorView(connector, scope)).filter((view): view is ConnectorView => view != null);
398}
399
400/** A setup path with its `:workspace` filled in. */
401export function connectorPath(path: string, workspace: string): string {
402 return path.replace(/:workspace\b/g, encodeURIComponent(workspace));
403}