

Keep everything you must back up in one private GitHub repo, and a new PC setup takes 45 minutes instead of a full day. Winget installs the apps, symlinks bring back the settings, and one more clone restores Claude.
One private GitHub repo holds every file you wrote: the app list, the scripts, the configs and your Claude setup.
Winget installs 35 apps from that list in 15 minutes, with no download pages.
Symlinks point each app at its config file in the repo, so a settings change becomes a commit.
Keys stay out of git. The repo keeps an example file with every key name, and you refill the values by hand.
A restore you never tested proves nothing, so run it in Windows Sandbox once a quarter.

Picture the same new laptop set up twice. From memory, you hunt for 35 installers, redo each setting, and re-explain your rules to Claude for about 8 hours. From the repo, you run two scripts and clone one more repo in about 45 minutes.
Jean-Michel Lemieux watched an agent set up his new work laptop, from the dev environment to the repos.
One Private GitHub Repo Holds Everything You Must Back Up
My rule is short. If I wrote a file or changed a setting, it lives in a GitHub repo. That covers the Winget app list, the install and restore scripts, every config file and the README.
I run the same rule for Claude. My global Claude config and my global skills live in one private repo, cloned straight into the .claude folder in my home directory.
Two things stay out on purpose: secret keys, and bulky data such as notes and databases.

Rule of thumb. When you change a setting, move its file into the repo the same day and link it back. A setting saved anywhere else never comes back, because no restore looks there.
Step 1. Install Git, the One Tool a Fresh Windows Lacks
A fresh Windows install ships with Winget but without Git. Open PowerShell as Administrator, install Git with Winget, and clone your setup repo to C:\dotfiles. Every later step reads from that folder.
Step 2. Install 35 Apps From One Winget List
Winget is the package manager built into Windows. It installs an app by its package ID, so you never visit a download page. One script installs all 35 in 15 minutes.
Productivity
Obsidian, the base of my second brain, one local folder of plain-text notes
TickTick, for tasks
Flow Launcher, to open files and apps from the keyboard
Communication
Slack, for work
Discord, for communities
Telegram, for everything else
Browsers and privacy
Brave, the main browser, with Chrome extensions
Proton VPN, for networks I do not trust
Development
VS Code, the main editor
Sublime Text, to open a single file faster
Docker, to run containers
Node.js, the JavaScript runtime
Python 3.12, the Python runtime
MongoDB, for local document databases
SQLite, for local file databases
Media
VLC, to play every format
Audacity, to edit audio
MediaMonkey 5, to keep the music library tagged
OBS Studio, to record the screen
Upscayl, to upscale images on the local GPU
qBittorrent, for downloads
System utilities
WizTree, to find the files that fill a disk
SyncThing, to sync folders between machines
CCleaner, to clear junk files
PDF24 Creator, to merge and split PDFs offline
Calibre, to manage ebooks
Command-line tools
FFmpeg, to convert media
yt-dlp, to download video
Gallery-DL, to download image galleries
ImageMagick, to edit images in batches
Rclone, to sync files to Google Drive and Dropbox
Pandoc, to convert Markdown to PDF or Docx
Zoxide, a
cdthat learns your foldersRipgrep, to search a whole codebase in seconds
Bat, to print files with syntax colors
Tip. Winget IDs change when a publisher renames a package. If one app fails, search Winget for its name and update the ID in the list.
Scott Hanselman of Microsoft uses the same package manager to update every app at once.
Winget can also export every app on your old machine to one file, which gives you a first draft of the list.
Microsoft's WinGet team shows how to export a machine setup to one file and apply it on another machine.
Step 3. Link Every Setting Back to the Repo With Symlinks
Apps keep their settings in scattered AppData folders. Copy each config folder into the repo once, on the old machine. On the new machine, the restore script renames any default config to .bak and links the repo copy in its place.

