Skip to main content
Glama
README.md
<p align="center">
  <a href="https://blackman99.github.io/undomap/"><img src="site/logo-512.png" width="96" alt="undomap logo: a survey benchmark triangle half under orange flood water"></a>
</p>

<h1 align="center">undomap</h1>

<p align="center"><b>Which paths can your AI coding agent erase with no way back?</b><br>
One read-only command maps them as flood zones, ranked by the bytes you'd lose forever, with the cheapest fix for each.</p>

<p align="center">
  <a href="https://github.com/Blackman99/undomap/actions/workflows/ci.yml"><img src="https://github.com/Blackman99/undomap/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://github.com/Blackman99/undomap/releases"><img src="https://img.shields.io/github/v/release/Blackman99/undomap?color=d4600a" alt="release"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-1f6f6a" alt="MIT"></a>
  <a href="https://blackman99.github.io/undomap/"><b>Live map →</b></a>
</p>

<p align="center"><img src="site/demo.gif" width="760" alt="undomap surveying a project: untracked and gitignored files land in Zone VE, each with a one-line fix"></p>

## Why

"The agent ran `rm -rf` / `git clean -fdx` / `git checkout .` and it's gone" is now a weekly post. `git` only protects what it tracks. The things people actually lose are the untracked folder of renders, the gitignored `.env`, the local SQLite database, the repo that was never pushed. You can't see any of that from `git status`.

undomap gives you the list **before** you hand an agent write access.

## 30-second start

```sh
cd your-project
npx github:Blackman99/undomap          # map it (Node ≥ 18.17, nothing to install)
npx github:Blackman99/undomap --fix    # plus the cheapest fix under every finding
```

Exit code `0` means nothing in Zone VE. `1` means something can be lost forever. `2` is a usage error.

## What you get

```text
▲ ZONE VE · PERMANENT
  2.4 GB across 5 places your agent can erase forever

  2.1 GB ██████████  renders/  untracked · 1 file
                     fix too big for git: copy it where the agent can't write, then snapshot that copy
                       $ mkdir -p -m 700 ~/.undomap-safe && rsync -a renders/ ~/.undomap-safe/textures-app-renders/
  380 MB █▊          data/     gitignored · 1 file
                     fix too big for git: copy it where the agent can't write, then snapshot that copy
                       $ mkdir -p -m 700 ~/.undomap-safe && rsync -a data/ ~/.undomap-safe/textures-app-data/
   27 KB ▏           .git/     repo with no remote: .git is the only copy of its history · 1 commit
                     fix .git is the only copy of the history: bundle it off-tree (or add a remote and push)
                       $ git bundle create ~/textures-app.bundle --all
    23 B ▏           .env      gitignored · looks like secrets
                     fix keep a copy outside the tree (password manager or ~/.config)
                       $ mkdir -p -m 700 ~/.undomap-safe && cp -Rp .env ~/.undomap-safe/textures-app-.env
     5 B ▏           notes/    untracked · 1 file
                     fix commit it, then git checkout brings it back
                       $ git add -- notes && git commit -m 'track notes'

▲ ZONE AE · SLOW UNDO   90 MB across 2 places — rebuild, restore or re-download
  90 MB  node_modules/  gitignored · regenerable (npm / pnpm / yarn install) · 1 file
   24 B  src/index.ts   tracked, uncommitted edits: last commit comes back, the edits don't

▲ ZONE X · UNDOABLE   41 B · 2 tracked files — git checkout / git restore

  probes  git ✓ 2.47.3  ·  snapshots ? not probed (--no-probe)

  whole map  snapshot the workspace: btrfs/ZFS with snapper, or restic/borg on a timer
    $ sudo snapper -c home create-config /home   # btrfs example
```

The zone letters come from FEMA flood maps:

| Zone | Meaning | What lands here |
|---|---|---|
| **VE** permanent | gone for good | gitignored or untracked and not regenerable, outside any repo, a repo with no remote, a writable symlink out of the tree, nothing snapshotting it |
| **AE** slow undo | recoverable, slowly | `node_modules/`, build output, venvs; uncommitted edits; unpushed commits; anything a detected snapshot or verified cloud version history covers |
| **X** undoable | one command away | clean tracked files: `git checkout` brings them back |

Secret-looking files (`.env`, keys, credentials) are always listed, even past the top 12, because they can't be regenerated.

## Everywhere you need it

**Scripts:** `--json` (stable schema) or `--markdown`. Use `--max-permanent 10MB` to tolerate a little.

