Back to catalog

safe-remove-code

Safely remove code patterns from multiple files with validation and rollback (project)

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • GitHub stars: 2
  • Updated: 2025-11-21

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: safe-remove-code
description: Safely remove code patterns from multiple files with validation and rollback (project)
allowed-tools: Bash, Read, Edit, Grep, Glob
---

# Safe Code Removal Skill

**Purpose**: Safely remove code patterns (instrumentation, debugging code, deprecated patterns) from multiple files with strict validation to prevent accidentally gutting files.

**Created**: 2025-11-07 after accidentally gutting 7 hooks during timing code removal

**Performance**: Prevents catastrophic file damage through per-file validation, syntax checks, and functional testing

## The Problem

When removing instrumentation, debugging code, or other patterns from multiple files, aggressive removal scripts can accidentally delete functional code, leaving only boilerplate (shebang, set commands).

**Real Example** (2025-11-07):
- **Task**: Remove timing instrumentation from 47 hooks
- **Mistake**: Removal script was too aggressive
- **Impact**: 7 hooks reduced to 3 lines (only `#!/bin/bash` and `set -euo pipefail`)
- **Hooks destroyed**: auto-learn-from-mistakes.sh, block-data-loss.sh, detect-worktree-violation.sh, enforce-requirements-phase.sh, load-todo.sh, detect-assistant-giving-up.sh, verify-convergence-entry.sh
- **Recovery**: Restored from backups
- **Root cause**: Didn't validate hooks after removal, declared task complete too early

## When to Use This Skill

### ✅ Use safe-remove-code When:

- Removing instrumentation code from multiple files
- Cleaning up debugging statements across codebase
- Removing deprecated patterns systematically
- Need validation that files remain functional after removal
- Pattern removal affects 5+ files

### ❌ Do NOT Use When:

- Removing code from single file (use Edit tool directly)
- Changes are complex refactoring (not simple removal)
- Pattern varies significantly across files
- Need to preserve some instances of pattern
- Pattern removal is part of larger refactoring task

## ⚠️ Critical Safety Rules

**MANDATORY BACKUP**: Always create timestamped backup before any removal
**PER-FILE VALIDATION**: Validate each file individually (syntax, size, integrity)
**FUNCTIONAL TESTING**: Run build and tests after all removals
**IMMEDIATE VERIFICATION**: Don't declare complete without verification
**AUTOMATIC CLEANUP**: Remove backups only after ALL validation passes
**PRECISE PATTERNS**: Use specific patterns, not vague regex

## Prerequisites

Before using this skill, verify:
- [ ] Working directory is clean: `git status` shows no uncommitted changes
- [ ] Know exact pattern to remove (tested with grep)
- [ ] Identified all files containing pattern
- [ ] Pattern is consistent across files
- [ ] Have test command available (build/test)

## Skill Workflow

### Phase 1: Identify Removal Patterns

**❌ WRONG - Vague Pattern**:
```bash
# Dangerous: May match more than intended
sed -i '/timing/,/end/d' *.sh
```

**✅ CORRECT - Precise Pattern**:
```bash
# Identify EXACT patterns to remove
PATTERNS_TO_REMOVE=(
  "HOOK_START="
  "log_timing()"
  "trap.*timing.*exit"
)

# Test pattern on one file first
for pattern in "${PATTERNS_TO_REMOVE[@]}"; do
  echo "Pattern: $pattern"
  grep -n "$pattern" /workspace/.claude/hooks/example-hook.sh || echo "  No matches"
done
```

**Preview Matches**:
```bash
# See what will be removed before removing
for file in /workspace/.claude/hooks/*.sh; do
  if grep -q "PATTERN" "$file"; then
    echo "=== $(basename "$file") ==="
    grep -n "PATTERN" "$file"
  fi
done
```

### Phase 2: Create Backups

**MANDATORY before any removal**:

```bash
# Create timestamped backups
BACKUP_SUFFIX=".backup-$(date +%Y%m%d-%H%M%S)"

for file in /workspace/.claude/hooks/*.sh; do
  if [[ -f "$file" ]] && [[ ! "$file" =~ \.backup ]]; then
    cp "$file" "${file}${BACKUP_SUFFIX}"
  fi
done

echo "✅ Backups created with suffix: $BACKUP_SUFFIX"
ls -la /workspace/.claude/hooks/*.backup-* | head -5
```

