saferm v0.8.1 /Changelog
On this page

#Changelog

#0.8.1

Publish-workflow fix so the npm wrapper stops racing the binary upload.

Context

0.8.0's publish job for the npm wrapper failed and had to be retried. The job runs npm ci, which runs the wrapper's own postinstall, which downloads the platform binary from the GitHub Release for the version in package.json -- an asset the goreleaser job in the same workflow was still uploading. The retry passed only because by then it had finished, so whether a release publishes to npm was decided by a race.

npm ci --ignore-scripts removes the race outright: the wrapper has no dependencies, and running a package's install hooks was never part of preparing it for publication. Infrastructure only -- no code, no behaviour, nothing a 0.8.0 user needs to read.

#Infrastructure

  • Publish-workflow fix so the npm wrapper stops racing the binary upload.

#0.8.0

One consent gate on purge, a --quiet flag that works, and a dry run that no longer creates the directory it promises not to touch.

Context

saferm shipped 0.7.1 with two consent gates stacked on purge: the CLI framework's consequential approval, and saferm's own [y/N] prompt behind it. An approved non-interactive saferm --approve-consequential purge --all reached the second gate, read EOF, and aborted -- one operation asking twice, with the second ask unanswerable exactly where the first had already been given. The prompt and its --skip-confirmation/-f escape hatch are gone. What the prompt was actually for survives it: the per-record listing of everything about to be destroyed now prints unconditionally after consent and before the first removal.

Two other flags stopped lying in the same pass. --quiet was accepted and ignored -- saferm read it nowhere -- and now suppresses summaries and progress while never touching the outputs that ARE the command: list, info, the dry-run previews, and purge's destruction listing. And --dry-run used to create saferm's whole state directory on a fresh machine on its way to promising it would touch nothing; that creation is now minted on the effects handle, so it is named in the would-do log and performed only in a real run. Following that thread turned up its mirror image: the read-only commands, which correctly create nothing, answered "what have I deleted?" on a fresh machine with SQLite's "unable to open database file". An absent archive is an empty one, and they say so now.

Two entries in this release's changelog described designs that were superseded before they ever shipped -- a blanket approval flag and a prompt in front of every destructive command -- and were rewritten to the end state that ships.

#Breaking

  • **Breaking: --quiet, --verbose, --dry-run and --approve-consequential are the CLI framework's flags now.** They are recognized anywhere on the command line and have no short forms. saferm's own --verbose global and purge's own --dry-run are gone: both spellings still work, they are just delivered by the framework, and --dry-run now applies to every command instead of only to purge. Every command also carries an effect classification -- delete, undelete and purge are mutating, list and info are read-only.
  • **Breaking: purge is the only command that asks before it runs, and --approve-consequential is the only way to say yes.** saferm delete and saferm undelete run bare -- no flag, no prompt -- because both are recoverable: delete moves a file into the archive and undelete brings it back. Confirmation is reserved for purge, the one operation with no way back. A non-interactive purge is saferm --approve-consequential purge --all; without the flag it refuses with error: stdin is not interactive; pass --approve-consequential to confirm.
  • **Breaking: purge --skip-confirmation (-f) is gone.** purge used to raise its own [y/N] prompt behind the framework's approval gate, so one operation asked for consent twice -- and the second ask was unanswerable exactly where the first had already been given: an approved saferm --approve-consequential purge --all read EOF from it and aborted. --approve-consequential is now the single consent. The listing of every record about to be destroyed survives the prompt that carried it: it prints unconditionally after consent and before the first removal, and --quiet does not suppress it.

