docs: Clean up README formatting and remove deprecated CLI hook commands
Removes crystal ball emoji and bullet formatting inconsistencies from README headers. Eliminates mark-dirty and sync-if-dirty CLI commands and related hook configuration code, simplifying the codebase after transitioning to file watcher-based auto-sync.
This commit is contained in:
+1
-89
@@ -16,18 +16,12 @@
|
||||
* codegraph files [options] Show project file structure
|
||||
* codegraph context <task> 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]
|
||||
*/
|
||||
|
||||
+2
-31
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, any> {
|
||||
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
|
||||
|
||||
@@ -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<void> {
|
||||
|
||||
// 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<void> {
|
||||
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`;
|
||||
|
||||
Reference in New Issue
Block a user