Skip to content

fix(cli): clean up hooks from Claude settings on package postuninstal… - #629

Open
AVPthegreat wants to merge 1 commit into
FailproofAI:mainfrom
AVPthegreat:fix/postuninstall-remove-claude-hooks
Open

fix(cli): clean up hooks from Claude settings on package postuninstal…#629
AVPthegreat wants to merge 1 commit into
FailproofAI:mainfrom
AVPthegreat:fix/postuninstall-remove-claude-hooks

Conversation

@AVPthegreat

@AVPthegreat AVPthegreat commented Jul 30, 2026

Copy link
Copy Markdown

Summary

Fixes #20 by adding a standalone, fail-open postuninstall script (scripts/postuninstall.mjs) and registering it in package.json. When a user runs npm uninstall -g failproofai or bun remove -g failproofai, hook entries are automatically cleaned up from Claude settings (.claude/settings.json), preventing dangling commands in user/project environments.


❌ Root Cause Analysis

Previously, package.json had no postuninstall lifecycle script:

  • Running npm uninstall -g failproofai deleted the global binary from Node/npm global modules, but left --hook entries inside .claude/settings.json.
  • Subsequent Claude Code runs produced command not found: failproofai errors because Claude attempted to execute non-existent binaries referenced in its settings file.

✅ Fix Details

  1. scripts/postuninstall.mjs:
    Created a zero-dependency script executed automatically on npm/bun uninstallation:

    • Scans user (~/.claude/settings.json), project (.claude/settings.json), and local (.claude/settings.local.json) settings files.
    • Filters out failproofai marked hook entries (is_failproofai: true or --hook command signatures).
    • Removes empty event hook matchers and saves clean JSON files.
    • Runs fail-open so package removal never throws or blocks.
  2. package.json Configuration:
    Registered "postuninstall": "node scripts/postuninstall.mjs" under "scripts".


🧪 Unit Tests & Verification

Added unit test suite in __tests__/scripts/postuninstall.test.ts:

  • returns 0 when settings file does not exist
  • removes failproofai hook entries from Claude settings and writes clean file

Test Results:

  • Ran vitest run __tests__/scripts/postuninstall.test.ts
  • Result: ✓ 2 passed (2)

Summary by CodeRabbit

  • New Features

    • Added automatic cleanup of application hooks from Claude settings during uninstallation.
    • Cleanup checks both project-level and home-directory settings files.
    • Reports the number of removed hooks when cleanup occurs.
  • Bug Fixes

    • Uninstallation cleanup safely handles missing settings files and unexpected errors without interrupting the process.
  • Tests

    • Added coverage for missing settings files and successful hook removal.

Copilot AI review requested due to automatic review settings July 30, 2026 14:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a post-uninstall lifecycle script that removes failproofai hooks from Claude settings files, handles cleanup errors without throwing, and adds Vitest coverage for missing files and successful hook removal.

Changes

Uninstall hook cleanup

Layer / File(s) Summary
Post-uninstall cleanup flow
scripts/postuninstall.mjs, package.json, __tests__/scripts/postuninstall.test.ts
The package runs cleanup after uninstall; the script removes matching hooks from user, project, and local Claude settings files; tests cover missing files and rewritten settings.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • exospherehost/failproofai issue 20 — Covers removal of failproofai hooks from Claude settings during npm uninstall.

Possibly related PRs

Suggested labels: bug

Suggested reviewers: copilot

Poem

A bunny found hooks in the Claude-file maze,
And cleaned them away in post-uninstall days.
The settings were read,
Old entries were shed,
While tests gave a bright little praise.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the main change: cleaning Claude hooks during package postuninstall.
Description check ✅ Passed The description covers the fix, rationale, implementation, and tests, though it omits the template's Type of Change and checklist sections.
Linked Issues check ✅ Passed The PR removes FailproofAI hooks from all three Claude settings locations and preserves non-FailproofAI hooks, matching issue #20.
Out of Scope Changes check ✅ Passed All changes are directly related to uninstall cleanup, the lifecycle script, and its tests.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

__tests__/scripts/postuninstall.test.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

package.json

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

scripts/postuninstall.mjs

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Your PR is awaiting review by a reviewer. Till then you can join the Discord for conversation: https://discord.befailproof.ai

@coderabbitai coderabbitai Bot added the bug Something isn't working label Jul 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
__tests__/scripts/postuninstall.test.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Place this unit test under __tests__/hooks/.

