dsh-safe-delete
Intercepts `rm` commands issued by any DSH agent session (GUI, automation runs, headless bridges) at the tools guard layer and moves the targets to the macOS Trash instead of deleting them.
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:NattoCB/dsh-safe-delete说明文档
阅读完整 README ↗dsh-safe-delete
A DeepSeek Harness host-side plugin that makes rm issued by any agent session recoverable: instead of deleting, targets are moved to the macOS Trash.
How it works
The plugin registers one global tools guard on the DSH tools registry. Every bash tool call — GUI sessions, scheduled automation runs, headless bridges, subagents: anything the registry serves — is scanned with a shell-aware lexer for an rm command in command position (rm, sudo rm, /bin/rm, xargs rm, after env/nice-style prefixes and VAR=x assignments).
When one is found, the plugin denies the original command, rewrites the rm invocation to /usr/bin/trash -v (flags stripped; quoting and globs survive because the rewritten command is re-executed by bash -c), runs the trash itself, and reports back to the model:
[dsh-safe-delete] intercepted `rm` — the targets were MOVED TO TRASH (recoverable),
not deleted. Original command denied. Targets: build/ dist
trash: build/ → .Trash/build/
Plain non-rm commands pass through untouched. Commands that merely mention rm (echo rm, grep "rm " log) are ignored. Constructs that could hide an rm behind a string the lexer refuses to rewrite — $(...), backticks, subshells, heredocs, eval, nested sh -c 'rm ...' — are denied with guidance to split the command into plain form; nothing is executed and nothing is deleted.
Scope and limits
- Covered: any
rm/sudo rm/absolute-pathrm/xargs rmin command position, in simple and compound commands (&&,||,;,|). - Not covered:
find -delete,unlink, language runtimes' own file APIs,git clean, and other non-rmdeletion paths. The guard is an accident-prevention net for the most common destructive verb, not a sandbox. rmflags (-f,-r, ...) are stripped rather than honored:trashmoves whole directories natively, and a failed trash (e.g. nonexistent target) is reported instead of silently ignored, sorm -f maybe-existsstyle calls surface an explicit report.sudo rmis intercepted at the guard layer (before sudo ever runs), but deleting files that require root to trash can still fail — trash errors are reported verbatim.
Install
dsh plugin --profile web add github:/dsh-safe-delete
Restart the DSH web process — host-side bundles and their patches load at process start. On boot the plugin logs rm guard active once.
Development
npm test # node --test: lexer, rewrite matrix, guard contract
The lexer/rewriter is exported as exports._internals so tests exercise the real code paths (analyzeCommand, tokenize, splitSegments).
License
MIT