Releases: forethought-studio/ai-trash
v1.6.12
What's new
- Bypass autoconf/configure artifacts —
conftest*,confdefs.h,confcache,confinc.*,confmf.*,conf[0-9]*,libconftest.*,conftstm.*, anda.outare now permanently deleted instead of filling up trash during./configureruns - Fix cleanup retention off-by-one —
find -mtime +Nrequires strictly more than N full 24h periods (fractional days are truncated), so items at exactly the retention boundary could survive cleanup indefinitely. Now correctly purges items ≥ N days old.
v1.6.11
Performance: near-zero overhead for non-AI rm calls
Changes
- Pre-source fast path: When no AI environment variables are set,
rm_wrapper.shnow bypasses library sourcing and process detection entirely, going straight to/bin/rm. This eliminates the./configureperformance problem where hundreds ofrmcalls from subshells each paid full detection cost (~0.47s each). FAST_PATH=falseconfig option: Users of standalone AI tools that only set process names (aider, goose, etc.) can disable the fast path to get full process-tree detection.
Impact
| Scenario | v1.6.10 | v1.6.11 |
|---|---|---|
Non-AI rm call |
~470ms | ~10ms |
./configure (500 rm calls) |
~235s overhead | ~5s overhead |
AI rm call (env var detected) |
~370ms | ~370ms (unchanged) |
All major AI platforms (Claude Code, VS Code, Cursor, Windsurf, Codex, Warp) set environment variables and are detected on the fast path.
v1.6.10
Performance: faster AI process detection
Reduces per-rm overhead for build tools (./configure, make, etc.) that call rm hundreds of times.
Changes
- Single-fork process tree walk: Replaced per-ancestor bash loop (
3N+1subprocess forks) with a singleps | awkpipeline in_is_ai_process(),_detect_ai_process_command(), and_build_process_chain(). Cuts first-call detection from ~16 forks to 1. - PPID-keyed file cache: After the first
rmcall from a given parent process, subsequent calls skip detection entirely by reading a cached result from/tmp. Cache validates against parent process name to guard against PID reuse. - Eliminated
basename/dirnameforks: Replaced$(basename "$f")and$(dirname "$f")with bash parameter expansion (${f##*/},${f%/*}) throughout hot paths.
Impact
| Scenario | Before | After |
|---|---|---|
First rm (non-AI, selective mode) |
~460ms | ~370ms |
Repeated rm (same parent, cached) |
~460ms | ~285ms |
./configure with 500 rm calls |
~230s overhead | ~145s overhead |
v1.6.6
Improvements
Added to the default bypass patterns (permanently deleted instead of trashed):
- Playwright browser binaries (
/ms-playwright/) — chromium, webkit, and firefox; large and auto-downloaded on demand - Gradle daemon state files (
/.gradle/daemon) — process lock/state files auto-recreated on next build
Also added Claude app cache and ML model weights (ggml-) as commented examples in the config.
v1.6.5
Improvements
- Global bypass pattern for
node_modules/: npm/yarn/bun dependency directories are permanently deleted instead of trashed. They carry no original content and are reinstalled instantly.
v1.6.4
Improvements
- Global bypass pattern for
~/.Trash/tmp.*: mktemp-style ephemeral files that land in the system Trash (common in safe mode) are now permanently deleted instead of being double-trashed. They carry no recoverable data.
v1.6.3
Bug fixes
rm -don symlink-to-directory: The symlink was left behind instead of being trashed. The operand classifier now treats symlinks as files (not directories), matching realrm -dbehaviour.is_empty_dirfalse positive on symlinks: A symlink to an empty directory would pass the empty-dir check and be routed to/bin/rmdir, which fails with "Not a directory". The guard now excludes symlinks.
These bugs were introduced in v1.6.2 by the empty-directory permanent-deletion feature.
v1.5.0
What's new
New wrappers
- git wrapper — snapshots files before destructive git operations (
clean -fd,checkout -- .,restore,reset --hard,stash drop/clear,branch -D). Blocksgit filter-repo. Non-destructive commands pass through instantly. - find wrapper — intercepts
find -deleteand routes deletions through the rm wrapper for trash protection. - unlink wrapper — handles
unlinkcalls with the same trash routing asrm.
macOS App Sandbox support
- All wrappers now detect
APP_SANDBOX_CONTAINER_IDand pass through to the real binary immediately. This prevents sandboxed apps from being blocked when they invoke barerm,git, orfindvia PATH. - Added "Known limitations" section to README documenting the macOS sandbox constraint.
Bug fixes
- Fix git wrapper global args propagation (
git -C /path clean -fdnow works correctly) - Remove unreachable fallback exec in git wrapper
- Fix 3 bugs found by new tests (test coverage audit)
- Fix test suite hang caused by locale test fake git looping through the wrapper
Testing
- 80+ new test assertions covering git, find, unlink, sandbox guards, and edge cases
- Comprehensive test coverage audit with destructive-action safety tests
v1.4.0
What's new
- Better AI detection: Added
CLAUDECODE=1andCODEX_SANDBOX=seatbeltto env var detection — catches deletions by scripts running inside AI tool shells, even when the AI process isn't a direct ancestor - Process-chain forensics: New
com.ai-trash.process-chainxattr stores the full ancestor chain on every trashed file (e.g.bash /Users/user/bin/q list > zsh > claude > zsh > login > Terminal), making it easy to diagnose misclassified deletions - Honest labeling: Fallback label changed from
(human)to(unknown)with the parent's full command line — the wrapper no longer claims to know the source when it doesn't - Smart chain labels: Interpreter processes (bash, python3, node, etc.) include their script argument in the chain when short enough, so
bashbecomesbash /Users/user/bin/q list
v1.3.0 — Windows Recycle Bin support
What's new
Windows: Recycle Bin routing
AI-deleted files on Windows now go to the real Windows Recycle Bin instead of a custom folder. Explorer's native "Restore" works out of the box, in addition to ai-trash restore.
A JSON manifest at %USERPROFILE%\.config\ai-trash\manifest.json tracks every deletion so ai-trash list, restore, and empty can identify and manage ai-trash items within the bin.
Windows: Silent-deletion hardening
Three-layer protection against SendToRecycleBin silently falling back to permanent deletion (network paths, policy-disabled bin, quota exceeded):
- Pre-check — skips bin attempt for UNC/network paths and
NoRecycleFilespolicy - Post-scan — confirms
$Ifile exists in$RECYCLE.BIN\<SID>after deletion - Better restore error — clear message when a manifest entry exists but the bin item is gone
Windows: Gone-item UX in ai-trash list
Items that were removed from the Recycle Bin externally (user emptied it, quota purge) are shown in red at the bottom of the list with GONE in the SIZE column, rather than silently disappearing. A footer line reports the count removed from tracking.
Windows: ai-trash status has no side effects
status now reads the manifest directly without reconciling against the live Recycle Bin, so it's always fast and never modifies state. Stale entries surface when the user runs list.
Cleanup
- Removed the legacy
%USERPROFILE%\.Trash\ai-trash\fallback folder — all deletions go to the Recycle Bin - Extracted shared
_FindInRecycleBin/_GetIFilehelpers intowindows/_recycle-bin.ps1(eliminates duplication betweenai-trash.ps1andai-trash-cleanup.ps1) - Removed disposable-pattern permanent-delete logic from Windows wrapper