As per coding guidelines, “Unit tests live in __tests__/hooks/.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@__tests__/scripts/postuninstall.test.ts` at line 1, Move the unit test
currently located under the scripts test area into the __tests__/hooks/
directory, preserving its existing test behavior and contents.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/postuninstall.mjs`:
- Around line 11-15: The isFailproofaiHook classifier is too broad and can
remove unrelated hooks containing similar text. In scripts/postuninstall.mjs
lines 11-15, match only the explicit FailproofAI marker properties or the exact
installer-generated command form; in __tests__/scripts/postuninstall.test.ts
lines 37-66, add coverage proving a non-FailproofAI hook with similar command
text is preserved.

---

Nitpick comments:
In `@__tests__/scripts/postuninstall.test.ts`:
- Line 1: Move the unit test currently located under the scripts test area into
the __tests__/hooks/ directory, preserving its existing test behavior and
contents.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bf6178aa-c7a7-407c-987e-48bf09f7287d

📥 Commits

Reviewing files that changed from the base of the PR and between debb1fd and 0d89d52.

📒 Files selected for processing (3)
  • __tests__/scripts/postuninstall.test.ts
  • package.json
  • scripts/postuninstall.mjs

Comment thread scripts/postuninstall.mjs
Comment on lines +11 to +15
function isFailproofaiHook(hook) {
if (!hook || typeof hook !== "object") return false;
if (hook.is_failproofai === true || hook.__failproofai_hook === true) return true;
const cmd = typeof hook.command === "string" ? hook.command : "";
return cmd.includes("failproofai") && cmd.includes("--hook");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files matching postuninstall:\n'
fd -a 'postuninstall|postinstall' . || true

printf '\nRelevant file outlines:\n'
for f in $(fd 'postuninstall|mjs$|test\.ts$' . | sed 's#^\./##' | head -50); do
  if grep -qE 'isFailproofaiHook|cleanClaudeSettings|hook' "$f" 2>/dev/null; then
    printf '\n--- %s\n' "$f"
    wc -l "$f"
    ast-grep outline "$f" --match cleanClaudeSettings || true
  fi
done

printf '\nSearch hook classifier usages and test hook fixtures:\n'
rg -n "isFailproofaiHook|failproofai|__failproofai_hook|cleanClaudeSettings|hooks" scripts __tests__ package.json 2>/dev/null || true

printf '\nShow relevant source/tests:\n'
if [ -f scripts/postuninstall.mjs ]; then
  cat -n scripts/postuninstall.mjs
fi
if [ -f __tests__/scripts/postuninstall.test.ts ]; then
  sed -n '1,120p' __tests__/scripts/postuninstall.test.ts | cat -n
fi

Repository: FailproofAI/failproofai

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'scripts/postuninstall.mjs:\n'
cat -n scripts/postuninstall.mjs

printf '\n__tests__/scripts/postuninstall.test.ts first 140 lines:\n'
sed -n '1,140p' __tests__/scripts/postuninstall.test.ts | cat -n

printf '\nBehavioral probe for isFailproofaiHook classifying fixtures:\n'
node - <<'JS'
function isFailproofaiHook(hook) {
  if (!hook || typeof hook !== "object") return false;
  if (hook.is_failproofai === true || hook.__failproofai_hook === true) return true;
  const cmd = typeof hook.command === "string" ? hook.command : "";
  return cmd.includes("failproofai") && cmd.includes("--hook");
}

const hooks = [
  { command: "failproofai --hook PreToolUse" },
  { is_failproofai: true, command: "failproofai --hook PreToolUse" },
  { __failproofai_hook: true, command: "failproofai --hook PreToolUse" },
  { command: "npx -y failproofai --hook PreToolUse" },
  { command: "npx -y failproofai policies --install block-rm-rf" },
  { command: "my-failproofai-helper --hook PreToolUse" },
  { name: "failproofai", type: "task", args: [] },
];
for (const hook of hooks) {
  console.log(JSON.stringify(hook), '=>', isFailproofaiHook(hook));
}
JS

Repository: FailproofAI/failproofai

Length of output: 6150


Use a precise FailproofAI hook identity contract. The substring classifier will clean hooks just because they contain failproofai and --hook, e.g. my-failproofai-helper --hook PreToolUse. Match only explicit FailproofAI markers or the exact installer-generated command form, and add a test case that asserts non-FailproofAI hooks with similar text are preserved.

📍 Affects 2 files
  • scripts/postuninstall.mjs#L11-L15 (this comment)
  • __tests__/scripts/postuninstall.test.ts#L37-L66
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/postuninstall.mjs` around lines 11 - 15, The isFailproofaiHook
classifier is too broad and can remove unrelated hooks containing similar text.
In scripts/postuninstall.mjs lines 11-15, match only the explicit FailproofAI
marker properties or the exact installer-generated command form; in
__tests__/scripts/postuninstall.test.ts lines 37-66, add coverage proving a
non-FailproofAI hook with similar command text is preserved.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: npm uninstall -g failproofai does not remove hooks from Claude settings

3 participants