From e3e94118193ebc4b7367646b0a54f0c8f2fd3928 Mon Sep 17 00:00:00 2001 From: AyhamBD Date: Sat, 2 May 2026 19:16:04 +0300 Subject: [PATCH 1/3] feat: add VS Code and Antigravity MCP host setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both register through the existing agentWiring registry, so cmd_setup, cmd_doctor, and cmd_uninstall pick them up without changes. - VS Code: writes /Code/User/mcp.json under the "servers" top-level key (the only host using MCP-spec-native naming rather than "mcpServers"). Generalized mergeJSONMCPServer into mergeJSONMCPServerAt to parameterize the key; original wrapper is byte-identical for the other hosts. - Antigravity: writes ~/.gemini/antigravity/mcp_config.json under "mcpServers". The helper now also tolerates zero-byte stub config files Antigravity creates on first install (caught against a real install — json.Unmarshal of empty bytes errored). - Brand colors in setup output: VS Code blue (#007ACC) and a per-character rainbow gradient for Antigravity matching its product mark. - README/site/AGENTS.md/npm: enumerated the two new hosts in the supported-host list. Antigravity uses Google's official press-kit PNG, committed to site/antigravity-mark.png. Co-Authored-By: Claude Opus 4.7 --- AGENTS.md | 3 +- README.md | 2 + cmd/clawdchan/agents.go | 13 ++- cmd/clawdchan/colors.go | 75 ++++++++++++++- cmd/clawdchan/setup_agent_antigravity.go | 106 +++++++++++++++++++++ cmd/clawdchan/setup_agent_vscode.go | 112 +++++++++++++++++++++++ cmd/clawdchan/setup_agents_test.go | 92 +++++++++++++++++++ cmd/clawdchan/setup_helpers.go | 31 ++++--- npm/README.md | 2 +- site/antigravity-mark.png | Bin 0 -> 16849 bytes site/index.html | 2 + 11 files changed, 418 insertions(+), 20 deletions(-) create mode 100644 cmd/clawdchan/setup_agent_antigravity.go create mode 100644 cmd/clawdchan/setup_agent_vscode.go create mode 100644 site/antigravity-mark.png diff --git a/AGENTS.md b/AGENTS.md index 595b63e..d89097f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,7 +35,8 @@ and you **must** pause there — do not bypass prompts or run with it themselves** — you should not pipe input into it. Up front the user picks which MCP hosts to wire: Claude Code, - Gemini CLI, Codex CLI, GitHub Copilot CLI, Cursor, or none. + Gemini CLI, Codex CLI, GitHub Copilot CLI, Cursor, VS Code, + Antigravity, or none. OpenClaw is a separate yes/no step later in setup. If Claude Code is selected, there are two scope decisions that only the user can make: diff --git a/README.md b/README.md index b5a3bfd..afcd114 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Codex CLI GitHub Copilot CLI Cursor + VS Code + Antigravity OpenClaw

diff --git a/cmd/clawdchan/agents.go b/cmd/clawdchan/agents.go index a0728dd..5d316b9 100644 --- a/cmd/clawdchan/agents.go +++ b/cmd/clawdchan/agents.go @@ -58,6 +58,8 @@ func allAgents() []*agentWiring { codexAgent(), copilotAgent(), cursorAgent(), + vscodeAgent(), + antigravityAgent(), } } @@ -99,15 +101,18 @@ func describeScopeFlag(a *agentWiring, scope string) string { } // mcpScopeChoices returns the MCP-registration scope names the agent -// supports. Gemini and CC support user+project; Codex and Copilot are -// user-only (per their upstream docs). +// supports. Gemini and CC support user+project; the rest are user-only +// (per their upstream docs). func mcpScopeChoices(a *agentWiring) []string { switch a.key { case "cc", "gemini": return []string{"user", "project"} default: - // Codex, Copilot, and Cursor are user-scope only. Cursor specifically - // ignores project-level .cursor/mcp.json — only ~/.cursor/mcp.json loads. + // Codex, Copilot, Cursor, VS Code, and Antigravity are user-scope only. + // Cursor specifically ignores project-level .cursor/mcp.json — only + // ~/.cursor/mcp.json loads. VS Code and Antigravity also support a + // workspace file (.vscode/mcp.json, etc.) but registering at user + // scope means a single setup run covers every project the user opens. return []string{"user"} } } diff --git a/cmd/clawdchan/colors.go b/cmd/clawdchan/colors.go index 0591768..d1a4691 100644 --- a/cmd/clawdchan/colors.go +++ b/cmd/clawdchan/colors.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/mattn/go-isatty" ) @@ -106,10 +107,27 @@ const ( // documented for the in-tree OpenClaw host. Change when we adopt // a real brand color. openclawRed = "#E34234" + // vscodeBlue is the published "VS Code Blue" used in the product + // icon. Source: https://code.visualstudio.com/brand + vscodeBlue = "#007ACC" ) -// agentColor returns the brand hex for an agent key ("cc", "gemini", -// "codex", "copilot", "openclaw"). Returns "" for unknown keys. +// antigravityGradient is the per-character color sweep used for +// Google Antigravity. Antigravity's brand identity is a gradient +// (pink → purple → blue → cyan → mint), not a single hex; rendering +// it as an interpolated sweep across the display name is the +// closest the terminal can get to the actual product mark. +var antigravityGradient = []string{ + "#FF5A8A", // rose + "#A155F0", // purple + "#5B7FFF", // blue + "#00C8FF", // cyan + "#34F5C5", // mint +} + +// agentColor returns the brand hex for an agent key. Returns "" for +// unknown keys, or for keys whose brand identity is a gradient rather +// than a single hex (Antigravity — see antigravityGradient). func agentColor(key string) string { switch key { case "cc": @@ -122,17 +140,23 @@ func agentColor(key string) string { return copilotPurple case "openclaw": return openclawRed + case "vscode": + return vscodeBlue } return "" } // agentStyle wraps s in bold + the agent's brand color, using 24-bit // ANSI truecolor. Falls back to plain bold when color is disabled or -// the key is unknown. +// the key is unknown. Antigravity is a gradient, not a single color, +// so it routes through agentGradient. func agentStyle(key, s string) string { if !colorEnabled { return s } + if key == "antigravity" { + return agentGradient(s, antigravityGradient) + } h := agentColor(key) if h == "" { return bold(s) @@ -144,6 +168,51 @@ func agentStyle(key, s string) string { return fmt.Sprintf("\x1b[1;38;2;%d;%d;%dm%s\x1b[0m", r, g, b, s) } +// agentGradient renders s with a per-character color sweep across the +// supplied hex stops, linearly interpolated in RGB. Bold attribute is +// applied across the whole run so the gradient still reads as a label. +// Returns plain s when color is disabled, falls back to plain bold on +// malformed stops or empty input. +func agentGradient(s string, stops []string) string { + if !colorEnabled || len(stops) < 2 { + return bold(s) + } + type rgb struct{ r, g, b int } + pal := make([]rgb, 0, len(stops)) + for _, h := range stops { + r, g, b, ok := parseHex(h) + if !ok { + return bold(s) + } + pal = append(pal, rgb{r, g, b}) + } + runes := []rune(s) + if len(runes) == 0 { + return s + } + var out strings.Builder + out.WriteString("\x1b[1m") + for i, ch := range runes { + pos := 0.0 + if len(runes) > 1 { + pos = float64(i) / float64(len(runes)-1) + } + seg := pos * float64(len(pal)-1) + a := int(seg) + b := a + 1 + if b >= len(pal) { + b = len(pal) - 1 + } + t := seg - float64(a) + r := pal[a].r + int(float64(pal[b].r-pal[a].r)*t) + g := pal[a].g + int(float64(pal[b].g-pal[a].g)*t) + bl := pal[a].b + int(float64(pal[b].b-pal[a].b)*t) + fmt.Fprintf(&out, "\x1b[38;2;%d;%d;%dm%c", r, g, bl, ch) + } + out.WriteString("\x1b[0m") + return out.String() +} + // parseHex parses a "#RRGGBB" string into 0..255 components. func parseHex(h string) (r, g, b int, ok bool) { if len(h) != 7 || h[0] != '#' { diff --git a/cmd/clawdchan/setup_agent_antigravity.go b/cmd/clawdchan/setup_agent_antigravity.go new file mode 100644 index 0000000..f25bed5 --- /dev/null +++ b/cmd/clawdchan/setup_agent_antigravity.go @@ -0,0 +1,106 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +// antigravityAgent wires the Google Antigravity host. Antigravity is a +// VS Code-derived editor in Google's Gemini ecosystem; its MCP config +// lives under the parent ~/.gemini directory, not its own home, which +// is why the path is ~/.gemini/antigravity/mcp_config.json on every +// platform. The schema reuses the "mcpServers" convention shared with +// Cursor, Gemini CLI, and GitHub Copilot CLI. +// +// User-scope only — Antigravity's documented entry point ("Manage MCP +// Servers" → "View raw config") opens a single file, with no project +// scope mentioned in the upstream docs. +// +// Reference: https://antigravity.google (Editor → MCP Integration). +func antigravityAgent() *agentWiring { + return &agentWiring{ + key: "antigravity", + flagName: "antigravity", + displayName: "Antigravity", + defaultOn: false, + scopeFlags: []string{"mcp"}, + setup: setupAntigravity, + doctorReport: doctorAntigravity, + uninstallHints: uninstallHintsAntigravity, + } +} + +// antigravityUserMCPPath returns the user-scope mcp_config.json path +// for Antigravity. Layout matches the docs verbatim on every platform: +// ~/.gemini/antigravity/mcp_config.json. +func antigravityUserMCPPath() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".gemini", "antigravity", "mcp_config.json"), nil +} + +func setupAntigravity(yes bool, scopes map[string]string) error { + scope := strings.ToLower(strings.TrimSpace(scopes["mcp"])) + if scope == "" && (yes || !stdinIsTTY()) { + scope = "user" + } + if scope == "" { + fmt.Println(" MCP scope " + dim("(~/.gemini/antigravity/mcp_config.json — user only)") + ":") + fmt.Printf(" %s user %s\n", cyan("[1]"), green("(recommended)")) + fmt.Printf(" %s skip\n", cyan("[2]")) + switch promptChoice(" Choice [1]: ", 1, 2) { + case 2: + scope = "skip" + default: + scope = "user" + } + } + + switch scope { + case "user": + mcpBin, err := requireMCPBinary() + if err != nil { + return err + } + path, err := antigravityUserMCPPath() + if err != nil { + return err + } + _, err = mergeJSONMCPServer(path, map[string]any{ + "command": mcpBin, + }, "(user)") + return err + case "skip": + fmt.Printf(" %s MCP %s %s\n", okTag(), dim("→"), dim("skipped")) + return nil + default: + return fmt.Errorf("unknown -antigravity-mcp-scope %q (use user|skip)", scope) + } +} + +func doctorAntigravity() []string { + path, err := antigravityUserMCPPath() + if err != nil { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil + } + if strings.Contains(string(data), `"clawdchan"`) { + return []string{"Antigravity: MCP registered (user scope, " + path + ")"} + } + return nil +} + +func uninstallHintsAntigravity() []string { + return []string{ + "If you wired Antigravity, remove the clawdchan entry from:", + " ~/.gemini/antigravity/mcp_config.json", + "Delete the \"clawdchan\" key under the \"mcpServers\" object.", + } +} diff --git a/cmd/clawdchan/setup_agent_vscode.go b/cmd/clawdchan/setup_agent_vscode.go new file mode 100644 index 0000000..3d295ef --- /dev/null +++ b/cmd/clawdchan/setup_agent_vscode.go @@ -0,0 +1,112 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +// vscodeAgent is the Visual Studio Code wiring. VS Code 1.102+ supports +// MCP servers natively. User-scope config lives at +// `/Code/User/mcp.json` — the same User profile dir that +// holds settings.json/keybindings.json — and uses a top-level "servers" +// key rather than the "mcpServers" convention shared by the other hosts. +// +// We register at user scope only. Workspace `.vscode/mcp.json` is also +// supported by VS Code, but matching the Cursor/Codex/Copilot pattern +// keeps the setup flow consistent and means a single registration works +// across every project the user opens. +// +// Reference: https://code.visualstudio.com/docs/copilot/customization/mcp-servers +func vscodeAgent() *agentWiring { + return &agentWiring{ + key: "vscode", + flagName: "vscode", + displayName: "VS Code", + defaultOn: false, + scopeFlags: []string{"mcp"}, + setup: setupVSCode, + doctorReport: doctorVSCode, + uninstallHints: uninstallHintsVSCode, + } +} + +// vscodeUserMCPPath returns the user-scope mcp.json path for VS Code +// Stable. The User folder under the OS-specific config dir is where VS +// Code stores per-user settings.json and keybindings.json; mcp.json +// lives alongside them. +func vscodeUserMCPPath() (string, error) { + cfg, err := os.UserConfigDir() + if err != nil { + return "", err + } + return filepath.Join(cfg, "Code", "User", "mcp.json"), nil +} + +func setupVSCode(yes bool, scopes map[string]string) error { + scope := strings.ToLower(strings.TrimSpace(scopes["mcp"])) + if scope == "" && (yes || !stdinIsTTY()) { + scope = "user" + } + if scope == "" { + fmt.Println(" MCP scope " + dim("(VS Code user profile — Code/User/mcp.json)") + ":") + fmt.Printf(" %s user %s\n", cyan("[1]"), green("(recommended)")) + fmt.Printf(" %s skip\n", cyan("[2]")) + switch promptChoice(" Choice [1]: ", 1, 2) { + case 2: + scope = "skip" + default: + scope = "user" + } + } + + switch scope { + case "user": + mcpBin, err := requireMCPBinary() + if err != nil { + return err + } + path, err := vscodeUserMCPPath() + if err != nil { + return err + } + if _, err := mergeJSONMCPServerAt(path, "servers", map[string]any{ + "command": mcpBin, + }, "(user)"); err != nil { + return err + } + fmt.Printf(" %s Reload VS Code %s to pick up the new server.\n", + warnTag(), dim("(Command Palette → \"Developer: Reload Window\")")) + return nil + case "skip": + fmt.Printf(" %s MCP %s %s\n", okTag(), dim("→"), dim("skipped")) + return nil + default: + return fmt.Errorf("unknown -vscode-mcp-scope %q (use user|skip)", scope) + } +} + +func doctorVSCode() []string { + path, err := vscodeUserMCPPath() + if err != nil { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil + } + if strings.Contains(string(data), `"clawdchan"`) { + return []string{"VS Code: MCP registered (user scope, " + path + ")"} + } + return nil +} + +func uninstallHintsVSCode() []string { + return []string{ + "If you wired VS Code, remove the clawdchan entry from the user mcp.json:", + " Command Palette → \"MCP: Open User Configuration\"", + " (or edit the file at /Code/User/mcp.json)", + "Delete the \"clawdchan\" key under the \"servers\" object.", + } +} diff --git a/cmd/clawdchan/setup_agents_test.go b/cmd/clawdchan/setup_agents_test.go index e323f77..6dee994 100644 --- a/cmd/clawdchan/setup_agents_test.go +++ b/cmd/clawdchan/setup_agents_test.go @@ -201,6 +201,98 @@ key = "y" } } +// TestMergeJSONMCPServerAt_EmptyFile guards against a real install +// case: Antigravity (and likely others) create a zero-byte stub +// mcp_config.json on first run. json.Unmarshal of empty bytes errors +// with "unexpected end of JSON input"; the helper has to treat +// zero-length files as if they were "{}". +func TestMergeJSONMCPServerAt_EmptyFile(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "mcp_config.json") + if err := os.WriteFile(path, []byte{}, 0o644); err != nil { + t.Fatal(err) + } + if _, err := mergeJSONMCPServer(path, map[string]any{ + "command": "/bin/clawdchan-mcp", + }, "(user)"); err != nil { + t.Fatalf("mergeJSONMCPServer on empty file: %v", err) + } + data, _ := os.ReadFile(path) + var obj map[string]any + if err := json.Unmarshal(data, &obj); err != nil { + t.Fatalf("result not valid JSON: %v\n%s", err, string(data)) + } + servers, _ := obj["mcpServers"].(map[string]any) + if _, ok := servers["clawdchan"]; !ok { + t.Errorf("clawdchan entry missing: %s", string(data)) + } +} + +// TestMergeJSONMCPServerAt_VSCodeKey verifies that VS Code's "servers" +// top-level key is honored end-to-end: the entry lands under "servers", +// the wrong key "mcpServers" is NOT created as a sibling, and any +// pre-existing "servers" siblings survive. +func TestMergeJSONMCPServerAt_VSCodeKey(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "mcp.json") + existing := `{"servers":{"playwright":{"command":"npx","args":["-y","@microsoft/mcp-server-playwright"]}}}` + if err := os.WriteFile(path, []byte(existing), 0o644); err != nil { + t.Fatal(err) + } + if _, err := mergeJSONMCPServerAt(path, "servers", map[string]any{ + "command": "/bin/clawdchan-mcp", + }, "(user)"); err != nil { + t.Fatalf("mergeJSONMCPServerAt: %v", err) + } + data, _ := os.ReadFile(path) + var obj map[string]any + if err := json.Unmarshal(data, &obj); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if _, wrong := obj["mcpServers"]; wrong { + t.Errorf("must not create mcpServers when topKey is servers: %s", string(data)) + } + servers, ok := obj["servers"].(map[string]any) + if !ok { + t.Fatalf("servers missing: %s", string(data)) + } + if _, ok := servers["playwright"]; !ok { + t.Errorf("sibling 'playwright' dropped") + } + entry, ok := servers["clawdchan"].(map[string]any) + if !ok { + t.Fatalf("no clawdchan entry: %s", string(data)) + } + if entry["command"] != "/bin/clawdchan-mcp" { + t.Errorf("command=%v", entry["command"]) + } +} + +// TestVSCodeUserMCPPath_Layout pins the mcp.json path under the OS +// config dir's Code/User folder — the location VS Code uses for +// user-profile settings. +func TestVSCodeUserMCPPath_Layout(t *testing.T) { + got, err := vscodeUserMCPPath() + if err != nil { + t.Fatalf("vscodeUserMCPPath: %v", err) + } + if !strings.HasSuffix(filepath.ToSlash(got), "Code/User/mcp.json") { + t.Errorf("path = %q, want suffix Code/User/mcp.json", got) + } +} + +// TestAntigravityUserMCPPath_Layout pins the gemini-rooted layout +// documented by Antigravity (.gemini/antigravity/mcp_config.json). +func TestAntigravityUserMCPPath_Layout(t *testing.T) { + got, err := antigravityUserMCPPath() + if err != nil { + t.Fatalf("antigravityUserMCPPath: %v", err) + } + if !strings.HasSuffix(filepath.ToSlash(got), ".gemini/antigravity/mcp_config.json") { + t.Errorf("path = %q, want suffix .gemini/antigravity/mcp_config.json", got) + } +} + // TestAgentRegistryUnique guards against copy-paste mistakes where two // agents share a flag name — which would silently break CLI parsing. func TestAgentRegistryUnique(t *testing.T) { diff --git a/cmd/clawdchan/setup_helpers.go b/cmd/clawdchan/setup_helpers.go index 7395674..e4e2715 100644 --- a/cmd/clawdchan/setup_helpers.go +++ b/cmd/clawdchan/setup_helpers.go @@ -28,17 +28,26 @@ func requireMCPBinary() (string, error) { // sibling field. Creates the file (and its parent dir) if missing. // // Used by hosts whose config format is a plain JSON object keyed by -// mcpServers (Gemini CLI, GitHub Copilot CLI, Cursor). Returns whether -// the clawdchan entry was newly added (true) or updated in place -// (false). The logSuffix is the parenthetical shown at the end of the -// success line, e.g. "(trust=true)" for Gemini or `(tools=["*"])` for -// Copilot. +// mcpServers (Gemini CLI, GitHub Copilot CLI, Cursor, Antigravity). +// Returns whether the clawdchan entry was newly added (true) or +// updated in place (false). The logSuffix is the parenthetical shown +// at the end of the success line, e.g. "(trust=true)" for Gemini or +// `(tools=["*"])` for Copilot. func mergeJSONMCPServer(path string, entry map[string]any, logSuffix string) (bool, error) { + return mergeJSONMCPServerAt(path, "mcpServers", entry, logSuffix) +} + +// mergeJSONMCPServerAt is the general form. VS Code is the one host +// that nests its servers under the top-level "servers" key (matching +// the MCP spec's native naming) instead of "mcpServers". +func mergeJSONMCPServerAt(path, topKey string, entry map[string]any, logSuffix string) (bool, error) { data, err := os.ReadFile(path) - if err != nil { - if !os.IsNotExist(err) { - return false, err - } + if err != nil && !os.IsNotExist(err) { + return false, err + } + // Missing file and empty-stub file (some hosts — e.g. Antigravity — + // create a zero-byte placeholder on first run) both decode as {}. + if len(data) == 0 { data = []byte("{}") } var obj map[string]any @@ -48,13 +57,13 @@ func mergeJSONMCPServer(path string, entry map[string]any, logSuffix string) (bo if obj == nil { obj = map[string]any{} } - servers, _ := obj["mcpServers"].(map[string]any) + servers, _ := obj[topKey].(map[string]any) if servers == nil { servers = map[string]any{} } _, existed := servers["clawdchan"] servers["clawdchan"] = entry - obj["mcpServers"] = servers + obj[topKey] = servers out, err := json.MarshalIndent(obj, "", " ") if err != nil { diff --git a/npm/README.md b/npm/README.md index ef8af7a..7d402b0 100644 --- a/npm/README.md +++ b/npm/README.md @@ -2,7 +2,7 @@ Let your Claude talk to mine — private channel between two (human, agent) pairs. Today the setup flow wires Claude Code, Gemini CLI, Codex CLI, -GitHub Copilot CLI, Cursor, and OpenClaw. +GitHub Copilot CLI, Cursor, VS Code, Antigravity, and OpenClaw. ```sh npm i -g clawdchan diff --git a/site/antigravity-mark.png b/site/antigravity-mark.png new file mode 100644 index 0000000000000000000000000000000000000000..c677e4f5f75ba256f0b1fc2e6e1f48d1ac23c0eb GIT binary patch literal 16849 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 your Claude talk to mine.
  • Codex CLI
  • GitHub Copilot CLI
  • Cursor
  • +
  • VS Code
  • +
  • Antigravity
  • OpenClaw
  • From 24854b1ef77b5c74b1ad7f3e4e4fc643e96fe106 Mon Sep 17 00:00:00 2001 From: Ayham Badarneh Date: Sat, 2 May 2026 19:24:25 +0300 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- npm/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/npm/README.md b/npm/README.md index 7d402b0..cb4f3fc 100644 --- a/npm/README.md +++ b/npm/README.md @@ -2,7 +2,8 @@ Let your Claude talk to mine — private channel between two (human, agent) pairs. Today the setup flow wires Claude Code, Gemini CLI, Codex CLI, -GitHub Copilot CLI, Cursor, VS Code, Antigravity, and OpenClaw. +GitHub Copilot CLI, Cursor, VS Code, and Antigravity, with an optional +OpenClaw gateway step. ```sh npm i -g clawdchan From aeed2c65c57c2aa4c733b808c4dbc6cfeaabd657 Mon Sep 17 00:00:00 2001 From: Ayham Badarneh Date: Sat, 2 May 2026 19:24:46 +0300 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- site/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/index.html b/site/index.html index 73ccdc4..c31f114 100644 --- a/site/index.html +++ b/site/index.html @@ -54,7 +54,7 @@

    Let your Claude talk to mine.

  • GitHub Copilot CLI
  • Cursor
  • VS Code
  • -
  • Antigravity
  • +
  • Antigravity
  • OpenClaw