| 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/bashandset -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 statusshows 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:
# Dangerous: May match more than intended
sed -i '/timing/,/end/d' *.sh
✅ CORRECT - Precise Pattern:
# 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:
# 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:
# 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:
# 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:
#!/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*$' | 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:
# 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:
# 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*$' | 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:
# 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*$' | 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:
# 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
# Dangerous: No per-file validation
sed -i '/pattern/d' *.sh
echo "Done!" # ← Declared complete without validation
❌ WRONG: Removing backups too early
# Removes backups before functional testing
rm *.backup
bash test.sh # ← Too late if test fails
❌ WRONG: Vague patterns
# May match more than intended
sed -i '/^[[:space:]]*#/d' *.sh # ← Removes ALL comments, including documentation
✅ CORRECT: Precise removal with validation
# 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
# Don't make it worse - stop removal process
echo "STOP: Gutted files detected"
Step 2: Restore from backups
# 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*$' | wc -l)
backup_lines=$(grep -v '^\s*#' "$backup" | grep -v '^\s*$' | 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
# 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
# Invoke learn-from-mistakes skill
Skill: learn-from-mistakes
# Document the mistake for prevention
Complete Example
Example: Remove Timing Instrumentation
#!/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:
#!/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*$' | 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:
- Precision over speed: Use specific patterns, not vague ones
- Validation per file: Check each file individually
- Test before cleanup: Keep backups until ALL validation passes
- Multiple validation layers: Syntax + integrity + functional tests
- 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 - Pattern matching best practices
- git-workflow.md - Backup-Verify-Cleanup pattern
Success Criteria
Code removal is successful when:
- ✅ Backups created before removal
- ✅ Patterns identified precisely
- ✅ Per-file validation passed
- ✅ Syntax checks passed
- ✅ Integrity checks passed (no gutted files)
- ✅ Functional tests passed
- ✅ Manual review completed
- ✅ Backups removed after all validation
- ✅ No errors in any validation step
- ✅ Files still work as intended