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:
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@colbymchenry/codegraph",
|
"name": "@colbymchenry/codegraph",
|
||||||
"version": "0.5.5",
|
"version": "0.6.0",
|
||||||
"description": "Supercharge Claude Code with semantic code intelligence. 30% fewer tokens, 25% fewer tool calls, 100% local.",
|
"description": "Supercharge Claude Code with semantic code intelligence. 30% fewer tokens, 25% fewer tool calls, 100% local.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && npm run copy-assets",
|
"build": "tsc && npm run copy-assets",
|
||||||
"postinstall": "node scripts/postinstall.js",
|
"postinstall": "node scripts/postinstall.js",
|
||||||
|
"preuninstall": "node dist/bin/uninstall.js",
|
||||||
"copy-assets": "node -e \"const fs=require('fs');fs.mkdirSync('dist/db',{recursive:true});fs.copyFileSync('src/db/schema.sql','dist/db/schema.sql');fs.mkdirSync('dist/extraction/wasm',{recursive:true});fs.readdirSync('src/extraction/wasm').filter(f=>f.endsWith('.wasm')).forEach(f=>fs.copyFileSync('src/extraction/wasm/'+f,'dist/extraction/wasm/'+f))\"",
|
"copy-assets": "node -e \"const fs=require('fs');fs.mkdirSync('dist/db',{recursive:true});fs.copyFileSync('src/db/schema.sql','dist/db/schema.sql');fs.mkdirSync('dist/extraction/wasm',{recursive:true});fs.readdirSync('src/extraction/wasm').filter(f=>f.endsWith('.wasm')).forEach(f=>fs.copyFileSync('src/extraction/wasm/'+f,'dist/extraction/wasm/'+f))\"",
|
||||||
"dev": "tsc --watch",
|
"dev": "tsc --watch",
|
||||||
"cli": "npm run build && node dist/bin/codegraph.js",
|
"cli": "npm run build && node dist/bin/codegraph.js",
|
||||||
|
|||||||
@@ -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 */ }
|
||||||
@@ -113,21 +113,18 @@ export function warn(message: string): void {
|
|||||||
/**
|
/**
|
||||||
* Show the "next steps" section after installation
|
* Show the "next steps" section after installation
|
||||||
*/
|
*/
|
||||||
export function showNextSteps(location: 'global' | 'local', codegraphAvailable?: boolean): void {
|
export function showNextSteps(location: 'global' | 'local'): void {
|
||||||
console.log();
|
console.log();
|
||||||
console.log(chalk.bold(' Done!') + ' Restart Claude Code to use CodeGraph.');
|
console.log(chalk.bold(' Done!') + ' Restart Claude Code to use CodeGraph.');
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
if (location === 'global') {
|
if (location === 'global') {
|
||||||
const cmd = codegraphAvailable ? 'codegraph' : 'npx @colbymchenry/codegraph';
|
|
||||||
console.log(chalk.dim(' Quick start:'));
|
console.log(chalk.dim(' Quick start:'));
|
||||||
console.log(chalk.dim(' cd your-project'));
|
console.log(chalk.dim(' cd your-project'));
|
||||||
console.log(chalk.cyan(` ${cmd} init -i`));
|
console.log(chalk.cyan(' codegraph init -i'));
|
||||||
if (!codegraphAvailable) {
|
|
||||||
console.log();
|
console.log();
|
||||||
console.log(chalk.dim(' Tip: For a shorter command, install globally:'));
|
console.log(chalk.dim(' To uninstall:'));
|
||||||
console.log(chalk.dim(' npm install -g @colbymchenry/codegraph'));
|
console.log(chalk.dim(' npm uninstall -g @colbymchenry/codegraph'));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.dim(' CodeGraph is ready to use in this project!'));
|
console.log(chalk.dim(' CodeGraph is ready to use in this project!'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,13 +98,13 @@ function writeJsonFile(filePath: string, data: Record<string, any>): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the MCP server configuration — always uses npx for reliability
|
* Get the MCP server configuration
|
||||||
*/
|
*/
|
||||||
function getMcpServerConfig(): Record<string, any> {
|
function getMcpServerConfig(): Record<string, any> {
|
||||||
return {
|
return {
|
||||||
type: 'stdio',
|
type: 'stdio',
|
||||||
command: 'npx',
|
command: 'codegraph',
|
||||||
args: ['@colbymchenry/codegraph', 'serve', '--mcp'],
|
args: ['serve', '--mcp'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ export function hasPermissions(location: InstallLocation): boolean {
|
|||||||
* Stop → sync-if-dirty (sync, ensures fresh index before next user turn)
|
* Stop → sync-if-dirty (sync, ensures fresh index before next user turn)
|
||||||
*/
|
*/
|
||||||
function getHooksConfig(): Record<string, any> {
|
function getHooksConfig(): Record<string, any> {
|
||||||
const command = 'npx @colbymchenry/codegraph';
|
const command = 'codegraph';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
PostToolUse: [
|
PostToolUse: [
|
||||||
|
|||||||
+6
-11
@@ -25,13 +25,10 @@ export async function runInstaller(): Promise<void> {
|
|||||||
showBanner();
|
showBanner();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 1: Try global install for bare `codegraph` command convenience.
|
// Step 1: Ensure codegraph is installed globally
|
||||||
// This is best-effort — configs always use npx regardless.
|
|
||||||
let codegraphAvailable = false;
|
|
||||||
try {
|
try {
|
||||||
const checkCmd = process.platform === 'win32' ? 'where codegraph' : 'command -v codegraph';
|
const checkCmd = process.platform === 'win32' ? 'where codegraph' : 'command -v codegraph';
|
||||||
execSync(checkCmd, { stdio: 'pipe' });
|
execSync(checkCmd, { stdio: 'pipe' });
|
||||||
codegraphAvailable = true;
|
|
||||||
} catch {
|
} catch {
|
||||||
// Not installed globally yet — try to install
|
// Not installed globally yet — try to install
|
||||||
console.log(chalk.dim(' Installing codegraph globally...'));
|
console.log(chalk.dim(' Installing codegraph globally...'));
|
||||||
@@ -40,18 +37,16 @@ export async function runInstaller(): Promise<void> {
|
|||||||
// Verify it actually worked (PATH may not include npm global bin)
|
// Verify it actually worked (PATH may not include npm global bin)
|
||||||
try {
|
try {
|
||||||
execSync(process.platform === 'win32' ? 'where codegraph' : 'command -v codegraph', { stdio: 'pipe' });
|
execSync(process.platform === 'win32' ? 'where codegraph' : 'command -v codegraph', { stdio: 'pipe' });
|
||||||
codegraphAvailable = true;
|
|
||||||
success('Installed codegraph command globally');
|
success('Installed codegraph command globally');
|
||||||
} catch {
|
} catch {
|
||||||
// Install "succeeded" but command not in PATH — common with nvm/fnm
|
// Install "succeeded" but command not in PATH — common with nvm/fnm
|
||||||
info('Global install succeeded but codegraph is not in your PATH');
|
info('Global install succeeded but codegraph is not in your PATH');
|
||||||
info('You may need to add npm\'s global bin to your PATH, or use:');
|
info('You may need to add npm\'s global bin directory to your PATH');
|
||||||
info(' npx @colbymchenry/codegraph <command>');
|
info('Then restart your terminal and run: codegraph init -i');
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
info('Could not install globally (permission denied)');
|
info('Could not install globally (permission denied)');
|
||||||
info('You can install manually with: sudo npm install -g @colbymchenry/codegraph');
|
info('Try: sudo npm install -g @colbymchenry/codegraph');
|
||||||
info('Or use: npx @colbymchenry/codegraph <command>');
|
|
||||||
}
|
}
|
||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
@@ -113,7 +108,7 @@ export async function runInstaller(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show next steps
|
// Show next steps
|
||||||
showNextSteps(location, codegraphAvailable);
|
showNextSteps(location);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log();
|
console.log();
|
||||||
if (err instanceof Error && err.message.includes('readline was closed')) {
|
if (err instanceof Error && err.message.includes('readline was closed')) {
|
||||||
@@ -139,7 +134,7 @@ async function initializeLocalProject(): Promise<void> {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof Error ? err.message : String(err);
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
error(`Could not load native modules: ${msg}`);
|
error(`Could not load native modules: ${msg}`);
|
||||||
info('Skipping project initialization. You can run "npx @colbymchenry/codegraph init -i" later.');
|
info('Skipping project initialization. You can run "codegraph init -i" later.');
|
||||||
info('If this persists, try a Node.js LTS version (20 or 22).');
|
info('If this persists, try a Node.js LTS version (20 or 22).');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user