nc://safe-swap

v0.1.0Pinned
ThomSharktools/nodecode-safe-swap: Swaps destructive shell commands for reversible twins: rm -r to quarantine, force push to a lease, undo by receipt. - a rewrite of itsolaph/hermes-safe-swap (MIT), credited to itsolaphce12ad4· 10 Nov1 commit
test1 filecell.lisp13.1 KBreceipts.lisp10.7 KBswap.lisp27.6 KB

nodecode-safe-swap

Safe Swap: destructive shell commands become reversible twins — rm -r moves into a quarantine, git push --force becomes a lease, a hard reset snapshots first — with a receipt for each and one command to undo it. A Nodecode rewrite of itsolaph/hermes-safe-swap by itsolaph.

Get it

Tell the agent "add the safe-swap cell", or run nodecode add nc://safe-swap.

Set it up

Nothing is required: the cell starts on the day it is installed, with every rule on and its quarantine in ~/.safe-swap/trash. Its section in ~/.nodecode/config.jsonc turns a rule off, moves the quarantine, or names branches where a force push is left alone (config.example.jsonc shows the shape). restart-cells after an edit.

Use it

Every command an eval snippet runs through (sh "...") is seen by one hook on sh, and a destructive one is rewritten before it runs:

Command Runs as Undo
rm -r build, rm *.log a move into <quarantine>/<receipt>/<n>, with the file and byte count in the note /safe-swap undo <id> puts it back
git push --force, -f, +ref --force-with-lease --force-if-includes not undoable: the lease only refuses an unsafe push
git reset --hard, git checkout -- <paths>, git restore, git clean -f, git stash drop a snapshot first: a stash commit under refs/safe-swap/<id>, the HEAD, or a tar of what clean may delete /safe-swap undo <id> re-applies it
git branch -D <name> the tip is recorded first /safe-swap undo <id> recreates the branch

A command that uses sudo, env, xargs, eval, sh -c, $( ), backticks, a subshell, a redirection, an unreadable option, or a protected target (/, ~, a system directory, the quarantine itself) runs exactly as written, and the model is told so, with the reason. Nothing is ever blocked: a hook that fails calls sh as the model wrote it.

What the model can call through eval:

(safe-swap:plan "rm -rf build")   ; what it would run as, and nothing executed
(safe-swap:receipts)             ; the receipts this quarantine holds, newest first
(safe-swap:undo "4dc78a")        ; put back what that receipt took; "last" for the newest
(safe-swap:purge "4dc78a")       ; delete that receipt's quarantine directory (nothing else is ever deleted)
(safe-swap:status)               ; the quarantine, the rules that are on, how many receipts there are

The operator has /safe-swap list, /safe-swap undo <id|last>, /safe-swap purge [id] and /safe-swap status, and (help :safe-swap) carries the same text the model reads.

A receipt is a directory of plain files (manifest.tsv, stash.sha, branches.tsv, clean.tar, stash.shas, original, kind, stats), so an undo can be finished by hand, without this cell.

The one thing to know: a swapped rm no longer looks like a recursive delete to a guard that reads the command — nodecode-guard will not stop it, and it is reversible from the quarantine instead.

Add nc://safe-swap

  1. 1

    Install Nodecode

    Once per machine.

    $ curl -fsSL https://nodecode.ai/install.sh | sh
  2. 2

    Add the cell

    Pinned at ce12ad4.

    $ nodecode add nc://safe-swap

    Or tell the agent add the safe swap cell.