Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# CodeGraph

### Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, and Kiro with Semantic Code Intelligence
### Supercharge Claude Code, CodeBuddy, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, and Claude with Semantic Code Intelligence

**~16% cheaper · ~58% fewer tool calls · 100% local**

Expand All @@ -17,6 +17,7 @@
[![Linux](https://img.shields.io/badge/Linux-supported-blue.svg)](#supported-platforms)

[![Claude Code](https://img.shields.io/badge/Claude_Code-supported-blueviolet.svg)](#supported-agents)
[![CodeBuddy](https://img.shields.io/badge/CodeBuddy-supported-blueviolet.svg)](#supported-agents)
[![Cursor](https://img.shields.io/badge/Cursor-supported-blueviolet.svg)](#supported-agents)
[![Codex](https://img.shields.io/badge/Codex-supported-blueviolet.svg)](#supported-agents)
[![opencode](https://img.shields.io/badge/opencode-supported-blueviolet.svg)](#supported-agents)
Expand Down Expand Up @@ -601,6 +602,7 @@ the MCP server (which delivers its own usage guidance, so no instructions file
is written):

- **Claude Code**
- **CodeBuddy**
- **Cursor**
- **Codex CLI**
- **opencode**
Expand Down
4 changes: 2 additions & 2 deletions site/src/content/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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**, **CodeBuddy**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, and **Kiro**.
- 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 an instructions file (e.g. `CLAUDE.md`, `.cursor/rules/codegraph.mdc`, `~/.codex/AGENTS.md`).
Expand All @@ -37,7 +37,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 / CodeBuddy / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro) for the MCP server to load.

## 3. Initialize projects

Expand Down
2 changes: 1 addition & 1 deletion site/src/content/docs/getting-started/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: What CodeGraph is, and why it makes AI coding agents faster and che

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, CodeBuddy, 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.

## Why it matters

Expand Down
2 changes: 1 addition & 1 deletion site/src/content/docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ npx @colbymchenry/codegraph # zero-install, or:
npm i -g @colbymchenry/codegraph
```

CodeGraph bundles its own runtime — nothing to compile, no native build, works the same everywhere. The interactive installer auto-configures your agent(s) — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro.
CodeGraph bundles its own runtime — nothing to compile, no native build, works the same everywhere. The interactive installer auto-configures your agent(s) — Claude Code, CodeBuddy, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro.

## Initialize Projects

Expand Down
2 changes: 1 addition & 1 deletion site/src/content/docs/guides/indexing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, CodeBuddy, 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.

### 1. File watcher with debounced auto-sync (always on)

Expand Down
37 changes: 37 additions & 0 deletions site/src/content/docs/reference/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The interactive installer auto-detects and configures each supported agent — w
## Supported agents

- **Claude Code**
- **CodeBuddy**
- **Cursor**
- **Codex CLI**
- **opencode**
Expand All @@ -26,6 +27,8 @@ If you'd rather wire it up yourself, install globally:
npm install -g @colbymchenry/codegraph
```

### Claude Code

Add the MCP server to `~/.claude.json`:

```json
Expand Down Expand Up @@ -59,6 +62,40 @@ Optionally auto-allow the read-only tools in `~/.claude/settings.json`:
}
```

### CodeBuddy

Add the MCP server to `~/.codebuddy/mcp.json`:

```json
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
}
}
```

Optionally auto-allow the read-only tools in `~/.codebuddy/settings.json`:

```json
{
"permissions": {
"allow": [
"mcp__codegraph__codegraph_search",
"mcp__codegraph__codegraph_context",
"mcp__codegraph__codegraph_callers",
"mcp__codegraph__codegraph_callees",
"mcp__codegraph__codegraph_impact",
"mcp__codegraph__codegraph_node",
"mcp__codegraph__codegraph_status"
]
}
}
```

:::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.
:::
2 changes: 1 addition & 1 deletion src/installer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ export async function runUninstaller(opts: RunUninstallerOptions): Promise<void>
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' },
{ value: 'global' as const, label: 'All projects (global)', hint: '~/.claude, ~/.codebuddy, ~/.cursor, ~/.codex, ~/.config/opencode, ~/.hermes, ~/.gemini, ~/.kiro' },
{ value: 'local' as const, label: 'Just this project (local)', hint: './.claude, ./.cursor, ./opencode.jsonc, ./.gemini, ./.kiro' },
],
initialValue: 'global' as const,
Expand Down
244 changes: 244 additions & 0 deletions src/installer/targets/codebuddy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/**
* CodeBuddy target. Writes:
*
* - MCP server entry to `~/.codebuddy/mcp.json` (global = user scope,
* loads in every project) or `./.mcp.json` (local = project scope).
* - Permissions to `~/.codebuddy/settings.json` (global) or
* `./.codebuddy/settings.json` (local), gated on `autoAllow`.
* - Instructions to `~/.codebuddy/CODEBUDDY.md` (global) or
* `./.codebuddy/CODEBUDDY.md` (local).
*
* CodeBuddy follows the same config layout as Claude Code, with
* `.codebuddy` replacing `.claude` and `CODEBUDDY.md` replacing
* `CLAUDE.md`. The global MCP config lives inside `~/.codebuddy/`
* (not as a top-level dotfile).
*
* NOTE — why CODEBUDDY.md is still written (unlike Claude Code):
* CodeBuddy Code does not surface the MCP `initialize` response's
* `instructions` field to the model. Claude Code / Codex / Gemini /
* Cursor all receive the SERVER_INSTRUCTIONS playbook automatically
* via that field (issue #529 removed the MD writes for those targets).
* Until CodeBuddy supports `initialize` instructions natively, we must
* keep writing to CODEBUDDY.md — when that support lands, apply the
* same #529 migration pattern: drop `writeInstructionsEntry` from
* `install()` and add a self-healing `removeInstructionsEntry` cleanup.
*/

import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import {
AgentTarget,
DetectionResult,
InstallOptions,
Location,
WriteResult,
} from './types';
import {
getCodeGraphPermissions,
getMcpServerConfig,
jsonDeepEqual,
readJsonFile,
removeMarkedSection,
replaceOrAppendMarkedSection,
writeJsonFile,
} from './shared';
import {
CODEGRAPH_SECTION_END,
CODEGRAPH_SECTION_START,
} from '../instructions-template';
import { SERVER_INSTRUCTIONS } from '../../mcp/server-instructions';

function configDir(loc: Location): string {
return loc === 'global'
? path.join(os.homedir(), '.codebuddy')
: path.join(process.cwd(), '.codebuddy');
}
function mcpJsonPath(loc: Location): string {
// global → ~/.codebuddy/mcp.json (user scope: visible in every project).
// local → ./.mcp.json (project scope).
return loc === 'global'
? path.join(os.homedir(), '.codebuddy', 'mcp.json')
: path.join(process.cwd(), '.mcp.json');
}
function settingsJsonPath(loc: Location): string {
return path.join(configDir(loc), 'settings.json');
}
function instructionsPath(loc: Location): string {
return path.join(configDir(loc), 'CODEBUDDY.md');
}

class CodeBuddyTarget implements AgentTarget {
readonly id = 'codebuddy' as const;
readonly displayName = 'CodeBuddy';
readonly docsUrl = 'https://cnb.cool/codebuddy/codebuddy-code';

supportsLocation(_loc: Location): boolean {
return true;
}

detect(loc: Location): DetectionResult {
const mcpPath = mcpJsonPath(loc);
const config = readJsonFile(mcpPath);
const alreadyConfigured = !!config.mcpServers?.codegraph;
const installed = loc === 'global'
? fs.existsSync(configDir(loc)) || fs.existsSync(mcpPath)
: fs.existsSync(mcpPath) || fs.existsSync(configDir(loc));
return { installed, alreadyConfigured, configPath: mcpPath };
}

install(loc: Location, opts: InstallOptions): WriteResult {
const files: WriteResult['files'] = [];

// 1. MCP server entry
files.push(writeMcpEntry(loc));

// 2. Permissions (only when autoAllow)
if (opts.autoAllow) {
files.push(writePermissionsEntry(loc));
}

// 3. CODEBUDDY.md instructions
//
// CodeBuddy Code does NOT surface the MCP `initialize` response's
// `instructions` field to the model, so the technique used for
// Claude Code / Codex / Gemini / Cursor (issue #529) does not work
// here. Those agents receive SERVER_INSTRUCTIONS automatically on
// every session via the MCP handshake; CodeBuddy silently discards
// that field today.
//
// As a result we must write the SERVER_INSTRUCTIONS playbook directly
// into CODEBUDDY.md so the agent knows to prefer codegraph tools over
// grep/find/Read exploration.
//
// If CodeBuddy adds first-class support for the MCP `initialize`
// instructions field in the future, this target should be updated to
// drop the CODEBUDDY.md write (and add a self-healing cleanup step,
// the same pattern used in the #529 migration for the other targets).
files.push(writeInstructionsEntry(loc));

return { files };
}

uninstall(loc: Location): WriteResult {
const files: WriteResult['files'] = [];

// 1. MCP server entry
const mcpPath = mcpJsonPath(loc);
const config = readJsonFile(mcpPath);
if (config.mcpServers?.codegraph) {
delete config.mcpServers.codegraph;
if (Object.keys(config.mcpServers).length === 0) {
delete config.mcpServers;
}
writeJsonFile(mcpPath, config);
files.push({ path: mcpPath, action: 'removed' });
} else {
files.push({ path: mcpPath, action: 'not-found' });
}

// 2. Permissions
const settingsPath = settingsJsonPath(loc);
const settings = readJsonFile(settingsPath);
if (Array.isArray(settings.permissions?.allow)) {
const before = settings.permissions.allow.length;
settings.permissions.allow = settings.permissions.allow.filter(
(p: string) => !p.startsWith('mcp__codegraph__'),
);
if (settings.permissions.allow.length !== before) {
if (settings.permissions.allow.length === 0) {
delete settings.permissions.allow;
}
if (Object.keys(settings.permissions).length === 0) {
delete settings.permissions;
}
writeJsonFile(settingsPath, settings);
files.push({ path: settingsPath, action: 'removed' });
} else {
files.push({ path: settingsPath, action: 'not-found' });
}
} else {
files.push({ path: settingsPath, action: 'not-found' });
}

// 3. Instructions — strip the legacy CodeGraph block if present.
files.push(removeInstructionsEntry(loc));

return { files };
}

printConfig(loc: Location): string {
const target = mcpJsonPath(loc);
const snippet = JSON.stringify({ mcpServers: { codegraph: getMcpServerConfig() } }, null, 2);
return `# Add to ${target}\n\n${snippet}\n`;
}

describePaths(loc: Location): string[] {
return [mcpJsonPath(loc), settingsJsonPath(loc), instructionsPath(loc)];
}
}

export function writeMcpEntry(loc: Location): WriteResult['files'][number] {
const file = mcpJsonPath(loc);
const existing = readJsonFile(file);
const before = existing.mcpServers?.codegraph;
const after = getMcpServerConfig();

if (jsonDeepEqual(before, after)) {
return { path: file, action: 'unchanged' };
}
const action: 'created' | 'updated' = before ? 'updated' : (fs.existsSync(file) ? 'updated' : 'created');
if (!existing.mcpServers) existing.mcpServers = {};
existing.mcpServers.codegraph = after;
writeJsonFile(file, existing);
return { path: file, action };
}

export function writePermissionsEntry(loc: Location): WriteResult['files'][number] {
const file = settingsJsonPath(loc);
const settings = readJsonFile(file);
const created = !fs.existsSync(file);

if (!settings.permissions) settings.permissions = {};
if (!Array.isArray(settings.permissions.allow)) settings.permissions.allow = [];

const want = getCodeGraphPermissions();
const before = [...settings.permissions.allow];
for (const perm of want) {
if (!settings.permissions.allow.includes(perm)) {
settings.permissions.allow.push(perm);
}
}
if (jsonDeepEqual(before, settings.permissions.allow) && !created) {
return { path: file, action: 'unchanged' };
}
writeJsonFile(file, settings);
return { path: file, action: created ? 'created' : 'updated' };
}

function codeGraphInstructionsBody(): string {
return (
CODEGRAPH_SECTION_START + '\n' +
SERVER_INSTRUCTIONS + '\n' +
CODEGRAPH_SECTION_END
);
}

export function writeInstructionsEntry(loc: Location): WriteResult['files'][number] {
const file = instructionsPath(loc);
const body = codeGraphInstructionsBody();
const raw = replaceOrAppendMarkedSection(file, body, CODEGRAPH_SECTION_START, CODEGRAPH_SECTION_END);
// `appended` means the markers weren't found and the section was added
// at the end — map it to `updated` for the installer log line.
const action: WriteResult['files'][number]['action'] =
raw === 'appended' ? 'updated' : raw;
return { path: file, action };
}

export 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 codebuddyTarget: AgentTarget = new CodeBuddyTarget();
2 changes: 2 additions & 0 deletions src/installer/targets/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import { AgentTarget, Location, TargetId } from './types';
import { claudeTarget } from './claude';
import { codebuddyTarget } from './codebuddy';
import { cursorTarget } from './cursor';
import { codexTarget } from './codex';
import { opencodeTarget } from './opencode';
Expand All @@ -19,6 +20,7 @@ import { kiroTarget } from './kiro';

export const ALL_TARGETS: readonly AgentTarget[] = Object.freeze([
claudeTarget,
codebuddyTarget,
cursorTarget,
codexTarget,
opencodeTarget,
Expand Down
Loading