Files
tolaria/.husky/pre-commit
Test 52a673e16e feat: auto-purge notes trashed 30+ days ago on app launch
Implement silent background cleanup of notes that have been in trash
for more than 30 days, fulfilling the promise already shown in the
Trash view UI.

Safety model (all 5 checks must pass per file):
- _trashed: true in frontmatter
- _trashed_at present and parseable as date
- Date strictly >30 days ago
- File exists on disk
- File path inside vault root

Uses trash::delete (OS trash) with fs::remove_file fallback.
Triggers on app launch and window focus (max once/hour).
Audit log at .laputa/purge.log. Dry-run mode for testing.

Also fixes pre-commit hook to read CodeScene thresholds from
.codescene-thresholds instead of hardcoded values, matching the
ratchet mechanism documented in CLAUDE.md.

ADR-0042 documents the safety model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 05:53:50 +02:00

74 lines
3.3 KiB
Bash
Executable File

#!/bin/sh
# Pre-commit: same checks as CI. Fix here, not in CI.
set -e
echo "🔍 Pre-commit checks..."
# Lint + types (only if TS files staged)
STAGED_TS=$(git diff --cached --name-only | grep -E '\.(ts|tsx)$' || true)
if [ -n "$STAGED_TS" ]; then
echo " → lint + tsc..."
pnpm lint --quiet
npx tsc --noEmit
fi
# Unit tests
echo " → tests..."
pnpm test --run --silent
echo "✅ Pre-commit passed"
# ── CodeScene Code Health gate ────────────────────────────────────────────
# Blocks commit if scores drop below thresholds in .codescene-thresholds.
# Thresholds are a ratchet — only go up, auto-updated after each successful push.
# Note: remote scores lag behind local changes (update after push + re-analysis).
# If check fails: extract hooks, split components, reduce complexity.
# Never use eslint-disable, #[allow(...)], or `as any`.
echo "🏥 CodeScene code health check..."
THRESHOLDS_FILE=".codescene-thresholds"
if [ -z "$CODESCENE_PAT" ] || [ -z "$CODESCENE_PROJECT_ID" ]; then
echo " ⚠️ CODESCENE_PAT or CODESCENE_PROJECT_ID not set — skipping (CI will enforce)"
elif [ ! -f "$THRESHOLDS_FILE" ]; then
echo " ⚠️ $THRESHOLDS_FILE not found — skipping (CI will enforce)"
else
# Read ratchet thresholds
HOTSPOT_THRESHOLD=$(grep '^HOTSPOT_THRESHOLD=' "$THRESHOLDS_FILE" | cut -d= -f2)
AVERAGE_THRESHOLD=$(grep '^AVERAGE_THRESHOLD=' "$THRESHOLDS_FILE" | cut -d= -f2)
if [ -z "$HOTSPOT_THRESHOLD" ] || [ -z "$AVERAGE_THRESHOLD" ]; then
echo " ⚠️ Could not parse thresholds from $THRESHOLDS_FILE — skipping"
else
API_RESPONSE=$(curl -sf \
-H "Authorization: Bearer $CODESCENE_PAT" \
-H "Accept: application/json" \
"https://api.codescene.io/v2/projects/$CODESCENE_PROJECT_ID" 2>/dev/null || echo "{}")
HOTSPOT_SCORE=$(echo "$API_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['analysis']['hotspot_code_health']['now'])" 2>/dev/null || echo "")
AVERAGE_SCORE=$(echo "$API_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['analysis']['code_health']['now'])" 2>/dev/null || echo "")
if [ -z "$HOTSPOT_SCORE" ] || [ -z "$AVERAGE_SCORE" ]; then
echo " ⚠️ Could not fetch CodeScene scores — skipping (CI will enforce)"
else
echo " Hotspot Code Health: $HOTSPOT_SCORE (threshold: $HOTSPOT_THRESHOLD)"
echo " Average Code Health: $AVERAGE_SCORE (threshold: $AVERAGE_THRESHOLD)"
python3 -c "
import sys
hotspot = float('$HOTSPOT_SCORE')
average = float('$AVERAGE_SCORE')
h_thresh = float('$HOTSPOT_THRESHOLD')
a_thresh = float('$AVERAGE_THRESHOLD')
failed = False
if hotspot < h_thresh:
print(f'FAIL: Hotspot Code Health {hotspot:.2f} < {h_thresh} — extract hooks, split components, reduce complexity')
failed = True
else:
print(f'OK: Hotspot {hotspot:.2f} >= {h_thresh}')
if average < a_thresh:
print(f'FAIL: Average Code Health {average:.2f} < {a_thresh} — recent changes introduced regressions in non-hotspot files')
print(' Review files changed in this task. Never use eslint-disable, #[allow(...)], or as any to bypass.')
failed = True
else:
print(f'OK: Average {average:.2f} >= {a_thresh}')
if failed:
sys.exit(1)
" || exit 1
fi
fi
fi