fix: address PR review — shebang, double-escaping, data loss, uninstall scope

- Add shebang banner to NPX CLI esbuild config so npx claude-mem works
- Remove manual backslash pre-escaping in WindsurfHooksInstaller (JSON.stringify handles it)
- Scope cache deletion to claude-mem only, not entire vendor namespace
- Use getWorkerPort() in OpenCodeInstaller instead of hard-coded 37777
- Throw on corrupt JSON in readJsonSafe/readGeminiSettings/Windsurf to prevent data loss
- Fix Cursor install stub to warn instead of silently succeeding
- Fix Gemini uninstall to remove individual hooks within groups, not whole groups
- Update tests for new corrupt-file-throws behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-04 13:49:14 -07:00
parent cdffdba97a
commit ae6915b88e
9 changed files with 53 additions and 61 deletions
@@ -147,19 +147,19 @@ function createHookGroup(hookCommand: string): GeminiHookGroup {
// ============================================================================
/**
* Read ~/.gemini/settings.json, returning empty object if missing/corrupt.
* Read ~/.gemini/settings.json, returning empty object if missing.
* Throws on corrupt JSON to prevent silent data loss.
*/
function readGeminiSettings(): GeminiSettingsJson {
if (!existsSync(GEMINI_SETTINGS_PATH)) {
return {};
}
const content = readFileSync(GEMINI_SETTINGS_PATH, 'utf-8');
try {
const content = readFileSync(GEMINI_SETTINGS_PATH, 'utf-8');
return JSON.parse(content) as GeminiSettingsJson;
} catch (error) {
logger.warn('GEMINI', `Failed to parse ${GEMINI_SETTINGS_PATH}, treating as empty`, {});
return {};
throw new Error(`Corrupt JSON in ${GEMINI_SETTINGS_PATH}, refusing to overwrite user settings`);
}
}
@@ -358,15 +358,15 @@ export function uninstallGeminiCliHooks(): number {
let removedCount = 0;
// Remove claude-mem hooks from each event
// Remove claude-mem hooks from within each group, preserving other hooks
for (const [eventName, groups] of Object.entries(settings.hooks)) {
const filteredGroups = groups.filter(group =>
!group.hooks.some(hook => hook.name === HOOK_NAME)
);
if (filteredGroups.length < groups.length) {
removedCount += groups.length - filteredGroups.length;
}
const filteredGroups = groups
.map(group => {
const remainingHooks = group.hooks.filter(hook => hook.name !== HOOK_NAME);
removedCount += group.hooks.length - remainingHooks.length;
return { ...group, hooks: remainingHooks };
})
.filter(group => group.hooks.length > 0);
if (filteredGroups.length > 0) {
settings.hooks[eventName] = filteredGroups;
@@ -381,7 +381,7 @@ export function uninstallGeminiCliHooks(): number {
}
writeGeminiSettings(settings);
console.log(` Removed ${removedCount} claude-mem hook group(s) from ${GEMINI_SETTINGS_PATH}`);
console.log(` Removed ${removedCount} claude-mem hook(s) from ${GEMINI_SETTINGS_PATH}`);
// Remove claude-mem context section from GEMINI.md
if (existsSync(GEMINI_MD_PATH)) {