#Features

  • **--dry-run works on every command and records what it would destroy.** It used to exist only on purge. A dry run now prints a would-do log naming each path it would move, archive or permanently remove -- and moves, archives and removes nothing. saferm --dry-run delete, --dry-run undelete and --dry-run purge are all previews you can trust.
  • The documentation site gained an architecture page and a configuration guide. The architecture page walks the archival, restore and purge paths and the database schema; the configuration guide covers ~/.saferm/config.toml, SAFERM_HOME and the metadata denylist. The exit-code table is now rendered from one source instead of hand-maintained prose, and the pages carry the descriptions and titles that search engines were warning about.
  • **Every flag and argument in --help now says what it is for.** The one-word help strings (Recursive deletion, Filter by path) are gone; each flag, argument and command carries a sentence explaining what it does and when to reach for it.
  • **SAFERM_HOME is now a declared infrastructure root.** It appears in --help under its own Infrastructure: section, and is explicitly exempt from --hermetic: it selects where saferm lives, which is a different question from the config values that select how saferm behaves.
  • **--quiet now actually silences saferm.** The framework-owned flag was accepted and ignored: saferm --quiet delete still printed its summary. It now suppresses the counted summaries, the --verbose per-item progress, Nothing to purge. and the Restored <path> confirmation, and it dominates --verbose when both are passed. It never suppresses what you asked for: list and info output, the --dry-run previews and would-do log, purge's listing of what it is destroying, and everything on stderr.

#Fixes

  • **Docs no longer show the retired undelete --force spelling.** The flag is --force-overwrite; the old form was a parse error exactly when someone was trying to recover a file.
  • A dry run no longer creates saferm's state directory. saferm --dry-run delete on a machine with no archive yet made ~/.saferm/, ~/.saferm/archive/ and ~/.saferm/db/ on its way to promising it would touch nothing, and never mentioned them. The creation is now declared like every other mutation: named in the would-do log, performed only in a real run.
  • **saferm list on a machine that has never deleted anything now says so.** It exited 5 with error: opening database: unable to open database file -- SQLite's answer to a missing file, offered as the answer to "what have I deleted?". An absent archive is an empty one: list reports no archived items and exits 0, and info reports no such record and exits 3.

#0.7.1

Fix Windows build break so releases publish for all platforms; correct config-conflict docs.

Context

v0.6.2 and v0.7.0 Publish workflows failed because goreleaser builds Windows but readParentCmdline was defined only under linux/darwin build tags, leaving it undefined on windows. Added a non-linux/darwin stub returning an honest empty (unavailable) value. Also corrected the config-conflict documentation to note that the CLI/config divergence check only fires for pre-command global flag placement, not post-command.

#Fixes

  • Windows binaries build again. Fixed a build break (undefined readParentCmdline) that prevented Windows binaries from compiling, so releases now publish for all platforms.
  • Corrected config-conflict docs. The CLI/config divergence protection only applies when a global flag is given in the pre-command position; the docs now state this caveat instead of implying it always applies.

#0.7.0

Adopt strictcli v0.21.0 config safety: malformed config.toml is now a hard error with parse position (was a silent warning), unknown config keys are rejected, and diverging CLI/config values for --archive-dir/--db-path conflict instead of silently letting the CLI win.

Context

saferm previously pinned strictcli v0.17.0, where a broken config.toml printed a warning and ran with default paths -- silent degradation that could send deletions to the wrong archive without the user noticing. This release bumps to strictcli v0.21.0 and deliberately adopts its config-safety primitives:

  • Malformed config is a hard error (exit 1) with parse position. This is the one

behavior change that arrives with the bump; it is announced as breaking because a config that previously "worked" (silently ignored) now fails loudly.

  • ConfigFields for archive_dir/db_path enable unknown-key rejection (catches typos

like archve_dir that were silently dropped).

  • Per-flag ConflictMode("error") on archive-dir/db-path makes a diverging CLI vs

config value a conflict; identical values are still accepted (divergence-aware). exclude-env-patterns stays cli-wins (it is a list; ConfigField supports scalars only).