**Verify Backups Created**:
```bash
# Count backups
BACKUP_COUNT=$(find /workspace/.claude/hooks -name "*.backup-*" | wc -l)
ORIGINAL_COUNT=$(find /workspace/.claude/hooks -name "*.sh" ! -name "*.backup-*" | wc -l)

if [[ "$BACKUP_COUNT" -ne "$ORIGINAL_COUNT" ]]; then
  echo "❌ ERROR: Backup count mismatch!"
  echo "   Original files: $ORIGINAL_COUNT"
  echo "   Backups created: $BACKUP_COUNT"
  exit 1
fi

echo "✅ All $ORIGINAL_COUNT files backed up"
```

### Phase 3: Remove Code with Validation

**Create removal script with per-file validation**:

```bash
#!/bin/bash
# safe-pattern-removal.sh
# Removes specific patterns with per-file validation

set -euo pipefail

PATTERN="${1:-}"  # Pattern to remove
TARGET_DIR="${2:-.claude/hooks}"
MIN_LINES="${3:-10}"  # Minimum lines after removal (safety check)

if [[ -z "$PATTERN" ]]; then
  echo "Usage: $0 <pattern> [target-dir] [min-lines]" >&2
  exit 1
fi

echo "Removing pattern: $PATTERN"
echo "Target directory: $TARGET_DIR"
echo "Minimum lines after removal: $MIN_LINES"
echo ""

EXIT_CODE=0

for file in "$TARGET_DIR"/*.sh; do
  if [[ ! -f "$file" ]] || [[ "$file" =~ \.backup ]]; then
    continue
  fi

  filename=$(basename "$file")
  lines_before=$(wc -l < "$file")

  # Remove pattern
  sed -i "/$PATTERN/d" "$file"

  lines_after=$(wc -l < "$file")
  lines_removed=$((lines_before - lines_after))

  # Validate syntax
  if ! bash -n "$file" 2>/dev/null; then
    echo "❌ $filename: SYNTAX ERROR after removal" >&2
    # Restore from backup
    BACKUP=$(ls -t "${file}.backup-"* 2>/dev/null | head -1)
    if [[ -n "$BACKUP" ]]; then
      cp "$BACKUP" "$file"
      echo "   Restored from $BACKUP" >&2
    fi
    EXIT_CODE=1
    continue
  fi

  # Check if file was gutted
  functional_lines=$(grep -v '^\s*#' "$file" | grep -v '^\s*
#39; | wc -l) if [[ $functional_lines -lt $MIN_LINES ]]; then echo "⚠️ $filename: SUSPICIOUSLY SMALL after removal ($functional_lines functional lines, removed $lines_removed)" >&2 echo " Review manually to ensure functional code not removed" >&2 EXIT_CODE=1 else echo "✅ $filename: Removed $lines_removed lines ($functional_lines functional lines remain)" fi done if [[ $EXIT_CODE -eq 0 ]]; then echo "" echo "✅ Pattern removal complete with validation" else echo "" echo "❌ Some files failed validation - review manually" fi exit $EXIT_CODE ``` **Execute Removal**: ```bash # Save script cat > /tmp/safe-pattern-removal.sh <<'EOF' [Script content from above] EOF chmod +x /tmp/safe-pattern-removal.sh # Run with pattern /tmp/safe-pattern-removal.sh "PATTERN_TO_REMOVE" "/workspace/.claude/hooks" 10 ``` ### Phase 4: Functional Testing **BEFORE removing backups, run functional tests**: ```bash # 1. Syntax validation (quick check) echo "Running syntax validation..." for hook in /workspace/.claude/hooks/*.sh; do if [[ -f "$hook" ]] && [[ ! "$hook" =~ \.backup ]]; then if ! bash -n "$hook"; then echo "❌ SYNTAX ERROR: $hook" exit 1 fi fi done echo "✅ All hooks pass syntax check" # 2. Integrity check (file size) echo "" echo "Running integrity check..." for hook in /workspace/.claude/hooks/*.sh; do if [[ -f "$hook" ]] && [[ ! "$hook" =~ \.backup ]]; then functional_lines=$(grep -v '^\s*#' "$hook" | grep -v '^\s*
#39; | wc -l) if [[ $functional_lines -lt 10 ]]; then echo "⚠️ $(basename "$hook"): Only $functional_lines functional lines" fi fi done # 3. Functional tests (if available) echo "" echo "Running functional tests..." if [[ -f /workspace/.claude/hooks/tests/test-hooks.sh ]]; then bash /workspace/.claude/hooks/tests/test-hooks.sh || { echo "❌ Functional tests FAILED" exit 1 } echo "✅ Functional tests passed" else echo "⚠️ No functional tests available - manual verification required" fi # 4. Build test (if applicable) if [[ -f /workspace/main/mvnw ]]; then echo "" echo "Running build test..." cd /workspace/main && ./mvnw clean verify -q || { echo "❌ Build FAILED after code removal" exit 1 } echo "✅ Build passed" fi ``` ### Phase 5: Manual Review **Sample files before declaring complete**: ```bash # Check a few files to verify removal was clean SAMPLE_FILES=( "/workspace/.claude/hooks/auto-learn-from-mistakes.sh" "/workspace/.claude/hooks/enforce-commit-squashing.sh" "/workspace/.claude/hooks/load-todo.sh" ) echo "Manual review of sample files:" for file in "${SAMPLE_FILES[@]}"; do if [[ -f "$file" ]]; then echo "" echo "=== $(basename "$file") ===" echo "Lines: $(wc -l < "$file")" echo "Functional lines: $(grep -v '^\s*#' "$file" | grep -v '^\s*
#39; | wc -l)" echo "" echo "First 20 lines:" head -20 "$file" fi done echo "" echo "⚠️ MANUAL VERIFICATION REQUIRED" echo " Review above output to confirm removal was clean" echo " If anything looks wrong, restore from backups" echo "" read -p "Do all files look correct? (y/n) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Aborting - restore from backups if needed" exit 1 fi echo "✅ Manual verification passed" ``` ### Phase 6: Cleanup Backups **ONLY after validation passes**: ```bash # Final confirmation echo "All validation passed:" echo "✅ Syntax check" echo "✅ Integrity check" echo "✅ Functional tests" echo "✅ Manual review" echo "" read -p "Remove all backups? (y/n) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Keeping backups for safety" exit 0 fi # Remove backups BACKUP_COUNT=$(find /workspace/.claude/hooks -name "*.backup-*" | wc -l) rm -f /workspace/.claude/hooks/*.backup-* echo "✅ Removed $BACKUP_COUNT backup files" echo "" echo "Code removal complete!" ``` ## Validation Checklist Before declaring code removal complete: - [ ] **Backups created** with timestamp - [ ] **Patterns identified** precisely (not vague) - [ ] **Removal script** validates per-file - [ ] **Syntax check** passes for all files - [ ] **Integrity check** passes (no gutted files) - [ ] **Functional tests** pass (hooks/build still work) - [ ] **Manual review** of sample files - [ ] **No errors** in validation output - [ ] **Backups cleanup** only after all checks pass ## Anti-Patterns to Avoid ### ❌ WRONG: Bulk removal without validation ```bash # Dangerous: No per-file validation sed -i '/pattern/d' *.sh echo "Done!" # ← Declared complete without validation ``` ### ❌ WRONG: Removing backups too early ```bash # Removes backups before functional testing rm *.backup bash test.sh # ← Too late if test fails ``` ### ❌ WRONG: Vague patterns ```bash # May match more than intended sed -i '/^[[:space:]]*#/d' *.sh # ← Removes ALL comments, including documentation ``` ### ✅ CORRECT: Precise removal with validation ```bash # Specific pattern with validation sed -i '/^[[:space:]]*# TIMING:/d' *.sh bash -n file.sh || { echo "Syntax error"; restore_backup; } bash test.sh || { echo "Functional test failed"; restore_backup; } ``` ## Recovery Procedures If you discover files were gutted after removal: ### Step 1: Stop immediately ```bash # Don't make it worse - stop removal process echo "STOP: Gutted files detected" ``` ### Step 2: Restore from backups ```bash # Restore all files from most recent backup for backup in /workspace/.claude/hooks/*.backup-*; do original="${backup%.backup-*}.sh" if [[ -f "$backup" ]]; then # Check if original was gutted functional_lines=$(grep -v '^\s*#' "$original" | grep -v '^\s*
#39; | wc -l) backup_lines=$(grep -v '^\s*#' "$backup" | grep -v '^\s*
#39; | wc -l) if [[ $functional_lines -lt 10 ]] && [[ $backup_lines -gt 10 ]]; then echo "Restoring $original from $backup" cp "$backup" "$original" fi fi done echo "✅ Restoration complete" ``` ### Step 3: Validate restoration ```bash # Verify files restored correctly for hook in /workspace/.claude/hooks/*.sh; do if [[ -f "$hook" ]] && [[ ! "$hook" =~ \.backup ]]; then bash -n "$hook" || echo "❌ SYNTAX ERROR: $hook" fi done echo "✅ All files restored and validated" ``` ### Step 4: Analyze what went wrong ```bash # Invoke learn-from-mistakes skill Skill: learn-from-mistakes # Document the mistake for prevention ``` ## Complete Example ### Example: Remove Timing Instrumentation ```bash #!/bin/bash # Complete workflow for removing timing code set -euo pipefail echo "=== Safe Code Removal: Timing Instrumentation ===" echo "" # Phase 1: Identify patterns echo "Phase 1: Identifying patterns..." PATTERNS=( "HOOK_START=" "log_timing" "^[[:space:]]*# TIMING:" ) # Preview what will be removed echo "Preview of matches:" for pattern in "${PATTERNS[@]}"; do echo " Pattern: $pattern" grep -c "$pattern" /workspace/.claude/hooks/*.sh 2>/dev/null | grep -v ":0" || echo " No matches" done echo "" read -p "Continue with removal? (y/n) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Aborted by user" exit 0 fi # Phase 2: Create backups echo "" echo "Phase 2: Creating backups..." BACKUP_SUFFIX=".backup-$(date +%Y%m%d-%H%M%S)" for file in /workspace/.claude/hooks/*.sh; do if [[ -f "$file" ]] && [[ ! "$file" =~ \.backup ]]; then cp "$file" "${file}${BACKUP_SUFFIX}" fi done echo "✅ Backups created" # Phase 3: Remove patterns echo "" echo "Phase 3: Removing patterns with validation..." for pattern in "${PATTERNS[@]}"; do /tmp/safe-pattern-removal.sh "$pattern" "/workspace/.claude/hooks" 10 done # Phase 4: Functional testing echo "" echo "Phase 4: Running functional tests..." for hook in /workspace/.claude/hooks/*.sh; do if [[ -f "$hook" ]] && [[ ! "$hook" =~ \.backup ]]; then bash -n "$hook" || { echo "❌ Syntax error in $(basename "$hook")" exit 1 } fi done echo "✅ All hooks pass syntax check" # Phase 5: Manual review echo "" echo "Phase 5: Manual review..." echo "Sample files:" head -30 /workspace/.claude/hooks/enforce-checkpoints.sh echo "" read -p "Does output look correct? (y/n) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Aborted - restore from backups" exit 1 fi # Phase 6: Cleanup echo "" echo "Phase 6: Cleaning up backups..." rm -f /workspace/.claude/hooks/*.backup-* echo "✅ Backups removed" echo "" echo "=== Code removal complete ===" ``` ## Prevention via Automation **Create pre-commit hook to catch gutted files**: ```bash #!/bin/bash # .git/hooks/pre-commit # Prevents committing gutted hook files HOOKS_DIR=".claude/hooks" MIN_LINES=10 for hook in "$HOOKS_DIR"/*.sh; do if [[ -f "$hook" ]] && git diff --cached --name-only | grep -q "$(basename "$hook")"; then functional_lines=$(grep -v '^\s*#' "$hook" | grep -v '^\s*
#39; | wc -l) if [[ $functional_lines -lt $MIN_LINES ]]; then echo "❌ ERROR: Attempting to commit gutted hook: $(basename "$hook")" >&2 echo " Only $functional_lines functional lines (expected at least $MIN_LINES)" >&2 echo " File may have been accidentally damaged during code removal" >&2 exit 1 fi fi done ``` ## Summary **Key Principles**: 1. **Precision over speed**: Use specific patterns, not vague ones 2. **Validation per file**: Check each file individually 3. **Test before cleanup**: Keep backups until ALL validation passes 4. **Multiple validation layers**: Syntax + integrity + functional tests 5. **Manual review**: Sample check before declaring complete **Remember**: It's better to be slow and careful than fast and destructive. Functional code is irreplaceable; removal can always wait for proper validation. ## Related Documentation - [tool-usage.md](../../docs/optional-modules/tool-usage.md) - Pattern matching best practices - [git-workflow.md](../../docs/project/git-workflow.md) - Backup-Verify-Cleanup pattern ## Success Criteria Code removal is successful when: 1. ✅ Backups created before removal 2. ✅ Patterns identified precisely 3. ✅ Per-file validation passed 4. ✅ Syntax checks passed 5. ✅ Integrity checks passed (no gutted files) 6. ✅ Functional tests passed 7. ✅ Manual review completed 8. ✅ Backups removed after all validation 9. ✅ No errors in any validation step 10. ✅ Files still work as intended