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.

122 linesCodeBlameRaw
1import { Check, Copy, ExternalLink } from "lucide-react";
2import { useMemo, useState, useSyncExternalStore } from "react";
3import { useRouteLoaderData } from "react-router";
4
5import type { loader as rootLoader } from "../root";
6
7import { AGENT_IDS, type AgentId, DEFAULT_AGENT, agentsFor, cursorInstallLink, getAgentChoice, setAgentChoice, subscribeAgentChoice } from "../lib/agent-setup";
8import { useAddresses } from "../lib/addresses";
9import { SelectField } from "./ui/select";
10import { Button } from "./ui/button";
11import { Card } from "./ui/card";
12
13/** The agent this browser chose last; every toggle on the page shares it. */
14export function useAgentChoice(): [AgentId, (id: AgentId) => void] {
15 const root = useRouteLoaderData<typeof rootLoader>("root");
16 const fallback = root?.agentDefault ?? DEFAULT_AGENT;
17 const id = useSyncExternalStore(
18 subscribeAgentChoice,
19 () => getAgentChoice(fallback),
20 () => root?.agentChoice ?? fallback,
21 );
22 return [id, setAgentChoice];
23}
24
25/**
26 * How to connect g1t to a coding agent: a client picker, the command or
27 * config for the one chosen, and a copy button. Choosing an agent switches
28 * every block on the page and is remembered.
29 */
30export function AgentSetup({ hint = true, className }: { hint?: boolean; className?: string }) {
31 const [chosen, choose] = useAgentChoice();
32 const [copied, setCopied] = useState(false);
33 const { mcp } = useAddresses();
34 const agents = useMemo(() => agentsFor(mcp), [mcp]);
35 const agent = agents[chosen];
36
37 const copy = () => {
38 void navigator.clipboard?.writeText(agent.code).then(
39 () => {
40 setCopied(true);
41 setTimeout(() => setCopied(false), 1500);
42 },
43 () => undefined,
44 );
45 };
46
47 return (
48 <div className={className}>
49 <Card radius="lg" className="@container overflow-hidden">
50 <div className="flex items-center justify-between gap-3 border-b border-line px-3.5 py-2">
51 <span className="text-xs text-muted">Connect with</span>
52 <SelectField
53 aria-label="AI client"
54 value={chosen}
55 onValueChange={(id) => {
56 setCopied(false);
57 choose(id as AgentId);
58 }}
59 options={AGENT_IDS.map((id) => ({ value: id, label: agents[id].label }))}
60 size="sm"
61 className="w-44"
62 />
63 </div>
64 <div className="relative" role="region" aria-label={`${agent.label} setup`}>
65 {agent.file && <p className="px-3.5 pt-2.5 font-mono text-[0.6875rem] text-faint">{agent.file}</p>}
66 <pre className="overflow-x-auto py-2.5 pr-11 pl-3.5 font-mono text-[0.8125rem] leading-relaxed">
67 <code>
68 {agent.lang === "sh"
69 ? agent.code.split("\n").map((line, index) => (
70 <span key={index} className="block whitespace-pre-wrap wrap-anywhere">
71 <span className="mr-2 text-faint select-none">$</span>
72 {line}
73 </span>
74 ))
75 : agent.code}
76 </code>
77 </pre>
78 <Button
79 type="button"
80 aria-label={copied ? "Copied" : `Copy the ${agent.label} ${agent.file ? "config" : agent.lang === "text" ? "URL" : "command"}`}
81 onClick={copy}
82 variant="ghost"
83 size="inline"
84 className="absolute top-1.5 right-1.5 p-1.5 text-faint"
85 >
86 {copied ? <Check size={14} className="text-success" /> : <Copy size={14} />}
87 </Button>
88 </div>
89 </Card>
90 {hint && (
91 <p className="mt-2 text-xs leading-5 text-muted">
92 {agent.then.split("`").map((part, index) =>
93 index % 2 ? (
94 <code key={index} className="font-mono text-fg-soft">
95 {part}
96 </code>
97 ) : (
98 part
99 ),
100 )}
101 {chosen === "codex" && mcp === "https://mcp.g1t.sh" && (
102 <>
103 {" "}
104 <a href="https://docs.g1t.sh/guides/bring-your-own-agent/#codex-marketplace" className="text-fg underline underline-offset-4">
105 Install the Codex plugin
106 </a>{" "}
107 for work item and session workflows.
108 </>
109 )}
110 {chosen === "cursor" && (
111 <>
112 {" "}
113 <a href={cursorInstallLink(mcp)} className="inline-flex items-center gap-1 text-fg underline underline-offset-4">
114 Add to Cursor <ExternalLink size={11} />
115 </a>
116 </>
117 )}
118 </p>
119 )}
120 </div>
121 );
122}