fix: Use bare codegraph everywhere, add preuninstall cleanup
- Revert configs (MCP, hooks) to use bare `codegraph` command - Remove all npx references from configs and messaging - Add preuninstall script that runs on `npm uninstall -g` to clean up MCP server, permissions, hooks, and CLAUDE.md section - Show uninstall instructions in post-install next steps
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* CodeGraph preuninstall cleanup script
|
||||
*
|
||||
* Runs automatically when `npm uninstall -g @colbymchenry/codegraph` is called.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
|
||||
const CODEGRAPH_SECTION_START = '<!-- CODEGRAPH_START -->';
|
||||
const CODEGRAPH_SECTION_END = '<!-- CODEGRAPH_END -->';
|
||||
|
||||
function readJson(filePath: string): Record<string, any> | null {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJson(filePath: string, data: Record<string, any>): void {
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove CodeGraph MCP server from ~/.claude.json
|
||||
*/
|
||||
function removeMcpConfig(): void {
|
||||
const filePath = path.join(os.homedir(), '.claude.json');
|
||||
const config = readJson(filePath);
|
||||
if (!config?.mcpServers?.codegraph) return;
|
||||
|
||||
delete config.mcpServers.codegraph;
|
||||
|
||||
// Clean up empty mcpServers object
|
||||
if (Object.keys(config.mcpServers).length === 0) {
|
||||
delete config.mcpServers;
|
||||
}
|
||||
|
||||
writeJson(filePath, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove CodeGraph permissions and hooks 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;
|
||||
|
||||
// Clean up empty allow array
|
||||
if (settings.permissions.allow.length === 0) {
|
||||
delete settings.permissions.allow;
|
||||
}
|
||||
// Clean up empty permissions object
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove CodeGraph section from ~/.claude/CLAUDE.md
|
||||
*/
|
||||
function removeClaudeMd(): void {
|
||||
const filePath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return;
|
||||
let content = fs.readFileSync(filePath, 'utf-8');
|
||||
|
||||
// Remove marked section
|
||||
const startIdx = content.indexOf(CODEGRAPH_SECTION_START);
|
||||
const endIdx = content.indexOf(CODEGRAPH_SECTION_END);
|
||||
|
||||
if (startIdx !== -1 && endIdx > startIdx) {
|
||||
const before = content.substring(0, startIdx).trimEnd();
|
||||
const after = content.substring(endIdx + CODEGRAPH_SECTION_END.length).trimStart();
|
||||
content = before + (before && after ? '\n\n' : '') + after;
|
||||
|
||||
if (content.trim() === '') {
|
||||
// File is empty after removing section — delete it
|
||||
fs.unlinkSync(filePath);
|
||||
} else {
|
||||
fs.writeFileSync(filePath, content.trim() + '\n');
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Never fail
|
||||
}
|
||||
}
|
||||
|
||||
// Run cleanup — never throw
|
||||
try {
|
||||
removeMcpConfig();
|
||||
} catch { /* ignore */ }
|
||||
|
||||
try {
|
||||
removeSettings();
|
||||
} catch { /* ignore */ }
|
||||
|
||||
try {
|
||||
removeClaudeMd();
|
||||
} catch { /* ignore */ }
|
||||
Reference in New Issue
Block a user