fix(hooks): improve uninstall safety with jq and better fallback handling

This commit is contained in:
carraes
2025-07-06 11:21:24 -03:00
parent 9e372f5f52
commit 67d101212f
+54 -10
View File
@@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"github.com/alecthomas/kong" "github.com/alecthomas/kong"
"github.com/carlosarraes/shush/internal/config" "github.com/carlosarraes/shush/internal/config"
@@ -111,7 +112,7 @@ func showLLMGuide() {
fmt.Print(`# Shush CLI - LLM Guide fmt.Print(`# Shush CLI - LLM Guide
## Overview ## Overview
Shush is a fast comment removal tool for source code files using sed under the hood. Shush is a fast comment removal tool for source code files using in-memory processing.
- **Purpose**: Remove comments from source code while preserving file structure - **Purpose**: Remove comments from source code while preserving file structure
- **Key Strength**: Processes individual files or entire directories with recursive support - **Key Strength**: Processes individual files or entire directories with recursive support
- **LLM-Friendly**: Supports dry-run mode with colored preview for safe operation - **LLM-Friendly**: Supports dry-run mode with colored preview for safe operation
@@ -210,7 +211,7 @@ shush src/ -r --inline --backup # Apply line comment removal
### Verbose Mode (--verbose) ### Verbose Mode (--verbose)
- **File discovery**: Shows which files found and processed - **File discovery**: Shows which files found and processed
- **Language detection**: Displays detected language per file - **Language detection**: Displays detected language per file
- **Command execution**: Shows sed commands being run - **Processing details**: Shows in-memory processing operations
- **Progress tracking**: File-by-file processing status - **Progress tracking**: File-by-file processing status
### Backup Mode (--backup) ### Backup Mode (--backup)
@@ -393,14 +394,30 @@ func uninstallHooks(scope string) error {
return fmt.Errorf("failed to load settings: %w", err) return fmt.Errorf("failed to load settings: %w", err)
} }
scopeName := "user-wide"
if hookScope == hooks.ScopeProject {
scopeName = "project"
}
if !hooks.HasShushHook(settings) { if !hooks.HasShushHook(settings) {
scopeName := "user-wide"
if hookScope == hooks.ScopeProject {
scopeName = "project"
}
return fmt.Errorf("shush hook not found for %s scope at %s", scopeName, path) return fmt.Errorf("shush hook not found for %s scope at %s", scopeName, path)
} }
if _, err := exec.LookPath("jq"); err == nil {
if err := uninstallWithJQ(path, scopeName); err == nil {
return nil
}
fmt.Printf("⚠️ jq removal failed, using Go implementation\n")
} else {
fmt.Printf("⚠️ jq not available. For surgical removal, install jq and run again.\n")
fmt.Printf(" Manual removal: edit %s and remove shush hook entries\n", path)
}
if err := hooks.RemoveShushHook(settings); err != nil { if err := hooks.RemoveShushHook(settings); err != nil {
return fmt.Errorf("failed to remove shush hook: %w", err) return fmt.Errorf("failed to remove shush hook: %w", err)
} }
@@ -409,14 +426,41 @@ func uninstallHooks(scope string) error {
return fmt.Errorf("failed to save settings: %w", err) return fmt.Errorf("failed to save settings: %w", err)
} }
scopeName := "user-wide"
if hookScope == hooks.ScopeProject {
scopeName = "project"
}
fmt.Printf("✓ Hooks uninstalled for %s scope at %s\n", scopeName, path) fmt.Printf("✓ Hooks uninstalled for %s scope at %s\n", scopeName, path)
return nil return nil
} }
func uninstallWithJQ(path, scopeName string) error {
jqFilter := `
.hooks.PostToolUse |= (
map(
.hooks |= map(select(.command != "shush --changes-only"))
) |
map(select(.hooks | length > 0))
) |
if .hooks.PostToolUse | length == 0 then
.hooks |= del(.PostToolUse)
else
.
end
`
cmd := exec.Command("jq", jqFilter, path)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("jq command failed: %w", err)
}
if err := os.WriteFile(path, output, 0644); err != nil {
return fmt.Errorf("failed to write updated settings: %w", err)
}
fmt.Printf("✓ Hooks uninstalled for %s scope at %s (using jq)\n", scopeName, path)
return nil
}
func showHooksStatus() error { func showHooksStatus() error {
userPath, _ := hooks.GetSettingsPath(hooks.ScopeUser) userPath, _ := hooks.GetSettingsPath(hooks.ScopeUser)
projectPath, _ := hooks.GetSettingsPath(hooks.ScopeProject) projectPath, _ := hooks.GetSettingsPath(hooks.ScopeProject)