Open VS Code after the script runs, and your theme and keybindings are already there. Only seven of my apps need a config copied, and every other app keeps the Winget default. A .gitignore keeps logs, databases and credentials out of the repo.
Step 4. Handle the Three Apps That Refuse a Symlink
Obsidian keeps its config in the
.obsidianfolder inside the vault. Point the app at the synced vault and skip the link.Brave encrypts profile data with a key tied to one machine, so a linked profile breaks saved logins. Use Brave Sync for bookmarks and extensions, and keep an exported bookmarks file as a spare.
Python and Node.js need their global packages. Add your
npm install -gandpip installlines to the end of the bootstrap script.
Step 5. Sync Notes and Databases With SyncThing Instead of Git
SyncThing syncs folders between machines peer to peer, with no cloud server in the middle. Pair it with the old machine, and your notes and databases copy across. Then point Obsidian at the synced notes folder.
Git stores each binary file whole on every change, so the repo grows and the history stops being readable. SyncThing keeps no history, which suits data you never compare line by line.
Gary Explains walks through a SyncThing setup across Windows, macOS, Linux and Android.
Watch out. A full disk image looks safer, and it fails twice. It carries years of junk back with it. It also carries profile data locked to the old machine, so Brave logins break anyway.
Step 6. Clone Your Claude Config Repo Into Your Home Folder
Claude Code is Anthropic's AI coding assistant that runs in your terminal. With no setup, it forgets you every session. A harness is Claude plus the files it reads when a session starts, and mine has four kinds:
CLAUDE.md, the plain-text rules Claude reads at the start of every sessionSkills, one folder each, with a
SKILL.mdprompt that loads when a request matches itMCP servers, small programs that give Claude a new tool, such as a web scraper (MCP stands for Model Context Protocol)
Routines, prompts that run on a schedule, such as a daily report
Install Git, Node.js and Claude Code first, because the tool setup needs all three. Then clone the private repo into the .claude folder and run the restore script inside it. The script skips any file that already exists, reinstalls each command-line tool, and registers its MCP server.

Everything Claude Code is a public example of the same idea: one repo of skills, memory and security config. I list it on my tools page.

The full backup and version control guide lives in the 5 files that give Claude Code permanent memory.
Step 7. Refill Every Key From the Example File
All API keys sit in one .env file that git ignores. A committed .env.example lists every key name with an empty value. Copy it to .env, paste each key back in, and rerun the tool setup so each command-line tool signs in.
Claude's login token never goes in the repo either, so you sign in again. A sync script copies the live folder back into the repo and blanks each secret, and you review the diff before every commit.
Step 8. Reschedule Every Routine From Its File
A routine registered only inside a Claude account disappears when you switch accounts. One social scheduling tool I use deletes an uploaded image after 7 days unless a post uses it.
So keep each routine as a Markdown file in the repo, with its schedule in the header block and its prompt in the body. On a new machine, point Claude at the folder and ask it to schedule every routine again.
Step 9. Test the Restore in Windows Sandbox Once a Quarter
A setup that works on the machine that built it proves nothing. The old machine still holds every file you forgot to save.
Windows Sandbox is the throwaway desktop built into Windows Pro. Run the restore there, write down every question you had to answer from memory, and add each answer to the README.
Prune old rules during the same test. Rule Rewriter, a $19 skill in my skill pack, audits CLAUDE.md and skill files for rules written for older models.

