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
352 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.

The phone app: Chat in full with phone notifications, plus Code and Customize (#4)1/**
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.2 * Deprecated: g1t no longer runs agents of its own. Kept only until
3 * apps/mobile drops its Customize screens, which import it; delete it
4 * afterwards.
5 */
6/**
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r97 * Customize (docs.g1t.sh/guides/customize/): one place for what shapes
8 * how a workspace's agents work, in three parts, each with what the
9 * workspace has (**Yours**) and what it can add (**Discover**). It is the
10 * Marketplace's successor on the site and the phone.
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)11 *
12 * - **Skills**: what agents know how to do, in the open SKILL.md format
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r913 * (./skill-library.ts, ./skill-format.ts): g1t's foundational skills,
14 * the workspace's library, and every skill published in a marketplace.
15 * - **Connectors**: what agents can reach: MCP servers (./claude-plugin.ts
16 * `.mcp.json`, the directory in ./connectors.ts, and any address an
17 * owner adds), and g1t's own integrations (GitHub, trackers, alerts,
18 * model providers).
19 * - **Plugins**: folders in the open plugin format (./claude-plugin.ts),
20 * found in marketplaces, each bringing skills, agents and connectors to
21 * g1t's agents and everything else to Claude Code on a runner.
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)22 *
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r923 * Everything installable keeps the names the open ecosystem uses:
24 * `name@marketplace` for a plugin, `plugin:skill` for a skill inside one.
25 * Wire shapes are snake_case. No imports beyond types, so it runs
26 * anywhere: services, the site, the phone.
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)27 */
28
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r929import type { FetchPlan, MarketplaceSource, McpServerConfig, PluginLayout, SubagentFile } from "./claude-plugin";
30import type { ConnectorCategory } from "./connectors";
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)31import type { ListingKind } from "./marketplace";
32
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.33/** A tool an MCP server offers, as it describes it. */
34export type McpTool = {
35 name: string;
36 description: string;
37 /** `read` when the server says the tool only reads; `write` otherwise, which is also what an unknown tool is. */
38 kind: "read" | "write";
39 /** Its arguments, as the server describes them (JSON Schema). */
40 input_schema: Record<string, unknown>;
41};
42
43/** What a skill was attached to. */
44export type SkillScope = "agent" | "team" | "workspace";
45
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)46export type CustomizeSection = "skills" | "connectors" | "plugins";
47
48export const CUSTOMIZE_SECTIONS: readonly CustomizeSection[] = ["skills", "connectors", "plugins"];
49
50export type CustomizeSectionInfo = {
51 id: CustomizeSection;
52 title: string;
53 /** One line, for the section's row. */
54 description: string;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r955 /** Where the site shows it, with `:workspace` to fill in. */
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)56 sitePath: string;
57};
58
59export const CUSTOMIZE: Record<CustomizeSection, CustomizeSectionInfo> = {
60 skills: {
61 id: "skills",
62 title: "Skills",
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r963 description: "What agents know how to do: the skills every agent has, your workspace's library, and public skills to add.",
64 sitePath: "/:workspace/-/customize/skills",
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)65 },
66 connectors: {
67 id: "connectors",
68 title: "Connectors",
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r969 description: "What agents can reach: MCP servers and the tools your team already uses, connected once and given to agents.",
70 sitePath: "/:workspace/-/customize/connectors",
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)71 },
72 plugins: {
73 id: "plugins",
74 title: "Plugins",
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r975 description: "Skills, agents and connectors published together, from any marketplace in the open plugin format.",
76 sitePath: "/:workspace/-/customize/plugins",
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)77 },
78};
79
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r980/** The part of Customize that shows a listing of this kind. */
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)81export function sectionOfListing(kind: ListingKind): CustomizeSection {
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r982 if (kind === "integration" || kind === "connector") return "connectors";
83 if (kind === "skill") return "skills";
84 return "plugins";
85}
86
87/** Customize's pages, under `/<workspace>/-/customize`. */
88export function customizePath(section: CustomizeSection | "" | "requests" | "marketplaces", workspace: string, rest = ""): string {
89 const base = `/${encodeURIComponent(workspace)}/-/customize`;
90 return `${base}${section ? `/${section}` : ""}${rest ? `/${rest}` : ""}`;
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)91}
92
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r993// ── Marketplaces ─────────────────────────────────────────────────────────
94
95/** A marketplace a workspace reads plugins from, as its page lists it. */
96export type Marketplace = {
97 /** Its name, as its manifest says and as `name@marketplace` uses. */
98 name: string;
99 source: MarketplaceSource;
100 /** Built in: every workspace has it, and it can't be removed. */
101 builtin: boolean;
102 about: string | null;
103 owner: string | null;
104 /** How many plugins it lists, and how many of those carry skills. */
105 plugins: number;
106 /** RFC 3339: when its manifest was last read, and why it couldn't be, if so. */
107 fetched_at: string | null;
108 problem: string | null;
109 added_by: string | null;
110 added_at: string | null;
111 can_remove: boolean;
112};
113
114/** Whether a plugin from Discover can be installed here, now. */
115export type PluginAvailability =
116 /** Installable now. */
117 | { kind: "available" }
118 /** The workspace has it (switched off, if `enabled` is false). */
119 | { kind: "installed"; enabled: boolean }
120 /** Listed but not something g1t can fetch (npm, a command, a git host g1t doesn't read), with why. It still works in Claude Code. */
121 | { kind: "unavailable"; why: string }
122 /** g1t's own, announced but not released. */
123 | { kind: "soon" };
124
125/** What a plugin would bring, worked out from its layout, before or after installing. */
126export type PluginComponents = {
127 /** Skills by name (`plugin:name` to agents). */
128 skills: { name: string; description: string | null; path: string }[];
129 /** Subagents by name, as hireable starting points. */
130 agents: { name: string; description: string | null; path: string }[];
131 /** Commands: the older one-file form of a skill. */
132 commands: { name: string; path: string }[];
133 /** MCP servers it names: remote ones become connectors; stdio ones are for Claude Code only. */
134 mcp_servers: { name: string; type: McpServerConfig["type"]; url: string | null; remote: boolean }[];
135 /** What only Claude Code on a runner uses. */
136 hooks: boolean;
137 lsp: boolean;
138 bin: boolean;
139 output_styles: boolean;
140};
141
142/** One plugin as Discover lists it, from a marketplace. */
143export type PluginListing = {
144 /** `name@marketplace`. */
145 ref: string;
146 name: string;
147 marketplace: string;
148 display_name: string | null;
149 description: string;
150 version: string | null;
151 author: string | null;
152 homepage: string | null;
153 category: string | null;
154 tags: string[];
155 keywords: string[];
156 /** Where g1t would read it from, when it can. */
157 fetch: FetchPlan;
158 availability: PluginAvailability;
159 /** Skills the entry names outright (a skills-only plugin), so Discover can list them without fetching. */
160 skills: string[];
161};
162
163/** A plugin the workspace installed. */
164export type PluginInstall = {
165 id: string;
166 /** `name@marketplace`. */
167 ref: string;
168 name: string;
169 marketplace: string;
170 display_name: string | null;
171 description: string;
172 version: string | null;
173 /** The commit it was read at, when the source has one; the ref otherwise. */
174 sha: string | null;
175 fetch: FetchPlan;
176 components: PluginComponents;
177 /** What couldn't be read when it was installed. */
178 problems: string[];
179 enabled: boolean;
180 installed_by: string;
181 installed_at: string;
182 /** A newer version in the marketplace, when the listing moved on. */
183 update: { version: string | null; sha: string | null } | null;
184};
185
186/** A plugin's page: the listing, the install if any, and what it holds, read from its source. */
187export type PluginDetail = {
188 listing: PluginListing;
189 install: PluginInstall | null;
190 components: PluginComponents | null;
191 readme: string | null;
192 /** The plugin's manifest, when it has one. */
193 manifest: { name: string; version: string | null; license: string | null; repository: string | null } | null;
194 /** The subagents in full, for the hire flow. */
195 agents: SubagentFile[];
196 problems: string[];
197 /** The marketplace it is listed in, for the header. */
198 source: { marketplace: string; owner: string | null };
199};
200
201// ── Skills in Discover ───────────────────────────────────────────────────
202
203/** One skill as Discover lists it: a skill inside a marketplace's plugin. */
204export type SkillListing = {
205 /** `plugin:skill`. */
206 ref: string;
207 name: string;
208 plugin: string;
209 marketplace: string;
210 /** The plugin's description until the skill's own is read. */
211 description: string;
212 category: string | null;
213 author: string | null;
214 /** Whether the workspace's library already has it from this plugin. */
215 added: boolean;
216 /** Whether the plugin it is in can be fetched here. */
217 availability: PluginAvailability;
218};
219
220// ── Connectors ───────────────────────────────────────────────────────────
221
222/** How a connector signs in to its server. */
223export type ConnectorAuth = "none" | "header" | "oauth";
224
225/** Where a connector came from. */
226export type ConnectorFrom =
227 | { kind: "directory"; id: string }
228 | { kind: "plugin"; ref: string; server: string }
229 | { kind: "address" }
230 /** An MCP server kept on one agent before connectors existed. */
231 | { kind: "agent"; handle: string };
232
233/** Whether a connector can be used now. */
234export type ConnectorState = "connected" | "needs_sign_in" | "needs_key" | "problem";
235
236/** A workspace's connector: an MCP server connected once, then given to agents. */
237export type WorkspaceConnector = {
238 /** `con_…`. */
239 id: string;
240 /** 2 to 32 lowercase letters, digits and hyphens; tools are offered as `<name>__<tool>`. */
241 name: string;
242 /** For the page: the directory's name, or the name again. */
243 title: string;
244 url: string;
245 category: ConnectorCategory | null;
246 about: string | null;
247 auth: ConnectorAuth;
248 state: ConnectorState;
249 /** Why it is a problem, when it is. */
250 problem: string | null;
251 from: ConnectorFrom;
252 tools: McpTool[];
253 /** RFC 3339: when its tools were last listed. */
254 checked_at: string | null;
255 /** The header's name when `auth` is header (its value is never shown). */
256 header_name: string | null;
257 /** For OAuth: who it is signed in as, if the server said, and when the sign-in was done. */
258 signed_in_as: string | null;
259 signed_in_at: string | null;
260 grants: ConnectorGrant[];
261 added_by: string;
262 added_at: string;
263};
264
265/** Where a connector reaches: every agent, a team's, or one. */
266export type ConnectorGrant = {
267 id: string;
268 scope: SkillScope;
269 /** The agent's handle, the team's slug, or null for the workspace. */
270 target: string | null;
271 label: string;
272 granted_by: string;
273 granted_at: string;
274};
275
276/** What an owner sends to add a connector. */
277export type ConnectorInput = {
278 /** From the directory (its id), a plugin's server (`ref` and `server`), or by address. */
279 from: { kind: "directory"; id: string } | { kind: "plugin"; ref: string; server: string } | { kind: "address"; name: string; url: string };
280 auth: ConnectorAuth;
281 /** For `header`: the header's name and its value, kept sealed. */
282 header?: { name: string; value: string } | null;
283};
284
285/** Starting an OAuth sign-in for a connector: where the browser goes. */
286export type ConnectorSignIn = { url: string };
287
288// ── Agents and what reaches them ─────────────────────────────────────────
289
290/** A connector as one agent's Abilities tab lists it. */
291export type AgentConnector = {
292 id: string;
293 name: string;
294 title: string;
295 state: ConnectorState;
296 /** How the agent has it. */
297 via: SkillScope | "agent-only";
298 via_label: string;
299 tools: number;
300};
301
302// ── The bundle a runner gets ─────────────────────────────────────────────
303
304/**
305 * What a job's Claude Code receives from the workspace's Customize
306 * (docs.g1t.sh/guides/self-hosted-runners/, "What a run gets from
307 * Customize"): plugins to fetch at their pinned commit, the library's skills
308 * as files, and connectors with the headers that sign them in. Read with
309 * the job's agent token, once per job.
310 */
311export type CustomizeBundle = {
312 plugins: { ref: string; name: string; fetch: FetchPlan; version: string | null }[];
313 /** Library skills, each a folder: SKILL.md and its files, as `path` and content. */
314 skills: { name: string; plugin: string | null; files: { path: string; content: string; encoding: "utf8" | "base64" }[] }[];
315 /** Connectors the job's agent has, with what to send; secrets included, so this never leaves the runner. */
316 connectors: { name: string; url: string; headers: Record<string, string> }[];
317};
318
319/** Something to hire from a plugin's `agents/` folder, as a template. */
320export type PluginAgentTemplate = {
321 /** `plugin:agent`. */
322 ref: string;
323 plugin: string;
324 marketplace: string;
325 name: string;
326 description: string;
327 /** The system prompt, as the agent's instructions. */
328 prompt: string;
329 model: string | null;
330 skills: string[];
331 tools: string[];
332};
333
334/** The layout of a plugin turned into what it brings. */
335export function componentsOf(layout: PluginLayout, servers: McpServerConfig[], skillDescriptions: Record<string, string | null> = {}, agentDescriptions: Record<string, string | null> = {}): PluginComponents {
336 return {
337 skills: layout.skills.map((s) => ({ name: s.name, description: skillDescriptions[s.path] ?? null, path: s.path })),
338 agents: layout.agents.map((a) => ({ name: a.name, description: agentDescriptions[a.path] ?? null, path: a.path })),
339 commands: layout.commands.map((c) => ({ name: c.name, path: c.path })),
340 mcp_servers: servers.map((s) => ({ name: s.name, type: s.type, url: s.url, remote: s.type === "http" || s.type === "sse" })),
341 hooks: layout.hooks.length > 0,
342 lsp: layout.lsp,
343 bin: layout.bin,
344 output_styles: layout.outputStyles,
345 };
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)346}
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9347
348/** The most marketplaces a workspace adds, and the most plugins it installs. */
349export const MAX_MARKETPLACES = 20;
350export const MAX_PLUGIN_INSTALLS = 100;
351/** The most connectors a workspace keeps. */
352export const MAX_CONNECTORS = 50;

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