From 4f28de9c90cbd8b6bacbd6cf73b3f664db64eee3 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Sun, 25 Jan 2026 20:05:43 -0500 Subject: [PATCH] feat(npm): add postuninstall script - add scripts/postuninstall.js for npm uninstall cleanup - register in package.json scripts.postuninstall - runs ccs --uninstall on npm uninstall -g --- package.json | 1 + scripts/postuninstall.js | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 scripts/postuninstall.js diff --git a/package.json b/package.json index 7e48c2cf..eb34e0af 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "preferGlobal": true, "scripts": { "preinstall": "node scripts/preinstall.js", + "postuninstall": "node scripts/postuninstall.js", "build": "tsc && node scripts/add-shebang.js", "build:watch": "tsc --watch", "build:server": "tsc && node scripts/add-shebang.js", diff --git a/scripts/postuninstall.js b/scripts/postuninstall.js new file mode 100644 index 00000000..d7e1181e --- /dev/null +++ b/scripts/postuninstall.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node +/** + * CCS Postuninstall Script + * + * Cleans up WebSearch hook from ~/.claude/settings.json after npm uninstall. + * Self-contained, no external dependencies. + */ + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json'); + +function cleanupHook() { + try { + if (!fs.existsSync(SETTINGS_PATH)) { + return; // Nothing to clean + } + + const content = fs.readFileSync(SETTINGS_PATH, 'utf8'); + let settings; + try { + settings = JSON.parse(content); + } catch { + return; // Malformed JSON, don't touch + } + + const hooks = settings.hooks; + if (!hooks?.PreToolUse) { + return; // No hooks to remove + } + + const originalLength = hooks.PreToolUse.length; + hooks.PreToolUse = hooks.PreToolUse.filter((h) => { + if (h.matcher !== 'WebSearch') return true; + const command = h.hooks?.[0]?.command; + if (!command) return true; + return !command.includes('.ccs/hooks/websearch-transformer'); + }); + + if (hooks.PreToolUse.length === originalLength) { + return; // Nothing changed + } + + // Clean up empty structures + if (hooks.PreToolUse.length === 0) { + delete hooks.PreToolUse; + } + if (Object.keys(hooks).length === 0) { + delete settings.hooks; + } + + fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8'); + } catch { + // Silent fail - not critical + } +} + +cleanupHook();