**GitHub Actions:** maps the checkout and writes a job summary.

```yaml
- uses: actions/checkout@v5
- uses: Blackman99/undomap@v0
  with:
    fail-on-permanent: "false"   # just report
    max-permanent: "0"
```

Outputs: `permanent-bytes`, `permanent-places`, `slow-bytes`, `json`.

**Agents (MCP):** a read-only stdio server, so the agent can check before it deletes.

```json
{ "mcpServers": { "undomap": { "command": "npx", "args": ["-y", "github:Blackman99/undomap", "mcp"] } } }
```

Tools: `map_report` (the whole map; `reach: true` adds credential reach) and `list_permanent` (just Zone VE).

**Config:** `.undomap.json` in the mapped directory tells undomap what it can't detect:

```json
{
  "permanent": ["dist/atlas/", ".cache/models/"],
  "regenerable": ["generated/"],
  "backedUp": ["media/"]
}
```

`permanent` forces a place into Zone VE even when its name or contents look regenerable (a hand-tuned folder under `dist/`, model weights in a cache). Precedence: what git knows (tracked, uncommitted, unpushed) wins, then real coverage (`backedUp`, a snapshot), then `permanent`, then any regenerable guess. See [configuration](https://blackman99.github.io/undomap/docs/configuration.html#precedence).

**Credential reach:** `--reach` adds the "lethal trifecta" check: can this process read live credentials, reach the network and write outside the tree, all at once? Those are the conditions that turn a deletion into an exfiltration.

```text
◆ REACH · LETHAL TRIFECTA   3 of 3: a bad command here can leak your keys as well as erase
  credentials  yes  .env · ~/.aws/credentials · ~/.ssh/id_ed25519 · $GITHUB_TOKEN
  network      yes  eth0 has an address (no connection attempted)
  write out    yes  ~ (your home folder) · ~/dev (the folder above the tree)
  fix          break one leg: sandbox the agent's commands so they write only this workspace and
               reach only allowed hosts (Codex --sandbox workspace-write, Claude Code /sandbox, a
               devcontainer); at least start it without the tokens
               $ env -u GITHUB_TOKEN "$SHELL"
```

Presence and paths only: credential files are never opened, and environment variables are reported by name. Run it the way your agent runs (or ask the MCP server with `reach: true`) to measure the agent's reach. It is opt-in and never changes the exit code. [Details](https://blackman99.github.io/undomap/docs/usage.html#reach).

## Guarantees

- **Never writes.** No file in the mapped tree, `.git` or the index is touched. Git runs with `GIT_OPTIONAL_LOCKS=0`.
- **Snapshots are probed, not assumed.** Time Machine, btrfs/snapper, ZFS and Timeshift are checked. A probe that can't run is reported as unknown and never fails the map.
- **Cloud sync is verified, not trusted.** Inside an iCloud Drive, Dropbox, OneDrive or Google Drive folder, a path only moves to Zone AE when the client's own config points at the folder, the client is running, and none of the provider's known exclusions apply (Dropbox ignore attributes and `rules.dropboxignore`, `.nosync`/temp/system names, per-file size limits). iCloud keeps deletes but not overwrites, so it never demotes. Anything undomap can't confirm stays in Zone VE as "possible cloud versioning, unverified", with a one-line way to check. [Rules](https://blackman99.github.io/undomap/docs/reference.html#cloud).
- **Never reads a secret.** Secret-looking files are graded by name. `--reach` only `stat`s credential files and reads environment variable names, never values.
- **No network.** No telemetry, no uploads, no connections (`--reach` lists interfaces, it doesn't dial out).

## What it isn't

undomap is a survey, not a guard. It does not block commands, take backups, or recover deleted files. Pair it with your agent's permission settings, and with [agentckpt](https://github.com/Blackman99/agentckpt) for working-tree checkpoints. To find secrets *inside* agent transcripts, use [spillscan](https://github.com/Blackman99/spillscan). Scope: [GOAL.md](GOAL.md) · plans: [ROADMAP.md](ROADMAP.md).

## Docs

[Install](https://blackman99.github.io/undomap/docs/install.html) · [Usage](https://blackman99.github.io/undomap/docs/usage.html) · [Configuration](https://blackman99.github.io/undomap/docs/configuration.html) · [Reference](https://blackman99.github.io/undomap/docs/reference.html) · [FAQ](https://blackman99.github.io/undomap/docs/faq.html)

## Contributing

Bug reports with a `--json` excerpt are gold. See [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed.