# wipeguard

**A seatbelt for agents with delete rights.** Before an agent runs a plan that touches files, wipeguard runs it on a
copy of the drive's manifest, measures what would be lost, checks every backup, and answers **allow**, **hold** or
**block**. You also get a safer plan — deletes become moves into a trash folder — and the undo script for it.

wipeguard never touches your files. `check` only reads a plan and a manifest; `scan` only reads a folder.

```
npm test                       # 15 tests, no dependencies
node bin/wipeguard.js check examples/free-space.txt --manifest examples/drive.csv
```

```
Blocked. 11 files would be lost or overwritten (46.2 GB); 9 without a good backup, 554 hours of work at risk.
  block line 3 would delete 8 files protected by edits/**
  hold  9 files without a good backup, 554 hours (69.3 days) of work at risk; 2 backups are older than the file
```

`check` exits **0** for allow, **1** for hold and **2** for block, so a wrapper script can stop before the plan runs.

## Use it from JavaScript

Node 18+ (`require`) or a browser (`<script src="wipeguard.js">` makes `window.wipeguard`).

```js
const wipeguard = require("./src/wipeguard.js");

const r = wipeguard.run({
  plan: "rm audio/mix_ep05.wav",
  manifest: "audio/mix_ep05.wav,1.1GB,18,stale",
  policy: { maxUnrecoverableHours: 0 }           // merged over the defaults
}, step => console.log(step.i + 1, step.tool));

r.verdict   // "hold"
r.summary   // "Held for a human. 1 file would be lost or overwritten (1.1 GB); 1 without a good backup, 18 hours of work at risk."
r.safe      // "mv audio/mix_ep05.wav .trash/wg-…/audio/mix_ep05.wav\n"
r.undo      // "mv .trash/wg-…/audio/mix_ep05.wav audio/mix_ep05.wav\n"
```

## Plans

One command per line; `#` starts a comment.

| Command | What wipeguard counts |
| --- | --- |
| `rm path`, `rm -r dir/`, `rm cache/*.tmp` | each matching file is deleted |
| `mv src dst` | a move keeps the data; a file already at `dst` is overwritten |
| `cp src dst` | a file already at `dst` is overwritten |
| `write path`, `truncate path` | the file's contents are overwritten |
| `sync src/ dst/`, `sync --delete src/ dst/` | files in both are overwritten; with `--delete`, files only in `dst` are deleted |

## Manifests

`path,size,hours,backup` — size like `5.2GB` or plain bytes, hours of work in the file, backup `ok`, `stale` (the
backup is older than the file) or `none`. `wipeguard scan <dir> --backup <backup dir>` writes one for a real folder:
a file is `ok` when the backup has the same size and is not older, `stale` when it differs, `none` when it is missing.
Hours can come from a `--hours hours.csv` file (`path,hours`); otherwise they are 0.

## Policy

| Key | Default | Meaning |
| --- | --- | --- |
| `protect` | `["edits/**", "project/**"]` | globs where any delete or overwrite **blocks** the plan |
| `maxUnrecoverableHours` | `0` | more hours of work than this without a good backup **holds** the plan |
| `maxDeletes` | `25` | more deletions than this in one plan **holds** it |
| `trash` | `".trash"` | where the safer plan moves deleted files |

Globs: `*` matches inside one folder, `**` crosses folders, and a trailing `/` means everything under that folder.

## API

| Call | Returns |
| --- | --- |
| `run(input, onStep?)` | `{ verdict, exit, summary, blast, findings, effects, safe, undo, trash, json, … }` |
| `plan(input)` | the eight steps `run()` takes: `[{ tool, args, why }]` |
| `tools.read`, `load`, `simulate`, `blast`, `check`, `verdict`, `rewrite`, `report` | each step on its own |
| `matches(path, glob)` | `true` / `false` |
| `policy` | a fresh copy of the default policy |

## Files

```
src/wipeguard.js       the library (one file, UMD, zero dependencies)
bin/wipeguard.js       the CLI (scan, check)
test/wipeguard.test.js 15 tests
examples/              an example drive and four plans: free-space, clear-cache, mirror, old-mix
```

MIT License.
