skillmirror
Concepts

بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ

Safety and undo

How a folder is replaced in one step, what the backup keeps, how undo works, and what skillmirror will not write.

skillmirror writes into many projects at once, so the rules that keep it safe are part of how it works and not an option. This page lists them.

One step or nothing

A skill folder is replaced by building the new copy beside the old one and swapping the two folders in a single step (renameat2 with the exchange flag). A project therefore has the old folder or the new one, never a mixture, and a crash never leaves a half written skill.

Some filesystems cannot swap folders this way: NFS, CIFS, FUSE and FAT. doctor names them in its filesystem check.

There is no fsync after each file. A killed process leaves no half skill, but a power loss in the middle of a run may. Skills are git-tracked copies, so the next sync restores them.

A plan first, then a question

Every command that writes works out its plan first. --dry-run prints it and stops. Without --dry-run it asks [y/N], and --yes skips the question. The interface asks too, and shows the changes behind v.

When no terminal can answer, a command that would write refuses instead of guessing:

skillmirror sync < /dev/null
error: refusing to write without confirmation
hint: pass --yes to apply, or --dry-run to preview

The exit code is 2. A plan that would write nothing needs no answer and goes straight through. --json cannot ask either, so it needs --yes or --dry-run as well.

The backup

Before a run replaces or removes a skill folder, the old copy goes to the backup store. A run is one command: a sync over twelve projects is one run, and one undo takes it all back.

~/.local/state/skillmirror/backups/
  20261103-212804-051-131217-0/     one run
    0/                              the first folder the run replaced
      entry.json                    the project, the skill and what the run did to it
      tree/                         the old copy of the folder
        SKILL.md
    1/
      ...
  • The store lives under $XDG_STATE_HOME/skillmirror/backups, and ~/.local/state/skillmirror/backups when that variable is not set.
  • The newest 30 runs are kept. A run that stored nothing leaves no folder.
  • A skill the run created has a note and no tree, so undoing the run removes it.
  • If the old copy cannot be stored, that target fails and the old copy stays in place. The other targets go on.
  • sync, push, add, init and delete make runs. So do promote (the old vault folder) and agents sync and agents add (the old AGENTS.md).
skillmirror history
skillmirror undo            # the newest run
skillmirror undo RUN        # a run from the list

undo shows what it would restore and asks. --project DIR and --skill NAME narrow it, --dry-run only shows, --yes skips the question. An undo is itself a run: it saves what it replaced, prints that run's id, and a second skillmirror undo redoes the first. A run that is fully undone leaves the list, and the run that saved what the undo replaced takes its place.

What undo does not take back

init makes a folder, a repository and a scaffold. undo removes the skills and the AGENTS.md of that run, and the folder, the repository and the scaffold stay. new, adopt and vault init only create files that did not exist and stage them, so there is nothing to restore: unstage or remove them with git.

Edits made while a plan waits

A plan is exact. If a project folder changed between the plan and the write, that skill fails with a message and the other targets continue. The same check guards the vault when promote swaps a skill folder, and the file when agents sync rewrites an AGENTS.md.

Links are never followed and never written through. A linked project folder, a linked .agents or a linked .agents/skills is invisible to a sync, so nothing outside a project can be replaced by accident. The one link skillmirror makes is the .claude/skills bridge, and only where nothing is in the way (Other agent folders).

What may write into the vault

Only the commands that grow or configure it. new, adopt, promote and vault init create files that did not exist and stage them with git add. promote is the one that replaces a folder, and only after it filed the old one in the backup. agents seed writes one file and leaves it unstaged. The configuration commands (config root, config web, mandatory add, mandatory remove) edit config.yaml, and they edit its text, so your comments, the key order and every other key stay. None of these commands commits.

What is left of an interrupted run

A run that was killed can leave a .stage-… or .trash-… folder beside skills in a project's .agents folder. Every scan reports them, doctor lists them under leftovers, and status prints a warning:

! leftovers ~/projects/infra/.agents/.stage-4294967000-abc-0: left over from an interrupted run; delete it by hand

Everything the scan could not read is reported the same way and never hidden.

On this page