claude-oops 0.4.0

Automatic safety net for Claude Code: snapshot before risky ops, restore in seconds.
# Changelog

## v0.4.0

Two improvements driven by feedback from early users — both close real
gaps in the snapshot strategy.

- **`Stop` hook: snapshot after every Claude turn.** Up to v0.3.x snapshots
  were strictly *before* risky operations (PreToolUse hook + a curated
  dangerous-bash matcher). That assumed Claude could recognise its own
  destructive moves — which it often can't. The Stop hook fires when
  Claude finishes a turn and is content-agnostic: every completed turn
  becomes an atomic recoverable unit, no matter what was inside.
  Idempotency suppresses snapshots for chat-only turns, so chatty
  sessions don't fill up with noise.
- **Especially valuable in agent-loop setups** where Claude operates
  autonomously without git access. The new framing in the README:
  *git semantics without giving Claude the git CLI* — claude-oops
  provides the recovery layer outside Claude's reach.
- **Expanded dangerous-bash patterns:** `sed -i`, `awk -i inplace`,
  `perl -i` / `perl -pi`, `git apply`, `truncate -s 0`. These were all
  destructive operations the matcher used to miss.

## v0.3.4

Three real bugs found by the first user trying `/oops` on a real project:

- **Restore now actually overwrites conflicting local changes.** Previously
  `claude-oops to <id>` used `git read-tree -m -u`, which is merge-aware
  and refuses to clobber uncommitted local edits — but that's exactly what
  the user just confirmed they want to do. Now we use a private temp
  index + `checkout-index -a -f`, which force-overwrites. Per-file restore
  was already doing this correctly; whole-tree restore now matches.
- **Whole-tree restore deletes working-tree files that aren't in the
  snapshot** (tracked + untracked, respecting `.gitignore`), so the working
  tree ends up matching the snapshot exactly. Previously these files
  were left stranded.
- **`confirm()` errors clearly when stdin is empty + non-TTY**, instead of
  silently aborting. Slash commands invoke the binary without a TTY,
  and the silent abort masqueraded as "user declined". Now: clear
  "pass --force" message.
- **`/oops` slash command now runs `claude-oops show <id>` first** to
  preview the change, asks the user in chat, then runs `to <id> --force`.
  No more confusing double-confirm flow where Claude tries `yes |` because
  the binary kept aborting.

## v0.3.3

- `claude-oops list` and `status` no longer fail when run outside a git
  repository — they print a friendly note and exit 0. This was making
  the `/oops` slash command blow up in any non-git project.
- `claude-oops snap --quiet` (used by the SessionStart hook) also exits 0
  in non-git directories instead of failing the hook.
- `claude-oops snap` (manual, non-quiet) still errors loudly — explicit
  user action deserves an explicit error.

## v0.3.2

- Per-file restore: rewrite path resolution. Use `git rev-parse --show-prefix`
  from the user's cwd instead of comparing absolute paths — Windows reports
  cwd with 8.3 short names (`RUNNER~1`) while git uses long names
  (`runneradmin`), and a lexical strip-prefix between them fails.
  Now we work entirely in repo-relative space; absolute paths are
  rejected with a helpful error.

## v0.3.1

- (yanked — Windows path resolution still broken; superseded by v0.3.2)
- Attempted Windows pathspec fix: emit forward slashes regardless of OS.
  Insufficient on its own — see v0.3.2.

## v0.3.0 (yanked — broken on Windows)

- **Per-file restore.** `claude-oops to <id> -- src/auth.rs` restores only
  the named paths from the snapshot, leaving everything else in the
  working tree alone. Pathspecs work too: `to <id> -- src/` for a whole
  directory. Files that exist in the working tree but not in the snapshot
  are deleted (that's what "restore the snapshot's state for this path"
  means). Paths are resolved relative to your current directory, so
  running from a subdirectory works as expected.
- CI: granted `contents: write` to the release job so `softprops/action-gh-release`
  can publish.
- Published to crates.io: `cargo install claude-oops` now works.

## v0.2.0

- New `claude-oops show <id>` — file-level summary of what `to <id>` would
  change. Color-coded `A`/`M`/`D` like `git status`.
- `claude-oops install` now also writes a `/oops` slash command to
  `~/.claude/commands/oops.md` so you can list and restore snapshots
  without leaving your Claude Code session.
- `_hook-pre-tool-use` now writes a one-line announcement to stderr each
  time it takes a snapshot, so users can see hooks firing.
- Hook auto-snapshots now record a useful default message (file path for
  Edit/Write, command for Bash) instead of `null`.
- `uninstall` removes the `/oops` slash command unless the user edited it.
- Test fixtures pin `core.autocrlf=false`; fixes Windows CI flake.

## v0.1.0

- `snap`, `list`, `diff`, `to`, `drop`, `clean`, `install`, `uninstall`,
  `status`, plus the internal `_hook-pre-tool-use` entry point.
- Storage as git refs under `refs/claude-oops/<id>` plus a JSONL index in
  `.git/claude-oops/index.jsonl`.
- Working-tree capture via a private temporary index — includes untracked
  files (respecting `.gitignore`) without disturbing the user's index.
- Auto-snapshot triggers: SessionStart, PreToolUse on Edit/Write (with a
  2-min cooldown), PreToolUse on Bash matching a curated dangerous-pattern
  list (`rm -rf`, `git reset --hard`, `find … -delete`, etc.).
- Retention: keep last 30 OR snapshots from the last 7 days, whichever is
  more permissive.
- Cross-platform CI on macOS, Linux, Windows. Release workflow builds
  binaries for x86_64 + aarch64 darwin, x86_64-linux-gnu, x86_64-windows.