Skip to content

[CLAUDE ROUTINE]: CX/Reliability enhancement — add a confirmation step (or --yes) to failproofai policies --uninstall --scope all so a single typo can't sweep hooks out of every scope at once #268

@NiveditJain

Description

@NiveditJain

Summary

failproofai policies --uninstall --scope all is the only --scope value that fans out across every Claude Code settings file the user owns (user / project / local) and clears enabledPolicies, customPoliciesPath, and policyParams from each scope. That fan-out is exactly what users want when they intentionally reach for it — but it currently runs without any "are you sure?" gate, so a one-character typo (or a --scope all autocomplete dropping in unintentionally) wipes the entire setup in one command.

Adding a tiny interactive confirmation (skippable with --yes for CI) keeps the existing happy path fast for power users while removing the "oh no, I lost my whole config" footgun for everyone else.

Where

bin/failproofai.mjs:328-386 — the uninstall branch parses --scope and goes straight to removeHooks(...) with no prompt:

const scopeIdx = subArgs.indexOf("--scope");
const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
// ...validation...
await removeHooks(
  policyNames.length > 0 ? policyNames : undefined,
  scope,                    // ← can be "all"
  undefined,
  { betaOnly, removeCustomHooks, cli },
);

src/hooks/manager.ts:272-424 — the removeHooks() body fans scope === "all" across every scope of every selected CLI and clears the shared policyParams / customPoliciesPath from every scope's config:

const scopesToRemove: HookScope[] =
  scope === "all"
    ? [...integration.scopes]
    : integration.scopes.includes(scope) ? [scope] : [];
// ...
if (scope === "all") {
  // Clear config across all three scopes
  for (const s of HOOK_SCOPES) {
    const existing = readScopedHooksConfig(s, cwd);
    if (existing.enabledPolicies.length > 0 || existing.customPoliciesPath || existing.policyParams) {
      const { customPoliciesPath: _drop, policyParams: _dropParams, ...rest } = existing;
      // …writes empty config back…
    }
  }
}

So a single command edits Claude / Codex / Copilot / Cursor settings across user + project + local + clears ~/.failproofai/policies-config.json, <repo>/.failproofai/policies-config.json, and <repo>/.failproofai/policies-config.local.json — all without asking.

Why this matters

flowchart TD
    A[User wants to uninstall hooks at project scope] --> B{Types command}
    B -->|--scope project ✅| C[Removes only project scope]
    B -->|--scope all 🚨 typo or autocomplete| D[Removes from user + project + local + every CLI]
    D --> E[customPoliciesPath cleared]
    D --> F[policyParams cleared]
    D --> G[enabledPolicies cleared in every scope file]
    E --> H[User: 'wait, where did my custom policies go?']
    F --> H
    G --> H
    H --> I[Re-discover paths, re-pick policies, re-tune params]
    I --> J[~10–30 minutes of recovery]

    A2[CI / scripted uninstall] --> B2[--scope all --yes]
    B2 --> K[Skips prompt cleanly]
Loading

Concrete scenarios we already see in support traffic-shaped questions:

  • Tab-completion drift. The user types --scope p and tab-completes to --scope project, fat-fingers, deletes the roject, hits enter. --scope all is a valid value too, so there's no error, just a sweeping uninstall.
  • Copy-paste from a teammate's runbook. Internal docs that say "to fully reset, run failproofai policies --uninstall --scope all" get copy-pasted into a project shell when the user only wanted to wipe the project scope.
  • Recovery cost is non-trivial. customPoliciesPath and policyParams are getting cleared from every scope at once — not just hook registrations — so the user has to re-locate their custom file and re-tune any policy params they had set. That's exactly the work --install was supposed to make easy.

The good news: this is a tiny CX add that keeps the existing happy path fast, and gives CI a clean opt-out via a --yes flag.

Proposed enhancement

Two complementary tightenings — implement together:

  1. Interactive confirmation when --scope all is used and stdin is a TTY. Show a one-screen summary of exactly what will be cleared, then prompt Continue? [y/N]. Default no.

    if (scope === "all" && process.stdin.isTTY && !args.includes("--yes")) {
      const affectedClis = selectedClis.map((c) => getIntegration(c).displayName).join(", ");
      console.log(`This will remove failproofai hooks from ALL scopes for ${affectedClis}:`);
      for (const cliId of selectedClis) {
        const integration = getIntegration(cliId);
        for (const s of integration.scopes) {
          console.log(`  • ${integration.displayName} / ${s}: ${integration.getSettingsPath(s, cwd)}`);
        }
      }
      console.log(`It will also clear customPoliciesPath, policyParams, and enabledPolicies from every scope's config.`);
      const ok = await prompt("Continue? [y/N] ");
      if (!/^y(es)?$/i.test(ok.trim())) {
        throw new CliError("Aborted by user.");
      }
    }
  2. --yes (alias -y) flag to suppress the prompt, so anyone scripting the cleanup (CI cleanup jobs, install-test scripts) has a clear opt-out. Documented under policies --uninstall in --help.

Composability note: re-use the same prompt helper (or extract one) that the interactive policy selector already uses, so non-TTY callers (CI) still go straight through with --yes.

Acceptance criteria

  • In a TTY, failproofai policies --uninstall --scope all shows the affected paths and prompts Continue? [y/N] with default no.
  • --yes / -y skips the prompt for scripted use.
  • Non-TTY stdin (e.g. failproofai policies --uninstall --scope all < /dev/null in CI) without --yes aborts with a clear error explaining --yes is required.
  • --scope user|project|local paths are unchanged (no prompt).
  • New unit test in __tests__/hooks/manager.test.ts (or bin test once the bin tests from [CLAUDE ROUTINE]: Test enhancement — add unit tests for bin/failproofai.mjs CLI dispatch, unknown-flag suggestions, and exit-code contract #255 land) covering: TTY-confirm, TTY-decline, --yes skip, non-TTY abort.
  • --help / policies --help documents the new prompt and --yes flag.
  • CHANGELOG entry under ## Unreleased > Features (or Fixes if framed as a guardrail).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions