diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ca5b9ff1..67e067632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### New Features + +- Grok is now a supported agent: `codegraph install` wires the CodeGraph MCP server into Grok (user-wide or per project), existing MCP servers in its config are preserved, and `codegraph uninstall` reverses the setup. Start a new Grok session — or press `r` in `/mcps` — after installing so Grok picks up the server. ## [1.6.0] - 2026-08-26 diff --git a/CLAUDE.md b/CLAUDE.md index 0b2434919..d594da2f6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -CodeGraph is a local-first code intelligence library + CLI + MCP server. It parses any supported codebase with tree-sitter, stores symbols/edges/files in SQLite (FTS5), and exposes a knowledge graph to AI agents (Claude Code, Cursor, Codex CLI, opencode) over MCP. Per-project data lives in `.codegraph/`. Extraction is deterministic — derived from AST, not LLM-summarized. +CodeGraph is a local-first code intelligence library + CLI + MCP server. It parses any supported codebase with tree-sitter, stores symbols/edges/files in SQLite (FTS5), and exposes a knowledge graph to AI agents (Claude Code, Cursor, Codex CLI, opencode, Grok) over MCP. Per-project data lives in `.codegraph/`. Extraction is deterministic — derived from AST, not LLM-summarized. Distributed as `@colbymchenry/codegraph` on npm; same binary serves as installer, indexer, and MCP server. @@ -74,11 +74,11 @@ Defined in `src/types.ts`. Both extractors and resolvers must use these exact st `src/installer/` is the entry point for `codegraph install` (and the bare `codegraph`/`npx @colbymchenry/codegraph` invocation). Architecture: - `targets/registry.ts` lists every supported agent. -- `targets/types.ts` defines the `AgentTarget` interface — adding a 5th agent (Continue, Zed, Windsurf…) is **one new file in `targets/` + one entry in `registry.ts`**. Each target owns its config-file location and MCP-server JSON/TOML/JSONC writing. (Targets no longer write an instructions file — see below.) -- Current targets: `claude.ts`, `cursor.ts`, `codex.ts`, `opencode.ts`. -- `targets/toml.ts` is a hand-rolled TOML serializer scoped to `[mcp_servers.codegraph]` (used by Codex). Sibling tables and `[[array_of_tables]]` are preserved verbatim. No new dependency. +- `targets/types.ts` defines the `AgentTarget` interface — adding a new agent (Continue, Zed, Windsurf…) is **one new file in `targets/` + one entry in `registry.ts`**. Each target owns its config-file location and MCP-server JSON/TOML/JSONC writing. (Targets no longer write an instructions file — see below.) +- Current targets: `claude.ts`, `cursor.ts`, `codex.ts`, `opencode.ts`, `hermes.ts`, `gemini.ts`, `antigravity.ts`, `kiro.ts`, `grok.ts`, `copilot-vscode.ts`, `copilot-cli.ts`, `copilot-jetbrains.ts`. +- `targets/toml.ts` is a hand-rolled TOML serializer scoped to `[mcp_servers.codegraph]` (used by Codex and Grok). Sibling tables and `[[array_of_tables]]` are preserved verbatim. No new dependency. - opencode reads `opencode.jsonc` by default; the installer prefers existing `.jsonc`, falls back to `.json`, and creates `.jsonc` for greenfield installs. Edits are surgical via `jsonc-parser` so user comments and formatting survive install/re-install/uninstall round-trips. -- `instructions-template.ts` no longer holds an instructions body — it exports only the ``/`` markers. The installer **stopped writing** a `## CodeGraph` block into each agent's instructions file (`CLAUDE.md` / `~/.codex/AGENTS.md` / `~/.config/opencode/AGENTS.md` / `~/.gemini/GEMINI.md` / `.cursor/rules/codegraph.mdc` / Kiro steering doc) because it duplicated the MCP `initialize` instructions verbatim (issue #529). Each target's `install` (self-heal on upgrade) and `uninstall` use the markers to **strip** a block a previous install left behind. `server-instructions.ts` is the single source of truth for agent-facing guidance. +- `instructions-template.ts` no longer holds an instructions body — it exports only the ``/`` markers. The installer **stopped writing** a `## CodeGraph` block into each agent's instructions file (`CLAUDE.md` / `~/.codex/AGENTS.md` / `~/.config/opencode/AGENTS.md` / `~/.gemini/GEMINI.md` / `.cursor/rules/codegraph.mdc` / Kiro steering doc / `$GROK_HOME/rules/codegraph.md`) because it duplicated the MCP `initialize` instructions verbatim (issue #529). Each target's `install` (self-heal on upgrade) and `uninstall` use the markers to **strip** a block a previous install left behind. `server-instructions.ts` is the single source of truth for agent-facing guidance. - All installer changes need matching coverage in `__tests__/installer-targets.test.ts` — there are ~47 parameterized contract tests covering install idempotency, sibling preservation, uninstall reverses install, byte-equal re-runs returning `unchanged`, and partial-state recovery for Codex. ### Cursor MCP working-directory quirk diff --git a/README.md b/README.md index 48323f6fd..81d5b2cf4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Already installed? Run `codegraph upgrade` Follow [@getcodegraph](https://x.com/getcodegraph) on X for updates. -### Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, Kiro, and GitHub Copilot with Semantic Code Intelligence +### Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, Kiro, Grok, and GitHub Copilot with Semantic Code Intelligence **The fastest complete code graph · surgical context · built for how agents actually work · 100% local** @@ -35,6 +35,7 @@ Follow [@getcodegraph](https://x.com/getcodegraph) on X for updates. [![Gemini](https://img.shields.io/badge/Gemini-supported-blueviolet.svg)](#supported-agents) [![Antigravity](https://img.shields.io/badge/Antigravity-supported-blueviolet.svg)](#supported-agents) [![Kiro](https://img.shields.io/badge/Kiro-supported-blueviolet.svg)](#supported-agents) +[![Grok](https://img.shields.io/badge/Grok-supported-blueviolet.svg)](#supported-agents) [![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-supported-blueviolet.svg)](#supported-agents)
@@ -105,7 +106,7 @@ In a **new terminal**, run the installer to connect CodeGraph to the agents you codegraph install ``` -Detects and auto-configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, and GitHub Copilot (VS Code, Copilot CLI, JetBrains IDEs) — wiring the CodeGraph MCP server into each. **This is the step that connects CodeGraph to your agent;** installing the CLI in step 1 does not do it on its own. It only wires up your agent — it does **not** index any code; building each project's graph is the separate `codegraph init` in step 3. (Shortcut: `npx @colbymchenry/codegraph` downloads and runs this in one go.) +Detects and auto-configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, Grok, and GitHub Copilot (VS Code, Copilot CLI, JetBrains IDEs) — wiring the CodeGraph MCP server into each. **This is the step that connects CodeGraph to your agent;** installing the CLI in step 1 does not do it on its own. It only wires up your agent — it does **not** index any code; building each project's graph is the separate `codegraph init` in step 3. (Shortcut: `npx @colbymchenry/codegraph` downloads and runs this in one go.) ### 3. Initialize each project @@ -286,7 +287,7 @@ CodeGraph's parsing engine is a **native Rust kernel**: 20 languages — TypeScr
How auto-syncing works — and why you don't need to run codegraph sync manually -When your agent (Claude Code, Cursor, Codex, opencode) launches `codegraph serve --mcp`, three layers keep the index in step with your code — and make sure the agent never gets a silent wrong answer in the brief window between an edit and the next sync: +When your agent (Claude Code, Cursor, Codex, opencode, Grok) launches `codegraph serve --mcp`, three layers keep the index in step with your code — and make sure the agent never gets a silent wrong answer in the brief window between an edit and the next sync: 1. **File watcher with debounced auto-sync.** A native FSEvents / inotify / ReadDirectoryChangesW watcher captures every source-file create / modify / delete and triggers a re-index after a debounce window (default `2000ms`, tunable via `CODEGRAPH_WATCH_DEBOUNCE_MS`, clamped to `[100ms, 60s]`). Bursts of edits collapse into a single sync. @@ -376,10 +377,10 @@ npx @colbymchenry/codegraph ``` The installer will: -- Ask which agent(s) to configure — auto-detects installed ones from: **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, **Kiro**, **GitHub Copilot** (VS Code, Copilot CLI, JetBrains IDEs) +- Ask which agent(s) to configure — auto-detects installed ones from: **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, **Kiro**, **Grok**, **GitHub Copilot** (VS Code, Copilot CLI, JetBrains IDEs) - Prompt to install `codegraph` on your PATH (so agents can launch the MCP server) - Ask whether configs apply to all your projects or just this one -- Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`) — that's how subagents and non-MCP agents learn the `codegraph explore` command, since the MCP server's own guidance only reaches the main agent. Removed cleanly by `codegraph uninstall`. +- Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md` / Grok `rules/codegraph.md`) — that's how subagents and non-MCP agents learn the `codegraph explore` command, since the MCP server's own guidance only reaches the main agent. Removed cleanly by `codegraph uninstall`. - Set up auto-allow permissions when Claude Code is one of the targets The installer **wires up your agents only — it does not index your code.** After it finishes, build each project's graph yourself with `codegraph init` (step 3). One global `codegraph install` covers every project; you run `codegraph init` once per project. @@ -390,9 +391,11 @@ The installer **wires up your agents only — it does not index your code.** Aft codegraph install --yes # auto-detect agents, install global codegraph install --yes --init # same, then build the current project's index (one-shot bootstrap) codegraph install --target=cursor,claude --yes # explicit target list +codegraph install --target=grok --yes # Grok only codegraph install --target=auto --location=local # detected agents, project-local codegraph install --target=copilot-vscode,copilot-cli,copilot-jetbrains --yes # GitHub Copilot everywhere codegraph install --print-config codex # print snippet, no file writes +codegraph install --print-config grok # same, for Grok codegraph install --print-config copilot-vscode # same, for Copilot in VS Code ``` @@ -407,7 +410,7 @@ codegraph install --print-config copilot-vscode # same, for Copilot in VS C ### 2. Restart Your Agent -Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro / VS Code, the Copilot CLI, or your JetBrains IDE for GitHub Copilot) for the MCP server to load. +Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro / Grok / VS Code, the Copilot CLI, or your JetBrains IDE for GitHub Copilot) for the MCP server to load. ### 3. Initialize Projects @@ -785,6 +788,7 @@ is written): - **Gemini CLI** - **Antigravity IDE** - **Kiro** +- **Grok** - **GitHub Copilot** — Copilot Chat in VS Code (`copilot-vscode`), the Copilot CLI (`copilot-cli`), and the Copilot plugin in JetBrains IDEs (`copilot-jetbrains`) ## Supported Languages @@ -886,7 +890,7 @@ MIT
-**Made for AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, and GitHub Copilot** +**Made for AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, Grok, and GitHub Copilot** [Report Bug](https://github.com/colbymchenry/codegraph/issues) · [Request Feature](https://github.com/colbymchenry/codegraph/issues) diff --git a/__tests__/installer-targets.test.ts b/__tests__/installer-targets.test.ts index 4ec3e5903..9e04569f4 100644 --- a/__tests__/installer-targets.test.ts +++ b/__tests__/installer-targets.test.ts @@ -39,6 +39,7 @@ function setHome(dir: string): { restore: () => void } { APPDATA: process.env.APPDATA, XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME, HERMES_HOME: process.env.HERMES_HOME, + GROK_HOME: process.env.GROK_HOME, COPILOT_HOME: process.env.COPILOT_HOME, }; process.env.HOME = dir; @@ -46,6 +47,7 @@ function setHome(dir: string): { restore: () => void } { process.env.APPDATA = path.join(dir, '.config'); process.env.XDG_CONFIG_HOME = path.join(dir, '.config'); delete process.env.HERMES_HOME; + delete process.env.GROK_HOME; delete process.env.COPILOT_HOME; return { restore() { @@ -54,6 +56,7 @@ function setHome(dir: string): { restore: () => void } { if (prev.APPDATA === undefined) delete process.env.APPDATA; else process.env.APPDATA = prev.APPDATA; if (prev.XDG_CONFIG_HOME === undefined) delete process.env.XDG_CONFIG_HOME; else process.env.XDG_CONFIG_HOME = prev.XDG_CONFIG_HOME; if (prev.HERMES_HOME === undefined) delete process.env.HERMES_HOME; else process.env.HERMES_HOME = prev.HERMES_HOME; + if (prev.GROK_HOME === undefined) delete process.env.GROK_HOME; else process.env.GROK_HOME = prev.GROK_HOME; if (prev.COPILOT_HOME === undefined) delete process.env.COPILOT_HOME; else process.env.COPILOT_HOME = prev.COPILOT_HOME; }, }; @@ -967,6 +970,125 @@ describe('Installer targets — partial-state idempotency', () => { expect(fs.readFileSync(tomlPath, 'utf-8')).toBe(historyTables); }); + it('grok: install writes config.toml AND the rules/codegraph.md block (#704)', () => { + const grok = getTarget('grok')!; + const first = grok.install('global', { autoAllow: false }); + const rulesMd = path.join(tmpHome, '.grok', 'rules', 'codegraph.md'); + expect(first.files.some((f) => f.path.endsWith('config.toml'))).toBe(true); + expect(fs.existsSync(rulesMd)).toBe(true); + const body = fs.readFileSync(rulesMd, 'utf-8'); + expect(body).toContain('## CodeGraph'); + expect(body).toContain('codegraph explore'); + const toml = fs.readFileSync(path.join(tmpHome, '.grok', 'config.toml'), 'utf-8'); + expect(toml).toContain('[mcp_servers.codegraph]'); + expect(toml).toContain('command = "codegraph"'); + expect(toml).toContain('args = ["serve", "--mcp"]'); + expect(first.notes?.join(' ')).toMatch(/\/mcps/); + const second = grok.install('global', { autoAllow: false }); + for (const f of second.files) expect(f.action).toBe('unchanged'); + }); + + it('grok: install replaces a legacy rules/codegraph.md block with the current one, keeping user content', () => { + const grok = getTarget('grok')!; + const dir = path.join(tmpHome, '.grok', 'rules'); + fs.mkdirSync(dir, { recursive: true }); + const rulesMd = path.join(dir, 'codegraph.md'); + fs.writeFileSync(rulesMd, `# My grok notes\n\nBe terse.\n\n${LEGACY_BLOCK}\n`); + + const result = grok.install('global', { autoAllow: false }); + + const body = fs.readFileSync(rulesMd, 'utf-8'); + expect(body).toContain('# My grok notes'); + expect(body).toContain('Be terse.'); + expect(body).not.toContain('Prefer `codegraph_search`'); + expect(body).toContain('codegraph explore'); + const mdEntry = result.files.find((f) => f.path.endsWith('codegraph.md')); + expect(mdEntry?.action).toBe('updated'); + }); + + it('grok: local install writes ./.grok/config.toml and ./.grok/rules/codegraph.md', () => { + const grok = getTarget('grok')!; + const result = grok.install('local', { autoAllow: false }); + const paths = result.files.map((f) => f.path.replace(/\\/g, '/')); + expect(paths.some((p) => p.endsWith('/.grok/config.toml'))).toBe(true); + expect(paths.some((p) => p.endsWith('/.grok/rules/codegraph.md'))).toBe(true); + + const toml = fs.readFileSync(path.join(process.cwd(), '.grok', 'config.toml'), 'utf-8'); + expect(toml).toContain('[mcp_servers.codegraph]'); + expect(fs.readFileSync(path.join(process.cwd(), '.grok', 'rules', 'codegraph.md'), 'utf-8')) + .toContain('codegraph explore'); + + expect(result.notes?.join(' ')).toMatch(/trusted/); + + expect(fs.existsSync(path.join(tmpHome, '.grok', 'config.toml'))).toBe(false); + }); + + it('grok: local uninstall reverses the local install and leaves the global entry alone', () => { + const grok = getTarget('grok')!; + grok.install('global', { autoAllow: false }); + grok.install('local', { autoAllow: false }); + expect(grok.detect('local').alreadyConfigured).toBe(true); + + grok.uninstall('local'); + + expect(grok.detect('local').alreadyConfigured).toBe(false); + expect(grok.detect('global').alreadyConfigured).toBe(true); + expect(fs.readFileSync(path.join(tmpHome, '.grok', 'config.toml'), 'utf-8')) + .toContain('[mcp_servers.codegraph]'); + }); + + it('grok: install preserves a sibling [mcp_servers.other] table', () => { + const grok = getTarget('grok')!; + const tomlPath = path.join(tmpHome, '.grok', 'config.toml'); + fs.mkdirSync(path.dirname(tomlPath), { recursive: true }); + fs.writeFileSync(tomlPath, [ + '[models]', + 'default = "grok-4"', + '', + '[mcp_servers.other]', + 'command = "other"', + 'args = ["serve"]', + '', + ].join('\n')); + + grok.install('global', { autoAllow: false }); + const afterInstall = fs.readFileSync(tomlPath, 'utf-8'); + expect(afterInstall).toContain('[models]'); + expect(afterInstall).toContain('default = "grok-4"'); + expect(afterInstall).toContain('[mcp_servers.other]'); + expect(afterInstall).toContain('command = "other"'); + expect(afterInstall).toContain('[mcp_servers.codegraph]'); + + grok.uninstall('global'); + const afterUninstall = fs.readFileSync(tomlPath, 'utf-8'); + expect(afterUninstall).toContain('[models]'); + expect(afterUninstall).toContain('[mcp_servers.other]'); + expect(afterUninstall).not.toContain('[mcp_servers.codegraph]'); + }); + + it('grok: GROK_HOME redirects the global config dir', () => { + const custom = path.join(tmpHome, 'custom-grok'); + process.env.GROK_HOME = custom; + const grok = getTarget('grok')!; + grok.install('global', { autoAllow: false }); + expect(fs.existsSync(path.join(custom, 'config.toml'))).toBe(true); + expect(fs.existsSync(path.join(tmpHome, '.grok', 'config.toml'))).toBe(false); + expect(fs.readFileSync(path.join(custom, 'config.toml'), 'utf-8')) + .toContain('[mcp_servers.codegraph]'); + expect(fs.existsSync(path.join(custom, 'rules', 'codegraph.md'))).toBe(true); + }); + + it('grok: printConfig names config.toml and does not write', () => { + const grok = getTarget('grok')!; + const before = listAllFiles(tmpHome).concat(listAllFiles(tmpCwd)); + const out = grok.printConfig('global'); + expect(out).toContain('[mcp_servers.codegraph]'); + expect(out).toContain('command = "codegraph"'); + expect(out).toMatch(/config\.toml/); + const after = listAllFiles(tmpHome).concat(listAllFiles(tmpCwd)); + expect(after.sort()).toEqual(before.sort()); + }); + it('claude: local install writes ./.mcp.json (project scope), not ./.claude.json', () => { const claude = getTarget('claude')!; const result = claude.install('local', { autoAllow: false }); @@ -1317,6 +1439,7 @@ describe('Installer targets — registry', () => { expect(getTarget('gemini')?.id).toBe('gemini'); expect(getTarget('antigravity')?.id).toBe('antigravity'); expect(getTarget('kiro')?.id).toBe('kiro'); + expect(getTarget('grok')?.id).toBe('grok'); expect(getTarget('copilot-vscode')?.id).toBe('copilot-vscode'); expect(getTarget('copilot-cli')?.id).toBe('copilot-cli'); expect(getTarget('copilot-jetbrains')?.id).toBe('copilot-jetbrains'); @@ -1330,6 +1453,11 @@ describe('Installer targets — registry', () => { expect(csv.map((t) => t.id)).toEqual(['claude', 'cursor']); }); + it("resolveTargetFlag('all') includes grok", () => { + const ids = resolveTargetFlag('all', 'global').map((t) => t.id); + expect(ids).toContain('grok'); + }); + it("resolveTargetFlag('all') includes every Copilot target", () => { const ids = resolveTargetFlag('all', 'global').map((t) => t.id); expect(ids).toContain('copilot-vscode'); diff --git a/site/src/content/docs/getting-started/installation.md b/site/src/content/docs/getting-started/installation.md index 4f9b90986..ea5ed5f3b 100644 --- a/site/src/content/docs/getting-started/installation.md +++ b/site/src/content/docs/getting-started/installation.md @@ -11,10 +11,10 @@ npx @colbymchenry/codegraph The installer will: -- Ask which agent(s) to configure — auto-detecting installed ones from **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, and **Kiro**. +- Ask which agent(s) to configure — auto-detecting installed ones from **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, **Kiro**, and **Grok**. - Prompt to install `codegraph` on your `PATH` (so agents can launch the MCP server). - Ask whether configs apply to all your projects or just this one. -- Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`). Cursor and Kiro get the MCP config only. Removed cleanly by `codegraph uninstall`. +- Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md` / Grok `rules/codegraph.md`). Cursor and Kiro get the MCP config only. Removed cleanly by `codegraph uninstall`. - Set up auto-allow permissions when Claude Code is one of the targets. The installer **wires up your agents only — it does not index your code.** After it finishes, build each project's graph yourself with `codegraph init` (step 3 below). @@ -38,7 +38,7 @@ codegraph install --print-config codex # print snippet, no file wr ## 2. Restart your agent -Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro) for the MCP server to load. +Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro / Grok) for the MCP server to load. ## 3. Initialize projects diff --git a/site/src/content/docs/getting-started/introduction.md b/site/src/content/docs/getting-started/introduction.md index 44d6546c1..29e894a01 100644 --- a/site/src/content/docs/getting-started/introduction.md +++ b/site/src/content/docs/getting-started/introduction.md @@ -5,7 +5,7 @@ description: What CodeGraph is, and why it makes AI coding agents faster and mor CodeGraph is a **local-first code-intelligence tool**. It parses your codebase with [tree-sitter](https://tree-sitter.github.io/), stores every symbol, edge, and file in a local SQLite database, and exposes the result as a queryable **knowledge graph** — over the [Model Context Protocol (MCP)](/codegraph/reference/mcp-server/), a CLI, and a TypeScript library. -It exists to make AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, and Kiro — **answer structural questions without scanning files**. Instead of fanning out across `grep`, `glob`, and `Read` to reconstruct how code fits together, an agent queries a pre-built index and gets the answer in a handful of calls. +It exists to make AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, and Grok — **answer structural questions without scanning files**. Instead of fanning out across `grep`, `glob`, and `Read` to reconstruct how code fits together, an agent queries a pre-built index and gets the answer in a handful of calls. ## Why it matters diff --git a/site/src/content/docs/getting-started/quickstart.md b/site/src/content/docs/getting-started/quickstart.md index e1ea543f0..57c386128 100644 --- a/site/src/content/docs/getting-started/quickstart.md +++ b/site/src/content/docs/getting-started/quickstart.md @@ -25,7 +25,7 @@ Already have Node? `npm i -g @colbymchenry/codegraph` works on any version. Code codegraph install ``` -Auto-detects and configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, and Kiro — wiring the CodeGraph MCP server into each. This step connects your agents only; it does **not** index any code. (Shortcut: `npx @colbymchenry/codegraph` downloads and runs the installer in one go.) +Auto-detects and configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, and Grok — wiring the CodeGraph MCP server into each. This step connects your agents only; it does **not** index any code. (Shortcut: `npx @colbymchenry/codegraph` downloads and runs the installer in one go.) ## 3. Initialize each project diff --git a/site/src/content/docs/guides/indexing.md b/site/src/content/docs/guides/indexing.md index 1518c3d0a..7d904d94f 100644 --- a/site/src/content/docs/guides/indexing.md +++ b/site/src/content/docs/guides/indexing.md @@ -24,7 +24,7 @@ codegraph sync # incremental — only changed files ## Stay fresh automatically -**You don't need to run `codegraph sync` by hand during an agent session.** When your agent (Claude Code, Cursor, Codex, opencode, Hermes, Gemini, Antigravity, Kiro) launches `codegraph serve --mcp`, three layers cooperate to keep the index in step with your code — and to never give the agent a quiet wrong answer in the small window between an edit and the next sync. +**You don't need to run `codegraph sync` by hand during an agent session.** When your agent (Claude Code, Cursor, Codex, opencode, Hermes, Gemini, Antigravity, Kiro, Grok) launches `codegraph serve --mcp`, three layers cooperate to keep the index in step with your code — and to never give the agent a quiet wrong answer in the small window between an edit and the next sync. ### 1. File watcher with debounced auto-sync (always on) diff --git a/site/src/content/docs/reference/integrations.md b/site/src/content/docs/reference/integrations.md index 1b4b87c89..989b84ed2 100644 --- a/site/src/content/docs/reference/integrations.md +++ b/site/src/content/docs/reference/integrations.md @@ -3,7 +3,7 @@ title: Integrations description: Supported agents, and manual MCP setup. --- -The interactive installer auto-detects and configures each supported agent — wiring the CodeGraph MCP server into each. For the agents that use an instructions file, it also writes a short marker-fenced CodeGraph section (`CLAUDE.md`, `AGENTS.md`, or `GEMINI.md`) so subagents and non-MCP harnesses learn the `codegraph explore` command; `codegraph uninstall` removes it. +The interactive installer auto-detects and configures each supported agent — wiring the CodeGraph MCP server into each. For the agents that use an instructions file, it also writes a short marker-fenced CodeGraph section (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, or Grok's `rules/codegraph.md`) so subagents and non-MCP harnesses learn the `codegraph explore` command; `codegraph uninstall` removes it. ## Supported agents @@ -15,6 +15,7 @@ The interactive installer auto-detects and configures each supported agent — w - **Gemini CLI** - **Antigravity IDE** - **Kiro** +- **Grok** Run `npx @colbymchenry/codegraph` and pick your agent(s); see [Installation](/codegraph/getting-started/installation/) for the non-interactive flags. @@ -57,3 +58,15 @@ One wildcard auto-approves every CodeGraph tool. The server lists a single tool :::tip Cursor launches MCP subprocesses with the wrong working directory. The installer handles this for you by injecting a `--path` argument; if you wire Cursor up by hand, pass the project path explicitly. ::: + +### Grok + +Add to `~/.grok/config.toml` (or `.grok/config.toml` in a project): + +```toml +[mcp_servers.codegraph] +command = "codegraph" +args = ["serve", "--mcp"] +``` + +Start a new Grok session, or press `r` in `/mcps`, so Grok picks up the server. diff --git a/src/bin/codegraph.ts b/src/bin/codegraph.ts index 19038df1b..34038420a 100644 --- a/src/bin/codegraph.ts +++ b/src/bin/codegraph.ts @@ -1855,7 +1855,7 @@ program // ever fires for a person who typed it. if (process.stdin.isTTY && !process.env.CODEGRAPH_DAEMON_INTERNAL) { console.error(chalk.bold('\nCodeGraph MCP server\n')); - console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, …)"); + console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, Grok, …)"); console.error("starts automatically — you don't run it yourself."); console.error(`\nIt's already wired up by ${chalk.cyan('codegraph install')}. To check on things:`); console.error(` ${chalk.cyan('codegraph status')} ${chalk.dim('— is this project indexed and healthy?')}`); @@ -2340,7 +2340,7 @@ program */ program .command('install') - .description('Install codegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot)') + .description('Install codegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, Grok, GitHub Copilot)') .option('-t, --target ', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt') .option('-l, --location ', 'Install location: "global" or "local". Default: prompt') .option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on') @@ -2454,7 +2454,7 @@ program */ program .command('uninstall') - .description('Remove codegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot)') + .description('Remove codegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, Grok, GitHub Copilot)') .option('-t, --target ', 'Target agent(s): comma-separated ids, or "all". Default: all') .option('-l, --location ', 'Uninstall location: "global" or "local". Default: prompt') .option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all') diff --git a/src/installer/index.ts b/src/installer/index.ts index 199a6de75..21492ca31 100644 --- a/src/installer/index.ts +++ b/src/installer/index.ts @@ -3,7 +3,7 @@ * * Multi-target: writes MCP server config + instructions for the * agents the user picks (Claude Code, Cursor, Codex CLI, opencode, - * Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, and GitHub + * Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, Grok, and GitHub * Copilot in VS Code / the Copilot CLI / JetBrains IDEs). * Defaults to the Claude-only behavior for backwards compatibility * when no targets are explicitly chosen and nothing else is detected. @@ -469,8 +469,8 @@ export async function runUninstaller(opts: RunUninstallerOptions): Promise const sel = await clack.select({ message: 'Remove CodeGraph from all your projects, or just this one?', options: [ - { value: 'global' as const, label: 'All projects (global)', hint: '~/.claude, ~/.cursor, ~/.codex, ~/.config/opencode, ~/.hermes, ~/.gemini, ~/.kiro, ~/.copilot, ~/.config/github-copilot' }, - { value: 'local' as const, label: 'Just this project (local)', hint: './.claude, ./.cursor, ./.vscode, ./opencode.jsonc, ./.gemini, ./.kiro' }, + { value: 'global' as const, label: 'All projects (global)', hint: '~/.claude, ~/.cursor, ~/.codex, ~/.config/opencode, ~/.hermes, ~/.gemini, ~/.kiro, ~/.grok, ~/.copilot, ~/.config/github-copilot' }, + { value: 'local' as const, label: 'Just this project (local)', hint: './.claude, ./.cursor, ./.vscode, ./opencode.jsonc, ./.gemini, ./.kiro, ./.grok' }, ], initialValue: 'global' as const, }); diff --git a/src/installer/targets/grok.ts b/src/installer/targets/grok.ts new file mode 100644 index 000000000..f68ef62e6 --- /dev/null +++ b/src/installer/targets/grok.ts @@ -0,0 +1,196 @@ +/** + * Grok (Grok Build TUI / `grok` CLI) target. + * + * - MCP server entry to `config.toml` as the dotted-key table + * `[mcp_servers.codegraph]`. Same TOML serializer as Codex + * (`./toml.ts`). + * - Instructions to `$GROK_HOME/rules/codegraph.md` (global) or + * `/.grok/rules/codegraph.md` (local). Grok always scans + * `rules/*.md` at those locations; it does not load a home-level + * `AGENTS.md` the way Codex does. + * + * Both locations are supported: + * - global: `$GROK_HOME/config.toml` (default `~/.grok/config.toml`) + * - local: `/.grok/config.toml` + * + * Project-scoped files contribute `[mcp_servers]` (and `[permission]`, + * `[plugins]`). Repo-local MCP servers are gated on folder trust — the + * same store as project hooks (`~/.grok/trusted_folders.toml`) — so a + * local install is surfaced with a trust note rather than silent + * success. + * + * Honors `$GROK_HOME` (default `~/.grok`). + * + * No installer-written permissions: Grok's `[permission]` table is + * user-owned (sibling allow/deny rules), and MCP tools can be + * always-allowed from the first prompt. `autoAllow` is ignored. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { + AgentTarget, + DetectionResult, + InstallOptions, + Location, + WriteResult, +} from './types'; +import { + atomicWriteFileSync, + getMcpServerConfig, + removeMarkedSection, + upsertInstructionsEntry, +} from './shared'; +import { + CODEGRAPH_SECTION_END, + CODEGRAPH_SECTION_START, +} from '../instructions-template'; +import { buildTomlTable, removeTomlTable, upsertTomlTable } from './toml'; + +const TOML_HEADER = 'mcp_servers.codegraph'; + +function grokHome(): string { + return process.env.GROK_HOME + ? path.resolve(process.env.GROK_HOME) + : path.join(os.homedir(), '.grok'); +} + +function configDir(loc: Location): string { + return loc === 'global' ? grokHome() : path.join(process.cwd(), '.grok'); +} + +function tomlConfigPath(loc: Location): string { + return path.join(configDir(loc), 'config.toml'); +} + +function instructionsPath(loc: Location): string { + return path.join(configDir(loc), 'rules', 'codegraph.md'); +} + +/** + * Repo-local MCP is skipped until the folder is trusted (same gate as + * project hooks). Say so rather than reporting silent success. + */ +function trustNote(): string { + return `Grok applies ${tomlConfigPath('local')} only in a trusted project — untrusted folders skip repo-local MCP servers. Trust this project in Grok to activate it.`; +} + +class GrokTarget implements AgentTarget { + readonly id = 'grok' as const; + readonly displayName = 'Grok'; + readonly docsUrl = 'https://docs.x.ai/build/features/mcp-servers'; + + supportsLocation(_loc: Location): boolean { + return true; + } + + detect(loc: Location): DetectionResult { + const tomlPath = tomlConfigPath(loc); + let alreadyConfigured = false; + if (fs.existsSync(tomlPath)) { + try { + const content = fs.readFileSync(tomlPath, 'utf-8'); + alreadyConfigured = content.includes(`[${TOML_HEADER}]`); + } catch { /* ignore */ } + } + // Global: ~/.grok (or $GROK_HOME) existing means Grok has run here. + // Local: the project only counts as "Grok-enabled" once it actually + // has a .grok/ dir or config file of its own. + const installed = fs.existsSync(configDir(loc)) || fs.existsSync(tomlPath); + return { installed, alreadyConfigured, configPath: tomlPath }; + } + + install(loc: Location, _opts: InstallOptions): WriteResult { + const files: WriteResult['files'] = []; + + files.push(writeMcpEntry(loc)); + + // rules/codegraph.md gets the short marker-fenced CodeGraph block + // (#704): subagents and non-MCP harnesses read project rules but + // never the MCP initialize instructions. Upsert self-heals a + // stale pre-#529 block. + files.push(upsertInstructionsEntry(instructionsPath(loc))); + + const notes = [ + 'Start a new Grok session (or press r in /mcps) for MCP changes to take effect.', + ]; + if (loc === 'local') notes.push(trustNote()); + return { files, notes }; + } + + uninstall(loc: Location): WriteResult { + const files: WriteResult['files'] = []; + + const tomlPath = tomlConfigPath(loc); + if (fs.existsSync(tomlPath)) { + const content = fs.readFileSync(tomlPath, 'utf-8'); + const { content: nextContent, action } = removeTomlTable(content, TOML_HEADER); + if (action === 'removed') { + if (nextContent.trim() === '') { + try { fs.unlinkSync(tomlPath); } catch { /* ignore */ } + } else { + atomicWriteFileSync(tomlPath, nextContent.trimEnd() + '\n'); + } + files.push({ path: tomlPath, action: 'removed' }); + } else { + files.push({ path: tomlPath, action: 'not-found' }); + } + } else { + files.push({ path: tomlPath, action: 'not-found' }); + } + + files.push(removeInstructionsEntry(loc)); + + return { files }; + } + + printConfig(loc: Location): string { + const block = buildCodegraphBlock(); + return `# Add to ${tomlConfigPath(loc)}\n\n${block}\n`; + } + + describePaths(loc: Location): string[] { + return [tomlConfigPath(loc), instructionsPath(loc)]; + } +} + +function buildCodegraphBlock(): string { + const mcp = getMcpServerConfig(); + return buildTomlTable(TOML_HEADER, { + command: mcp.command, + args: mcp.args, + }); +} + +function writeMcpEntry(loc: Location): WriteResult['files'][number] { + const file = tomlConfigPath(loc); + const dir = path.dirname(file); + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + + const block = buildCodegraphBlock(); + // Single read — `existing === ''` derives both "is the file empty + // or absent" and "what was its content," avoiding a TOCTOU window + // between two `fs.existsSync` calls. + const existing = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : ''; + const created = existing.length === 0; + const { content: nextContent, action } = upsertTomlTable(existing, TOML_HEADER, block); + + if (action === 'unchanged') { + return { path: file, action: 'unchanged' }; + } + atomicWriteFileSync(file, nextContent); + return { path: file, action: created ? 'created' : 'updated' }; +} + +/** + * Strip the marker-delimited CodeGraph block from this location's + * rules/codegraph.md if a prior install wrote one. + */ +function removeInstructionsEntry(loc: Location): WriteResult['files'][number] { + const file = instructionsPath(loc); + const action = removeMarkedSection(file, CODEGRAPH_SECTION_START, CODEGRAPH_SECTION_END); + return { path: file, action }; +} + +export const grokTarget: AgentTarget = new GrokTarget(); diff --git a/src/installer/targets/registry.ts b/src/installer/targets/registry.ts index 3798b39ad..dc1604601 100644 --- a/src/installer/targets/registry.ts +++ b/src/installer/targets/registry.ts @@ -16,6 +16,7 @@ import { hermesTarget } from './hermes'; import { geminiTarget } from './gemini'; import { antigravityTarget } from './antigravity'; import { kiroTarget } from './kiro'; +import { grokTarget } from './grok'; import { copilotVscodeTarget } from './copilot-vscode'; import { copilotCliTarget } from './copilot-cli'; import { copilotJetbrainsTarget } from './copilot-jetbrains'; @@ -29,6 +30,7 @@ export const ALL_TARGETS: readonly AgentTarget[] = Object.freeze([ geminiTarget, antigravityTarget, kiroTarget, + grokTarget, copilotVscodeTarget, copilotCliTarget, copilotJetbrainsTarget, diff --git a/src/installer/targets/toml.ts b/src/installer/targets/toml.ts index 1dc086bf3..9fd77b87c 100644 --- a/src/installer/targets/toml.ts +++ b/src/installer/targets/toml.ts @@ -1,7 +1,7 @@ /** * Tiny TOML helpers — just enough to inject / replace / remove a * single dotted-key table block (`[mcp_servers.codegraph]`) inside an - * existing `~/.codex/config.toml`. We deliberately do NOT try to be a + * existing Codex or Grok `config.toml`. We deliberately do NOT try to be a * general TOML parser/serializer; that would mean pulling in a * dependency (~50KB) for ~6 lines of output. * diff --git a/src/installer/targets/types.ts b/src/installer/targets/types.ts index d93680573..e5b6e9b47 100644 --- a/src/installer/targets/types.ts +++ b/src/installer/targets/types.ts @@ -19,7 +19,7 @@ export type Location = 'global' | 'local'; * lookup. New targets add a value here when they're added to the * registry. Keep these short and lowercase. */ -export type TargetId = 'claude' | 'cursor' | 'codex' | 'opencode' | 'hermes' | 'gemini' | 'antigravity' | 'kiro' | 'copilot-vscode' | 'copilot-cli' | 'copilot-jetbrains'; +export type TargetId = 'claude' | 'cursor' | 'codex' | 'opencode' | 'hermes' | 'gemini' | 'antigravity' | 'kiro' | 'grok' | 'copilot-vscode' | 'copilot-cli' | 'copilot-jetbrains'; /** * Result of `target.detect(location)`.