Config-layer failures exit 1 (strictcli's layer); saferm's own semantic conflicts remain exit 7. SAFERM_HOME stays infrastructure, not config, and is not suppressed by --hermetic.

#Breaking

  • **Malformed config.toml is now a hard error.** A syntactically invalid ~/.saferm/config.toml previously printed a warning and silently ran with default archive/database paths. It now fails immediately (exit 1) reporting the parse position, so a broken config can never be ignored.

#Features

  • **Conflicting CLI and config values for --archive-dir/--db-path now error.** When a CLI flag value diverges from the value set in config.toml, saferm reports a conflict (exit 1) instead of silently letting the CLI win. Identical values are accepted.

#Fixes

  • Unknown config keys are rejected. A typo'd or unrecognized key in config.toml (for example archve_dir) is now a hard error instead of being silently ignored, surfacing misconfiguration that previously left settings unapplied.

#0.6.2

CI fix: generate missing pypi/uv.lock

#Infrastructure

  • CI fix: generate missing pypi/uv.lock

#0.6.1

Fix CI workflow for PyPI wrapper package

#Infrastructure

  • Fix CI workflow for PyPI wrapper package

#0.6.0

Purge preserves metadata audit trail

#Breaking

  • Breaking. Migrate to strictcli v0.17.0: rename flags and add explicit bool defaults.

#Features

  • New feature. Purge now keeps the metadata record (audit trail) and only deletes archived content. Purged items appear in list --all with 'purged' status.

#0.5.0

Purge size filtering and dry-run preview

#Features

  • New feature. saferm purge --larger-than SIZE filters by file size (e.g. 100MB, 1GB). saferm purge --dry-run previews what would be purged without deleting.

#0.4.0

Config migration to strictcli

Context

Config is now managed by strictcli's built-in config system instead of a hand-rolled TOML loader. The config file stays at ~/.saferm/config.toml with the same keys, but you get config show/set/path/edit subcommands for free. The internal/config package is removed.

#Breaking

  • Breaking. Removed internal/config package. Config is now handled entirely by strictcli flags (--archive-dir, --db-path, --exclude-env-patterns).

#Features

  • New feature. Config is now managed by strictcli's built-in config system. New subcommands: saferm config show, saferm config set, saferm config path, saferm config edit. Config file remains at ~/.saferm/config.toml with the same keys.
  • Upgrade. strictcli framework upgraded to v0.12.1 (array config support, repeatable flag defaults fix).

#0.3.0

Symlink support and selfdoc root file templates

Context

Standalone symlinks are now archived correctly instead of following the link target. Previously, archiving a symlink would hash and store the target's content (or fail entirely for dangling symlinks). Now saferm preserves the symlink target path and recreates it on restore. Also adopted selfdoc templates for CLAUDE.md and README.md.

#Features

  • New feature. Symlinks are now archived by preserving the link target path instead of following the symlink. Dangling symlinks, relative symlinks, and symlinks to directories are all handled correctly. Restore recreates the original symlink.
  • New feature. saferm list now shows type indicators: [sym] for symlinks, [dir] for directories.
  • New feature. saferm info now shows symlink type and target path for symlink deletions.

#Fixes

  • Fix. saferm purge now correctly removes .symlink metadata files from the archive.

#0.2.1

Dependency upgrades and housekeeping

#Features

  • Upgrade. strictcli framework upgraded to v0.11.0.

#0.2.0

#Features

  • Automatic git index management. When deleting a git-tracked file, saferm now runs git rm --cached to stage the removal. When restoring via undelete, it runs git add. Use --no-git on delete to suppress. Both operations are best-effort -- failures produce warnings, not errors.

#0.1.3

  • No user-facing changes.

#0.1.2

#Features

  • New feature. npm and PyPI binary wrapper packages for easier installation via npm install -g saferm and pipx install saferm.

#0.1.1

#Features

  • New feature. SAFERM_HOME environment variable allows overriding the default config/data directory, enabling isolated testing and custom configurations.

#0.1.0

#Features

  • Feature. Safe file deletion with automatic archival
  • Feature. Undelete command to restore archived files
  • Feature. List, purge, and info commands for archive management
  • Feature. Rich metadata capture: environment, git context, parent process
  • Feature. SQLite database with WAL mode for concurrency safety
  • Feature. Tar+zstd compression for archived directories
  • Feature. Configurable environment variable denylist
Search