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