Skill: Versionamento de Arquivos de Configuração
Faz backup automático de versões de arquivos de configuração local que são "pequenos + importantes + que você não quer que sejam sobrescritos silenciosamente".
Cada vez que o conteúdo de um arquivo monitorado muda de fato, um commit git é gerado automaticamente em até 10 segundos, mantido em um repositório local separado (separate-git-dir). Reverta para qualquer versão anterior com um comando.
Quando Usar
Use esta skill quando o usuário apresentar qualquer um dos seguintes sinais de gatilho:
- "Adiciona versionamento/backup automático para "
- "Não quero que algum programa sobrescreva meu de novo"
- "Faz commit automático quando mudar"
- "Usa <diretório de backup existente> para fazer backup de "
- O usuário menciona que uma config importante foi perdida por sobrescrita acidental e pergunta como prevenir no futuro
Não é adequado para:
- Repositórios de código grandes (use o fluxo de trabalho git normal)
- Sincronização entre máquinas (esta skill só mantém histórico local; para sincronizar, adicione Syncthing ou git push manual para um remote privado)
- Arquivos que mudam com alta frequência (
.bash_history,history.jsonl, etc. — gerariam commits em massa)
Visão Geral da Arquitetura
<Arquivo Protegido> (local real, ex.: ~/.ssh/config)
│ alteração
▼
Observador de Sistema de Arquivos (Nível de SO)
┌──────────────────────────────┬──────────────────────┬─────────┐
│ macOS: launchd WatchPaths │ Linux: systemd .path │ Outros │
└──────────────────────────────┴──────────────────────┴─────────┘
│ disparo (após throttle de 10s)
▼
auto-commit.sh
│ git add -A → só faz commit se houver diff no staged
▼
<BACKUP_ROOT>/<dominio>-config.git/ ← repositório git real (separate-git-dir)
▲
│ conectado pelo ".git" (uma linha "gitdir: <caminho>") na raiz da worktree
Pontos-chave de design:
| Design | Motivo |
|---|---|
| Cada domínio de config tem seu próprio repositório (não unificado em um grande repositório) | Histórico limpo; pode ser ativado/desativado/migrado independentemente |
| separate-git-dir | Quando a worktree inteira é deletada por algum programa, o histórico continua vivo em <BACKUP_ROOT> |
.gitignore de lista branca (* + !<arquivo_protegido>) | Evita que dados de runtime, cache ou arquivos temporários no mesmo diretório sejam incluídos acidentalmente |
| Observação orientada a eventos + throttle (padrão 10s) | Sem processos residentes em idle; evita múltiplos commits para uma única escrita |
Verificação git diff --cached --quiet no script de commit | Sai silenciosamente se o conteúdo realmente não mudou (apenas mtime alterado) |
Passos de Implementação
Passo 0: Alinhar 4 variáveis com o usuário
| Variável | Significado | Valor Padrão Recomendado |
|---|---|---|
<DOMINIO> | Abreviação do domínio de config (apenas letras minúsculas, números e hífens; usado como nome de diretório/sufixo de label) | Discutir com o usuário; ex.: ssh / zsh / vscode / claude |
<WORKTREE> | Caminho absoluto do diretório onde está o arquivo protegido (raiz da git worktree) | Geralmente o diretório onde está o arquivo monitorado |
<ARQUIVOS> | Lista de caminhos absolutos a serem monitorados (≥ 1) | Especificado pelo usuário |
<BACKUP_ROOT> | Diretório raiz para armazenar repositórios, scripts e templates de serviço | Perguntar obrigatoriamente: sugestão padrão ~/.local/share/config-versioning/<DOMINIO>/ (convenção XDG); se o usuário já tiver um diretório dedicado para backup/sincronização (ex.: ~/Documentos/backups/, ~/Sync/..., seu próprio diretório sandbox), deixar o usuário decidir |
Passo 1: Criar diretório de dados + iniciar repositório git
mkdir -p "<BACKUP_ROOT>"
mkdir -p "<WORKTREE>"
cd "<WORKTREE>" && git init -q -b main
Passo 2: Escrever .gitignore de lista branca
Copie templates/gitignore-whitelist para <WORKTREE>/.gitignore e adicione, para cada item em <ARQUIVOS>, uma linha !<caminho-relativo> relativa a <WORKTREE>.
Exemplo: worktree=~/.ssh, proteger ~/.ssh/config e ~/.ssh/known_hosts, adicione:
!config
!known_hosts
Passo 3: Commit inicial
cd "<WORKTREE>"
git add -A
git commit -q -m "chore: snapshot inicial da config <DOMINIO>"
Não use
-c user.name/-c user.emailpara forçar sobrescrita — deixe o git usar a configuração global do usuário. O repositório é puramente local, não é feito push, o autor do commit é irrelevante.
Passo 4: Mover o diretório .git para <BACKUP_ROOT> e criar ponteiro gitdir
GITDIR="<BACKUP_ROOT>/<DOMINIO>-config.git"
mv "<WORKTREE>/.git" "$GITDIR"
echo "gitdir: $GITDIR" > "<WORKTREE>/.git"
git -C "$GITDIR" config core.worktree "<WORKTREE>"
Passo 5: Copiar e adaptar o script auto-commit
Copie templates/auto-commit.sh para <BACKUP_ROOT>/auto-commit.sh e substitua os 2 placeholders:
| Placeholder | Substituir por |
|---|---|
__WORKTREE__ | Caminho absoluto de <WORKTREE> |
__LOG__ | Caminho do log; sugestão: ~/Library/Logs/config-file-versioning.log (macOS) ou ~/.local/state/config-file-versioning.log (Linux) |
chmod +x no arquivo.
Passo 6: Instalar camada de observação (por sistema operacional)
6a. macOS (launchd LaunchAgent)
Copie templates/launchagent.plist, substitua os placeholders:
| Placeholder | Substituir por |
|---|---|
__LABEL__ | local.<USUARIO>.<DOMINIO>-config-watch (prefixo local. evita conflitos; <USUARIO> usar $USER) |
__SCRIPT_PATH__ | Caminho absoluto de <BACKUP_ROOT>/auto-commit.sh |
__WATCH_PATHS__ | Cada arquivo em <ARQUIVOS> dentro de um par <string>...</string>, agrupados em <array> |
__LOG__ | Mesmo do passo 5 |
Salve em dois lugares (garantindo portabilidade do diretório):
<BACKUP_ROOT>/<LABEL>.plist(template de backup)~/Library/LaunchAgents/<LABEL>.plist(ativo)
Carregar:
launchctl unload "$HOME/Library/LaunchAgents/<LABEL>.plist" 2>/dev/null
launchctl load "$HOME/Library/LaunchAgents/<LABEL>.plist"
launchctl list | grep "<LABEL>" # verificar se está registrado
6b. Linux (systemd path unit)
Copie templates/systemd.path e templates/systemd.service para ~/.config/systemd/user/, renomeando para <DOMINIO>-config-watch.path e <DOMINIO>-config-watch.service, substituindo os placeholders conforme os arquivos. Depois:
systemctl --user daemon-reload
systemctl --user enable --now <DOMINIO>-config-watch.path
6c. Outros / Alternativa
Qualquer ferramenta que "observe + execute comando" serve:
- gitwatch (shell cross-platform):
brew install gitwatchouapt install gitwatch - fswatch + daemon próprio:
fswatch <ARQUIVOS> | xargs -n1 <BACKUP_ROOT>/auto-commit.sh - inotifywait (Linux) + loop shell
Hospede o processo do observador no gerenciador de serviços da plataforma (OpenRC, runit, launchd, systemd).
Passo 7: Teste ponta a ponta (obrigatório, não pule)
Altere um arquivo monitorado, espere 2 segundos a mais que o intervalo de throttle, e verifique se o log e o git log foram atualizados. Primeiro injete um campo temporário, depois limpe-o, esperando 2 commits:
# Ex.: adicionar chave temporária _autoCommitTest em arquivo JSON
# sleep 12 (assumindo throttle=10s)
tail -2 "<LOG>"
git -C "<WORKTREE>" log --oneline | head -3
Passo 8: Escrever README
Cada <BACKUP_ROOT> deve conter um README.md para facilitar migrações futuras. Deve incluir no mínimo:
- Propósito (por que isso existe)
- Tabela de arquivos
- Comandos do dia a dia (ver histórico, reverter, status do observador, ver log)
- Manutenção (adicionar novo arquivo monitorado, alterar intervalo de throttle)
- Passos para recriar em uma máquina nova
- Passos completos de desinstalação
Anti-padrões (evitar explicitamente)
| ❌ Errado | ✅ Correto |
|---|---|
| Colocar múltiplos domínios de config no mesmo repositório git | Um repositório por domínio |
Manter .git dentro da worktree | Mover para <BACKUP_ROOT> usando ponteiro gitdir |
Usar .gitignore de lista negra | Usar .gitignore de lista branca |
| Pular o teste ponta a ponta antes de entregar | Obrigatório, confirmar que o commit realmente é gerado |
Colocar arquivos plist/service apenas no diretório do sistema, sem template em <BACKUP_ROOT> | Colocar em ambos, garantindo que o diretório seja autocontido e portátil |
| Fazer push para remote público no commit | Proibido — config pode conter tokens/informações sensíveis, repositório puramente local |
Hardcodar git user.name/email no auto-commit.sh | Deixar para a configuração global do git; o script não deve conter informações pessoais |
Usar KeepAlive=true para rodar fswatch residente | Priorizar observação orientada a eventos (WatchPaths / .path unit), sem processo residente |
Tratamento de Casos Limite
- Diretório worktree não existe: Execute
mkdir -pprimeiro. Se for a primeira vez, o arquivo monitorado pode nem existir — pergunte se deve criar um arquivo vazio. - Arquivo monitorado é um link simbólico: No observador, monitore tanto o caminho do link quanto o caminho real de destino, para evitar que alguns métodos de escrita não disparem um dos tipos.
- Arquivo monitorado é binário: Funciona normalmente (git armazena blob), mas o diff não mostra o conteúdo.
- Mesmo label/serviço já existe: Primeiro faça unload/disable do antigo, depois carregue o novo.
- Múltiplas escritas dentro do intervalo de throttle: Múltiplas alterações dentro da janela de throttle são consolidadas em um único commit (é o comportamento desejado).
- Escrita atômica (write-temp + rename): launchd WatchPaths geralmente dispara corretamente; se a forma de escrever de algum programa específico causar perda de commit, oriente o usuário a usar KeepAlive + loop principal de gitwatch/fswatch.
Arquivos de Template
templates/auto-commit.sh— Esqueleto do script de commit automáticotemplates/gitignore-whitelist— Modelo de .gitignore de lista brancatemplates/launchagent.plist— LaunchAgent do macOStemplates/systemd.path+templates/systemd.service— Unidade systemd path do Linux
Filosofia de Design
- Visível + Legível + Editável manualmente: O histórico de versões é um repositório git; qualquer pessoa que entenda git pode localizar/reverter, sem depender de GUI.
- Orientado a eventos ≫ Polling: Reduz consumo de CPU/disco em idle.
- À prova de falhas: O script de commit não tem efeitos colaterais — falhas de
cd, diff vazio etc. saem silenciosamente, sem danificar a worktree. - Diretório autocontido:
<BACKUP_ROOT>contém dados + scripts + templates plist/unit + README; copie o diretório inteiro e recrie em uma máquina nova. - Não faz push: Evita enviar configurações privadas para qualquer remote; para backup remoto, use soluções em nível de arquivo (Time Machine / Backblaze / Syncthing com criptografia).