Four Questions Decide Whether This Setup Fits Your Machine
Does this work on Windows Home?
Winget and symlinks work on Home. Creating a symlink needs an Administrator PowerShell or Developer Mode turned on. Windows Sandbox needs Pro, so on Home run the test in a virtual machine.
Why a private repo and not a public one?
Config files leak your email, your machine paths and your tool list. Keep the repo private, and publish a cleaned copy only to share it.
Does the approach carry over to macOS?
It does. A Homebrew Brewfile replaces the Winget list, and ln -s replaces the PowerShell symlink command. The rule stays the same: one repo holds every file you wrote.
How do I keep the repo current?
Commit when you change a setting. The symlink already put the change in the repo, so you only commit and push.
Appendix. Every Script, Path and Command
Install Git
winget install --id Git.Git -ebootstrap.ps1 Installs All 35 Apps
Run it as Administrator.
# Run as Admin check
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Error "Run this script as Administrator!"; return
}
$apps = @(
# Productivity
"Obsidian.Obsidian", "Appest.TickTick", "SlackTechnologies.Slack", "Discord.Discord",
"Telegram.TelegramDesktop", "Flow-Launcher.Flow-Launcher",
# Browsers & VPN
"Brave.Brave", "Proton.ProtonVPN",
# Dev & Terminal
"Microsoft.VisualStudioCode", "SublimeHQ.SublimeText.4", "Docker.DockerDesktop",
"OpenJS.NodeJS", "Python.Python.3.12", "MongoDB.Server", "SQLite.SQLite",
# Media & Creativity
"VideoLAN.VLC", "Audacity.Audacity", "VentisMedia.MediaMonkey.5",
"OBSProject.OBSStudio", "Upscayl.Upscayl", "qBittorrent.qBittorrent",
# Utilities
"AntibodySoftware.WizTree", "Syncthing.Syncthing", "Piriform.CCleaner",
"geeksoftwareGmbH.PDF24Creator", "calibre.calibre",
# CLI Power Tools
"Gyan.FFmpeg", "yt-dlp.yt-dlp", "mikf.gallery-dl", "ImageMagick.ImageMagick",
"Rclone.Rclone", "JohnMacFarlane.Pandoc", "ajeetdsouza.zoxide",
"BurntSushi.ripgrep.MSVC", "sharkdp.bat"
)
foreach ($app in $apps) {
Write-Host "Installing $app..." -ForegroundColor Cyan
winget install --id $app --silent --accept-source-agreements --accept-package-agreements
}Export the App List From an Old Machine
winget export -o apps.jsonSeven Config Paths to Copy Into the Repo
Obsidian:
[YourVault]/.obsidian, withappearance.jsonandplugins.jsonVS Code:
%AppData%/Code/User/, withsettings.jsonandkeybindings.jsonFlow Launcher:
%AppData%/FlowLauncher/, withSettings/andPlugins/Zoxide:
%LocalAppData%/zoxide/, withdb.json, your folder historyMediaMonkey:
%AppData%/MediaMonkey/, withMM5.db, your library metadatayt-dlp:
%AppData%/yt-dlp/, withconfig.txt, your default download settingsBrave:
%LocalAppData%/BraveSoftware/Brave-Browser/User Data/Default, withBookmarksandLogin Data
A Quick Symlink Script
# Define where your synced configs live
$syncFolder = "C:\Backup\Config"
# Function to create the link
function Set-Link($appPath, $backupPath) {
if (Test-Path $appPath) { Remove-Item -Recurse -Force $appPath }
New-Item -ItemType SymbolicLink -Path $appPath -Target $backupPath
}
# Apply Links
Write-Host "Restoring App Settings via Symlinks..." -ForegroundColor Green
# 1. VS Code Settings
Set-Link "$env:APPDATA\Code\User" "$syncFolder\vscode"
# 2. Flow Launcher (Everything: plugins, history, themes)
Set-Link "$env:APPDATA\FlowLauncher" "$syncFolder\FlowLauncher"
# 3. yt-dlp Global Config
Set-Link "$env:APPDATA\yt-dlp" "$syncFolder\yt-dlp"
# 4. Git Config (Crucial for your dev work)
Set-Link "$env:USERPROFILE\.gitconfig" "$syncFolder\git\.gitconfig"Repo Layout
.
โโโ scripts/
โ โโโ bootstrap.ps1 # Installs all apps (Winget)
โ โโโ restore.ps1 # Creates the symlinks
โโโ configs/
โ โโโ vscode/
โ โ โโโ settings.json
โ โ โโโ keybindings.json
โ โโโ flow-launcher/ # Folder for Flow settings
โ โโโ git/
โ โ โโโ .gitconfig
โ โโโ obsidian/ # Optional: Global snippets or CSS
โโโ .gitignore # CRITICAL: Prevents pushing secrets
โโโ README.md # Instructions for your future self.gitignore
# Ignore local databases and logs
*.log
*.db
*.sqlite
# Ignore sensitive VS Code data
configs/vscode/backups/
configs/vscode/workspaceStorage/
# Ignore actual Obsidian Vaults (Keep those in Syncthing/Proton Drive)
# Only track global Obsidian CSS or templates here if desired.
vaults/
# Ignore sensitive Git credentials
.git-credentialsOne-Line Deploy on a New Laptop
git clone https://github.com/YourUsername/dotfiles-windows.git C:\dotfiles; & C:\dotfiles\scripts\bootstrap.ps1; & C:\dotfiles\scripts\restore.ps1restore.ps1 Links Configs From Any Clone Path
$repoRoot = (Get-Item $PSScriptRoot).Parent.FullName
$configSource = "$repoRoot\configs"
function Set-Link($targetPath, $sourcePath) {
if (Test-Path $targetPath) {
Write-Host "Backing up existing config for $(Split-Path $targetPath -Leaf)..."
Move-Item $targetPath "$targetPath.bak" -Force
}
# Ensure the parent directory exists
$parentDir = Split-Path $targetPath -Parent
if (!(Test-Path $parentDir)) { New-Item -ItemType Directory -Path $parentDir }
New-Item -ItemType SymbolicLink -Path $targetPath -Target $sourcePath
Write-Host "Linked: $targetPath" -ForegroundColor Gray
}
# -- Execute Links ---
Write-Host "Linking configurations from $configSource..." -ForegroundColor Cyan
# VS Code
Set-Link "$env:APPDATA\Code\User\settings.json" "$configSource\vscode\settings.json"
# Git
Set-Link "$env:USERPROFILE\.gitconfig" "$configSource\git\.gitconfig"
# Flow Launcher
Set-Link "$env:APPDATA\FlowLauncher\Settings" "$configSource\flow-launcher\Settings"
Write-Host "Migration Sync Complete!" -ForegroundColor GreenThe .bak file holds one prior version only. Run the script twice on the same machine and the second .bak replaces the first.
Commit a Settings Change
cd C:\dotfiles
git add .
git commit -m "Updated VS Code theme"
git push





