` | Project path | auto-detect |
+| `-q, --quiet` | Output file paths only | `false` |
-**How it works:**
-
-1. For each changed file, BFS-traverses its transitive dependents (files that import from it, directly or indirectly)
-2. Filters results to test files using common conventions (`*.spec.*`, `*.test.*`, `e2e/`, `tests/`, `__tests__/`) or a custom `--filter` glob
-3. Changed files that are themselves test files are always included
-
-**Example: CI/hook integration**
+**CI/hook example:**
```bash
#!/usr/bin/env bash
-# In a pre-commit hook or CI step:
AFFECTED=$(git diff --name-only HEAD | codegraph affected --stdin --quiet)
if [ -n "$AFFECTED" ]; then
- echo "Running affected tests..."
npx vitest run $AFFECTED
fi
```
-### `codegraph serve`
+---
-Start CodeGraph as an MCP server for AI assistants.
+## MCP Tools
-```bash
-codegraph serve # Show MCP configuration help
-codegraph serve --mcp # Start MCP server (stdio)
-codegraph serve --mcp --path /project # Specify project path
-```
+When running as an MCP server, CodeGraph exposes these tools to Claude Code:
-## ๐ MCP Tools Reference
+| Tool | Purpose |
+|------|---------|
+| `codegraph_search` | Find symbols by name across the codebase |
+| `codegraph_context` | Build relevant code context for a task |
+| `codegraph_callers` | Find what calls a function |
+| `codegraph_callees` | Find what a function calls |
+| `codegraph_impact` | Analyze what code is affected by changing a symbol |
+| `codegraph_node` | Get details about a specific symbol (optionally with source code) |
+| `codegraph_files` | Get indexed file structure (faster than filesystem scanning) |
+| `codegraph_status` | Check index health and statistics |
-When running as an MCP server, CodeGraph exposes these tools to AI assistants. **These tools are designed to be used by Claude's Explore agents** for faster, more efficient codebase exploration.
+---
-### `codegraph_context`
-
-Build context for a specific task. Good for focused queries.
-
-```
-codegraph_context(task: "fix checkout validation bug", maxNodes: 20)
-```
-
-### `codegraph_search`
-
-Quick symbol search by name. Returns locations only.
-
-```
-codegraph_search(query: "UserService", kind: "class", limit: 10)
-```
-
-### `codegraph_callers` / `codegraph_callees`
-
-Find what calls a function, or what a function calls.
-
-```
-codegraph_callers(symbol: "validatePayment", limit: 20)
-codegraph_callees(symbol: "processOrder", limit: 20)
-```
-
-### `codegraph_impact`
-
-Analyze what code would be affected by changing a symbol.
-
-```
-codegraph_impact(symbol: "UserService", depth: 2)
-```
-
-### `codegraph_node`
-
-Get details about a specific symbol. Use `includeCode: true` only when needed.
-
-```
-codegraph_node(symbol: "authenticate", includeCode: true)
-```
-
-### `codegraph_files`
-
-Get the project file structure from the index. Faster than filesystem scanning.
-
-```
-codegraph_files(path: "src/components", format: "tree", includeMetadata: true)
-```
-
-### `codegraph_status`
-
-Check index health and statistics.
-
-### How It Works With Claude Code
-
-Claude's **Explore agents** use these tools instead of grep/glob/Read for faster exploration:
-
-| Without CodeGraph | With CodeGraph | Benefit |
-|-------------------|----------------|---------|
-| `grep -r "auth"` | `codegraph_search("auth")` | Instant symbol lookup |
-| Multiple `Read` calls | `codegraph_context(task)` | Related code in one call |
-| Manual file tracing | `codegraph_callers/callees` | Call graph traversal |
-| Guessing impact | `codegraph_impact(symbol)` | Know what breaks |
-| `Glob`/`find` scanning | `codegraph_files(path)` | Indexed file structure |
-
-This gives Explore agents **~94% fewer tool calls** and **~77% faster exploration** while producing equally thorough answers.
-
-## ๐ Library Usage
-
-CodeGraph can also be used as a library in your Node.js applications:
+## Library Usage
```typescript
import CodeGraph from '@colbymchenry/codegraph';
-// Initialize a new project
const cg = await CodeGraph.init('/path/to/project');
+// Or: const cg = await CodeGraph.open('/path/to/project');
-// Or open an existing one
-const cg = await CodeGraph.open('/path/to/project');
-
-// Index with progress callback
await cg.indexAll({
- onProgress: (progress) => {
- console.log(`${progress.phase}: ${progress.current}/${progress.total}`);
- }
+ onProgress: (p) => console.log(`${p.phase}: ${p.current}/${p.total}`)
});
-// Search for symbols
const results = cg.searchNodes('UserService');
+const callers = cg.getCallers(results[0].node.id);
+const context = await cg.buildContext('fix login bug', { maxNodes: 20, includeCode: true, format: 'markdown' });
+const impact = cg.getImpactRadius(results[0].node.id, 2);
-// Get callers of a function
-const node = results[0].node;
-const callers = cg.getCallers(node.id);
-
-// Build context for a task
-const context = await cg.buildContext('fix login bug', {
- maxNodes: 20,
- includeCode: true,
- format: 'markdown'
-});
-
-// Get impact radius
-const impact = cg.getImpactRadius(node.id, 2);
-
-// Sync changes manually
-const syncResult = await cg.sync();
-
-// Or watch for changes and auto-sync
-cg.watch(); // uses native OS file events, debounced
+cg.watch(); // auto-sync on file changes
cg.unwatch(); // stop watching
-
-// Clean up
cg.close();
```
-## โ๏ธ How It Works
+---
-### 1. Extraction
+## Configuration
-CodeGraph uses [tree-sitter](https://tree-sitter.github.io/) to parse source code into ASTs. Language-specific queries (`.scm` files) extract:
-
-- **Nodes**: Functions, methods, classes, interfaces, types, variables
-- **Edges**: Calls, imports, extends, implements, returns_type
-
-Each node gets a unique ID based on its kind, file path, name, and line number.
-
-### 2. Storage
-
-All data is stored in a local SQLite database (`.codegraph/codegraph.db`):
-
-- **nodes** table: All code entities with metadata
-- **edges** table: Relationships between nodes
-- **files** table: File tracking for incremental updates
-- **unresolved_refs** table: References pending resolution
-- **nodes_fts**: FTS5 virtual table for full-text search
-- **schema_versions** table: Schema version tracking
-- **project_metadata** table: Project-level key-value metadata
-
-### 3. Reference Resolution
-
-After extraction, CodeGraph resolves references:
-
-1. Match function calls to function definitions
-2. Resolve imports to their source files
-3. Link class inheritance and interface implementations
-4. Apply framework-specific patterns (Express routes, etc.)
-
-### 4. File Watching
-
-The MCP server automatically watches your project for file changes using native OS file events (FSEvents on macOS, inotify on Linux, ReadDirectoryChangesW on Windows):
-
-1. File saves are detected instantly via OS-level events โ no polling
-2. Changes are **debounced** (2-second quiet window) so rapid saves don't thrash
-3. Only source files matching your include/exclude patterns trigger a sync
-4. Build outputs, node_modules, and `.codegraph/` changes are ignored
-5. Incremental sync runs automatically โ only changed files are re-parsed
-
-No configuration needed. The graph stays fresh as you code.
-
-### 5. Graph Queries
-
-The graph structure enables powerful queries:
-
-- **Callers/Callees**: Direct call relationships
-- **Impact Radius**: BFS traversal to find all potentially affected code
-- **Dependencies**: What a symbol depends on
-- **Dependents**: What depends on a symbol
-
-### 6. Context Building
-
-When you request context for a task:
-
-1. FTS search finds relevant entry points
-2. Graph traversal expands to related code
-3. Code snippets are extracted
-4. Results are formatted for AI consumption
-
-## โ๏ธ Configuration
-
-The `.codegraph/config.json` file controls indexing behavior:
+The `.codegraph/config.json` file controls indexing:
```json
{
"version": 1,
"languages": ["typescript", "javascript"],
- "exclude": [
- "node_modules/**",
- "dist/**",
- "build/**",
- "*.min.js"
- ],
+ "exclude": ["node_modules/**", "dist/**", "build/**", "*.min.js"],
"frameworks": [],
"maxFileSize": 1048576,
"extractDocstrings": true,
@@ -681,18 +369,16 @@ The `.codegraph/config.json` file controls indexing behavior:
}
```
-### Options
-
| Option | Description | Default |
|--------|-------------|---------|
| `languages` | Languages to index (auto-detected if empty) | `[]` |
| `exclude` | Glob patterns to ignore | `["node_modules/**", ...]` |
| `frameworks` | Framework hints for better resolution | `[]` |
| `maxFileSize` | Skip files larger than this (bytes) | `1048576` (1MB) |
-| `extractDocstrings` | Whether to extract docstrings from code | `true` |
-| `trackCallSites` | Whether to track call site locations | `true` |
+| `extractDocstrings` | Extract docstrings from code | `true` |
+| `trackCallSites` | Track call site locations | `true` |
-## ๐ Supported Languages
+## Supported Languages
| Language | Extension | Status |
|----------|-----------|--------|
@@ -714,33 +400,17 @@ The `.codegraph/config.json` file controls indexing behavior:
| Liquid | `.liquid` | Full support |
| Pascal / Delphi | `.pas`, `.dpr`, `.dpk`, `.lpr` | Full support (classes, records, interfaces, enums, DFM/FMX form files) |
-## ๐ง Troubleshooting
+## Troubleshooting
-### "CodeGraph not initialized"
+**"CodeGraph not initialized"** โ Run `codegraph init` in your project directory first.
-Run `codegraph init` in your project directory first.
+**Indexing is slow** โ Check that `node_modules` and other large directories are excluded. Use `--quiet` to reduce output overhead.
-### Indexing is slow
+**MCP server not connecting** โ Ensure the project is initialized/indexed, verify the path in your MCP config, and check that `codegraph serve --mcp` works from the command line.
-- Check if `node_modules` or other large directories are excluded
-- Use `--quiet` flag to reduce console output overhead
-- Consider increasing `maxFileSize` if you have large files to skip
+**Missing symbols** โ The MCP server auto-syncs on save (wait a couple seconds). Run `codegraph sync` manually if needed. Check that the file's language is supported and isn't excluded by config patterns.
-### MCP server not connecting
-
-1. Ensure the project is initialized and indexed
-2. Check the path in your MCP configuration is correct
-3. Verify `codegraph serve --mcp` works from the command line
-4. Check Claude Code logs for connection errors
-
-### Missing symbols in search
-
-- The MCP server auto-syncs on file changes โ wait a couple seconds after saving
-- Run `codegraph sync` manually if needed
-- Check if the file's language is supported
-- Verify the file isn't excluded by config patterns
-
-## ๐ License
+## License
MIT
@@ -748,7 +418,7 @@ MIT
-**Made for the Claude Code community** ๐ค
+**Made for the Claude Code community**
[Report Bug](https://github.com/colbymchenry/codegraph/issues) ยท [Request Feature](https://github.com/colbymchenry/codegraph/issues)
diff --git a/src/bin/codegraph.ts b/src/bin/codegraph.ts
index eb508b8..d118a1f 100644
--- a/src/bin/codegraph.ts
+++ b/src/bin/codegraph.ts
@@ -16,18 +16,12 @@
* codegraph files [options] Show project file structure
* codegraph context Build context for a task
* codegraph affected [files] Find test files affected by changes
- * codegraph mark-dirty [path] Mark project as needing sync (hooks)
- * codegraph sync-if-dirty [path] Sync if marked dirty (hooks)
- *
- * Note: Git hooks have been removed. CodeGraph sync is triggered automatically
- * through codegraph's Claude Code hooks integration.
*/
import { Command } from 'commander';
import * as path from 'path';
import * as fs from 'fs';
-import { spawn } from 'child_process';
-import { getCodeGraphDir, findNearestCodeGraphRoot, isInitialized } from '../directory';
+import { getCodeGraphDir, isInitialized } from '../directory';
import { createShimmerProgress } from '../ui/shimmer-progress';
// Lazy-load heavy modules (CodeGraph, runInstaller) to keep CLI startup fast.
@@ -1078,88 +1072,6 @@ program
}
});
-/**
- * codegraph mark-dirty [path]
- *
- * Touches .codegraph/.dirty to signal that files have changed.
- * Used by Claude Code PostToolUse hooks to batch syncs.
- * Runs silently and always exits 0.
- */
-program
- .command('mark-dirty [path]')
- .description('Mark project as needing sync (used by Claude Code hooks)')
- .action(async (pathArg: string | undefined) => {
- try {
- const startPath = path.resolve(pathArg || process.cwd());
- const projectRoot = findNearestCodeGraphRoot(startPath);
- if (!projectRoot) {
- // No .codegraph/ found โ exit silently
- process.exit(0);
- }
- const dirtyPath = path.join(getCodeGraphDir(projectRoot), '.dirty');
- fs.writeFileSync(dirtyPath, Date.now().toString(), 'utf-8');
- } catch {
- // Never fail โ this runs in the background during edits
- }
- process.exit(0);
- });
-
-/**
- * codegraph sync-if-dirty [path]
- *
- * Checks if .codegraph/.dirty exists and, if so, spawns a detached
- * background process to run `codegraph sync`. The hook process exits
- * immediately so Claude Code's Stop hook never blocks.
- *
- * Removes the marker BEFORE spawning so edits during sync
- * create a new marker for the next Stop event.
- * Runs silently and always exits 0.
- */
-program
- .command('sync-if-dirty [path]')
- .description('Sync if project was marked dirty (used by Claude Code hooks)')
- .action(async (pathArg: string | undefined) => {
- try {
- const startPath = path.resolve(pathArg || process.cwd());
- const projectRoot = findNearestCodeGraphRoot(startPath);
- if (!projectRoot) {
- process.exit(0);
- }
- const dirtyPath = path.join(getCodeGraphDir(projectRoot!), '.dirty');
-
- // No marker โ nothing to do (sub-ms exit)
- if (!fs.existsSync(dirtyPath)) {
- process.exit(0);
- }
-
- // Remove marker FIRST so edits during sync create a new one
- try { fs.unlinkSync(dirtyPath); } catch { /* ignore */ }
-
- // If not fully initialized (no DB), exit
- if (!isInitialized(projectRoot!)) {
- process.exit(0);
- }
-
- // Spawn sync as a detached background process
- // so this hook exits immediately and doesn't block Claude Code.
- // Uses process.argv[0]/[1] (e.g. node /path/to/codegraph.js) so it
- // works whether invoked via global install, npx, or directly.
- const child = spawn(
- process.argv[0]!,
- [process.argv[1]!, 'sync', '--quiet', projectRoot!],
- {
- detached: true,
- stdio: 'ignore',
- windowsHide: true,
- }
- );
- child.unref();
- } catch {
- // Never fail โ this runs at the end of Claude responses
- }
- process.exit(0);
- });
-
/**
* codegraph unlock [path]
*/
diff --git a/src/bin/uninstall.ts b/src/bin/uninstall.ts
index 8ae3d13..4344a04 100644
--- a/src/bin/uninstall.ts
+++ b/src/bin/uninstall.ts
@@ -6,7 +6,6 @@
* Removes all CodeGraph configuration from Claude Code:
* - MCP server entry from ~/.claude.json
* - Permissions from ~/.claude/settings.json
- * - Hooks from ~/.claude/settings.json
* - CodeGraph section from ~/.claude/CLAUDE.md
*
* This script must never throw โ a failed cleanup must not block uninstall.
@@ -51,22 +50,20 @@ function removeMcpConfig(): void {
}
/**
- * Remove CodeGraph permissions and hooks from ~/.claude/settings.json
+ * Remove CodeGraph permissions from ~/.claude/settings.json
*/
function removeSettings(): void {
const filePath = path.join(os.homedir(), '.claude', 'settings.json');
const settings = readJson(filePath);
if (!settings) return;
- let changed = false;
-
// Remove codegraph permissions
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) changed = true;
+ if (settings.permissions.allow.length === before) return;
// Clean up empty allow array
if (settings.permissions.allow.length === 0) {
@@ -76,33 +73,7 @@ function removeSettings(): void {
if (Object.keys(settings.permissions).length === 0) {
delete settings.permissions;
}
- }
- // Remove codegraph hooks
- if (settings.hooks) {
- for (const event of Object.keys(settings.hooks)) {
- if (!Array.isArray(settings.hooks[event])) continue;
-
- const before = settings.hooks[event].length;
- settings.hooks[event] = settings.hooks[event].filter((entry: any) => {
- const json = JSON.stringify(entry);
- return !json.includes('codegraph mark-dirty') && !json.includes('codegraph sync-if-dirty');
- });
- if (settings.hooks[event].length !== before) changed = true;
-
- // Clean up empty event arrays
- if (settings.hooks[event].length === 0) {
- delete settings.hooks[event];
- }
- }
-
- // Clean up empty hooks object
- if (Object.keys(settings.hooks).length === 0) {
- delete settings.hooks;
- }
- }
-
- if (changed) {
writeJson(filePath, settings);
}
}
diff --git a/src/installer/config-writer.ts b/src/installer/config-writer.ts
index b0bf19a..5e01990 100644
--- a/src/installer/config-writer.ts
+++ b/src/installer/config-writer.ts
@@ -192,94 +192,6 @@ export function hasPermissions(location: InstallLocation): boolean {
return permissions.some((p: string) => p.startsWith('mcp__codegraph__'));
}
-// =============================================================================
-// Hooks Configuration
-// =============================================================================
-
-/**
- * Get the hooks configuration for Claude Code auto-sync.
- *
- * PostToolUse(Edit|Write) โ mark-dirty (async, non-blocking)
- * Stop โ sync-if-dirty (sync, ensures fresh index before next user turn)
- */
-function getHooksConfig(): Record {
- const command = 'codegraph';
-
- return {
- PostToolUse: [
- {
- matcher: 'Edit|Write',
- hooks: [
- {
- type: 'command',
- command: `${command} mark-dirty`,
- async: true,
- },
- ],
- },
- ],
- Stop: [
- {
- matcher: '.*',
- hooks: [
- {
- type: 'command',
- command: `${command} sync-if-dirty`,
- },
- ],
- },
- ],
- };
-}
-
-/**
- * Check if Claude Code hooks already exist for CodeGraph
- */
-export function hasHooks(location: InstallLocation): boolean {
- const settingsPath = getSettingsJsonPath(location);
- const settings = readJsonFile(settingsPath);
- const hooks = settings.hooks;
- if (!hooks) return false;
-
- // Check if any hook command references codegraph
- const json = JSON.stringify(hooks);
- return json.includes('codegraph mark-dirty') || json.includes('codegraph sync-if-dirty');
-}
-
-/**
- * Write Claude Code hooks to settings.json for auto-sync.
- * Merges with existing hooks, deduplicating any previous codegraph entries.
- */
-export function writeHooks(location: InstallLocation): void {
- const settingsPath = getSettingsJsonPath(location);
- const settings = readJsonFile(settingsPath);
-
- if (!settings.hooks) {
- settings.hooks = {};
- }
-
- const newHooks = getHooksConfig();
-
- // For each hook event (PostToolUse, Stop), merge with existing entries
- for (const [event, newEntries] of Object.entries(newHooks)) {
- if (!Array.isArray(settings.hooks[event])) {
- settings.hooks[event] = [];
- }
-
- // Remove any existing codegraph entries for this event
- settings.hooks[event] = (settings.hooks[event] as any[]).filter((entry: any) => {
- // Keep entries that don't reference codegraph
- const entryJson = JSON.stringify(entry);
- return !entryJson.includes('codegraph mark-dirty') && !entryJson.includes('codegraph sync-if-dirty');
- });
-
- // Add new codegraph entries
- settings.hooks[event].push(...(newEntries as any[]));
- }
-
- writeJsonFile(settingsPath, settings);
-}
-
/**
* Get the path to CLAUDE.md
* - Global: ~/.claude/CLAUDE.md
diff --git a/src/installer/index.ts b/src/installer/index.ts
index 0e58d21..7d01af9 100644
--- a/src/installer/index.ts
+++ b/src/installer/index.ts
@@ -8,8 +8,8 @@ import { execSync } from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
import {
- writeMcpConfig, writePermissions, writeClaudeMd, writeHooks,
- hasMcpConfig, hasPermissions, hasHooks,
+ writeMcpConfig, writePermissions, writeClaudeMd,
+ hasMcpConfig, hasPermissions,
} from './config-writer';
import type { InstallLocation } from './config-writer';
@@ -50,7 +50,7 @@ export async function runInstaller(): Promise {
// Step 1: Install globally
const shouldInstallGlobally = await clack.confirm({
- message: 'Install codegraph globally? (Required for hooks & MCP server)',
+ message: 'Install codegraph globally? (Required for MCP server)',
initialValue: true,
});
@@ -70,7 +70,7 @@ export async function runInstaller(): Promise {
clack.log.warn('Try: sudo npm install -g @colbymchenry/codegraph');
}
} else {
- clack.log.info('Skipped global install โ hooks and MCP server may not work without it');
+ clack.log.info('Skipped global install โ MCP server may not work without it');
}
// Step 2: Installation location
@@ -140,11 +140,6 @@ function writeConfigs(
clack.log.success(`${permAction} permissions in ${locationLabel}/settings.json`);
}
- // Hooks
- const hookAction = hasHooks(location) ? 'Updated' : 'Added';
- writeHooks(location);
- clack.log.success(`${hookAction} auto-sync hooks in ${locationLabel}/settings.json`);
-
// CLAUDE.md
const claudeMdResult = writeClaudeMd(location);
const claudeMdPath = `${locationLabel}/CLAUDE.md`;