aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md67
-rw-r--r--windows/AppData/AppData/windows-terminal/settings.json590
-rw-r--r--windows/Documents/Documents/PowerShell/Microsoft.PowerShell_profile.ps1284
-rw-r--r--windows/Documents/Documents/PowerShell/bloatware.ps1335
-rw-r--r--windows/Documents/Documents/PowerShell/bootstrap.ps1669
-rw-r--r--windows/Documents/Documents/PowerShell/initialize.ps1227
-rw-r--r--windows/Documents/PowerShell/Microsoft.PowerShell_profile.ps1276
-rw-r--r--windows/Documents/PowerShell/bootstrap.ps1949
-rw-r--r--windows/install.bat13
9 files changed, 3020 insertions, 390 deletions
diff --git a/README.md b/README.md
index 34d96d5..776bbc8 100644
--- a/README.md
+++ b/README.md
@@ -42,31 +42,6 @@ Linux:
---
-## Usage Examples
-
-### Adding Files to Your Dotfiles
-
-```bash
-# Add a config file explicitly to the common directory in the repo
-config add --target common .bashrc
-
-# Add with a specific target directory
-config add --target windows/Documents/PowerShell ~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
-
-# Windows:
-config add --target windows/Documents/PowerShell "$env:USERPROFILE\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
-
-# Linux WSL or Git Bash:
-config add --target windows/Documents/PowerShell /mnt/c/Users/\<User\>/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
-
-# Add multiple files at once (each will be mapped appropriately)
-config add ~/.vim .tmux.conf # Will go to OS's home
-
-# Add files outside of home
-config add --target linux/etc /etc/issue
-
-```
-
## Installation Methods
### Method 1: Shell Scripts (Recommended)
@@ -106,12 +81,6 @@ ansible-playbook -i inventory.yml playbook.yml -e dotfiles_profile=dev
ansible-playbook -i inventory.yml playbook.yml --limit linux
```
-**Note:** Both installation methods include:
-- System hardening and security configurations
-- Kernel/OS/distribution update checking
-- Profile-based package installation
-- Development environment setup
-
### Method 3: Installing onto a new system (Manual)
1. Avoid weird behaviour/recursion issues when `.cfg` tries to track itself
@@ -742,30 +711,38 @@ config config --local status.showUntrackedFiles no
config deploy
```
+**Note:** All installation methods include:
+- System hardening and security configurations
+- Kernel/OS/distribution update checking
+- Profile-based package installation
+- Development environment setup
---
-### Auto-installer
+## Usage Examples
-Linux/macOS (one-liner):
+### Adding Files to Your Dotfiles
-```sh
-sh -c "$(curl -fsSL https://raw.githubusercontent.com/srdusr/dotfiles/main/common/install.sh)"
-```
+```bash
+# Add a config file explicitly to the common directory in the repo
+config add --target common .bashrc
-Windows PowerShell (one-liner):
+# Add with a specific target directory
+config add --target windows ~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
-```powershell
-Set-ExecutionPolicy Bypass -Scope Process -Force; irm 'https://raw.githubusercontent.com/srdusr/dotfiles/main/windows/Documents/PowerShell/bootstrap.ps1' | iex
-```
+# Windows:
+config add --target windows "$env:USERPROFILE\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
-Windows CMD (.bat alternative):
+# Linux WSL or Git Bash:
+config add --target windows /mnt/c/Users/\<User\>/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
-```bat
-REM From the cloned repo, run the batch installer (if present):
-call windows\Documents\install.bat
-```
+# Add multiple files at once (each will be mapped appropriately)
+config add ~/.vim .tmux.conf # Will go to OS's home
+
+# Add files outside of home
+config add --target linux/etc /etc/issue
+```
---
<details>
diff --git a/windows/AppData/AppData/windows-terminal/settings.json b/windows/AppData/AppData/windows-terminal/settings.json
new file mode 100644
index 0000000..71bf5bd
--- /dev/null
+++ b/windows/AppData/AppData/windows-terminal/settings.json
@@ -0,0 +1,590 @@
+{
+ "$help": "https://aka.ms/terminal-documentation",
+ "$schema": "https://aka.ms/terminal-profiles-schema",
+ "actions":
+ [
+ { "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },
+ { "command": "unbound", "keys": "ctrl+v" },
+ { "command": "unbound", "keys": "ctrl+c" },
+ { "command": { "action": "copy", "singleLine": false } },
+ { "command": { "action": "scrollUp" }, "keys": "ctrl+shift+up" },
+ { "command": "find", "keys": "ctrl+shift+f" },
+ { "command": "paste" },
+
+ { "command": { "action": "splitPane", "split": "vertical", "splitMode": "duplicate" }, "keys": "alt+shift+plus" },
+ { "command": { "action": "splitPane", "split": "horizontal", "splitMode": "duplicate" }, "keys": "alt+shift+-" },
+
+ { "command": { "action": "nextTab", "tabSwitcherMode": "mru" }, "keys": "ctrl+tab" },
+ { "command": "openTabRenamer", "keys": "ctrl+shift+r" },
+
+ { "command": { "action": "splitPane", "split": "auto", "splitMode": "duplicate" }, "keys": "alt+shift+d" },
+ { "command": { "action": "scrollDown" }, "keys": "ctrl+shift+down" },
+
+ { "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
+
+ { "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+h" },
+ { "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+j" },
+ { "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+k" },
+ { "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+l" },
+
+ { "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+y" },
+ { "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+u" },
+ { "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+i" },
+ { "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+o" },
+
+ { "command": { "action": "swapPane", "direction": "left" }, "keys": "alt+shift+h" },
+ { "command": { "action": "swapPane", "direction": "down" }, "keys": "alt+shift+j" },
+ { "command": { "action": "swapPane", "direction": "up" }, "keys": "alt+shift+k" },
+ { "command": { "action": "swapPane", "direction": "right" }, "keys": "alt+shift+l" },
+
+ { "command": "togglePaneZoom", "keys": "alt+f" }
+ ],
+ "copyFormatting": "none",
+ "copyOnSelect": false,
+ "defaultProfile": "{f19ccc1d-fe9a-4e79-ad18-49276786d144}",
+ "launchMode": "default",
+ "profiles":
+ {
+ "defaults":
+ {
+ "backgroundImage": null,
+ "colorScheme": "Gruvbox Dark",
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "historySize": 9001,
+ "opacity": 100,
+ "padding": "0",
+ "snapOnInput": false,
+ "useAcrylic": false,
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ "list":
+ [
+ {
+ "backgroundImage": "C:\\Users\\srdusr\\wallpapers\\download3.jpg",
+ "backgroundImageOpacity": 0.089999999999999997,
+ "colorScheme": "Tokyo Night",
+ "commandline": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
+ "experimental.retroTerminalEffect": false,
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{f19ccc1d-fe9a-4e79-ad18-49276786d144}",
+ "historySize": 99999,
+ "icon": "ms-appx:///ProfileIcons/pwsh.png",
+ "name": "PowerShell 7 - BG",
+ "opacity": 100,
+ "padding": "0",
+ "scrollbarState": "visible",
+ "startingDirectory": "%USERPROFILE%",
+ "suppressApplicationTitle": false,
+ "useAcrylic": false,
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "backgroundImage": "C:\\Users\\srdusr\\wallpapers\\photo-1533484306792-cf313c2b8ab0 (2).jpg",
+ "backgroundImageOpacity": 0.19,
+ "bellStyle": "taskbar",
+ "colorScheme": "Tokyo Night",
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{07b52e3e-de2c-5db4-bd2d-ba144ed6c273}",
+ "hidden": false,
+ "historySize": 99999,
+ "name": "Ubuntu-20.04",
+ "source": "Windows.Terminal.Wsl",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "guid": "{f9ceaf27-504c-58d7-927c-d1d6a7ac7d3c}",
+ "hidden": false,
+ "backgroundImage": "C:\\Users\\srdusr\\wallpapers\\photo-1533484306792-cf313c2b8ab0 (2).jpg",
+ "backgroundImageOpacity": 0.19,
+ "name": "Ubuntu 22.04.1 LTS",
+ "source": "CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc",
+ "colorScheme": "Tokyo Night",
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
+ "hidden": false,
+ "historySize": 99999,
+ "name": "PowerShell 7 - NO BG",
+ "padding": "0",
+ "source": "Windows.Terminal.PowershellCore",
+ "suppressApplicationTitle": false,
+ "useAcrylic": false,
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
+ "hidden": false,
+ "historySize": 99999,
+ "name": "Command Prompt",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
+ "hidden": false,
+ "historySize": 99999,
+ "name": "PowerShell 5",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{46ca431a-3a87-5fb3-83cd-11ececc031d2}",
+ "hidden": false,
+ "name": "Kali Linux",
+ "source": "Windows.Terminal.Wsl",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}",
+ "hidden": false,
+ "historySize": 99999,
+ "name": "Azure Cloud Shell",
+ "source": "Windows.Terminal.Azure",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "backgroundImage": null,
+ "backgroundImageOpacity": 0.29999999999999999,
+ "commandline": "C:\\GBox\\Applications\\Tools\\Applications\\Neovim\\nvim-win64\\0.6.0\\bin\\nvim.exe",
+ "font":
+ {
+ "face": "CaskaydiaCove NF"
+ },
+ "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6102}",
+ "hidden": false,
+ "historySize": 99999,
+ "name": "Neovim",
+ "padding": "0",
+ "useAcrylic": false,
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "guid": "{d701ea23-d39a-51a9-9966-855f1c8051f1}",
+ "hidden": false,
+ "name": "Developer Command Prompt for VS 2022 [Preview]",
+ "source": "Windows.Terminal.VisualStudio",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "guid": "{960e8c7a-d53d-5f77-ad6f-daf9dfeb597d}",
+ "hidden": false,
+ "name": "Developer PowerShell for VS 2022 [Preview]",
+ "source": "Windows.Terminal.VisualStudio",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "guid": "{fddd4872-371a-5869-85aa-657716af07fa}",
+ "hidden": true,
+ "name": "Developer Command Prompt for VS 2022",
+ "source": "Windows.Terminal.VisualStudio",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ },
+ {
+ "guid": "{e1c4148c-0ee2-5cd8-802c-8a170e188519}",
+ "hidden": true,
+ "name": "Developer PowerShell for VS 2022",
+ "source": "Windows.Terminal.VisualStudio",
+ "closeOnExit":"graceful",
+ "intenseTextStyle": "all"
+ }
+ ]
+ },
+ "schemes":
+ [
+ {
+ "background": "#0C0C0C",
+ "black": "#0C0C0C",
+ "blue": "#0037DA",
+ "brightBlack": "#767676",
+ "brightBlue": "#3B78FF",
+ "brightCyan": "#61D6D6",
+ "brightGreen": "#16C60C",
+ "brightPurple": "#B4009E",
+ "brightRed": "#E74856",
+ "brightWhite": "#F2F2F2",
+ "brightYellow": "#F9F1A5",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#3A96DD",
+ "foreground": "#CCCCCC",
+ "green": "#13A10E",
+ "name": "Campbell",
+ "purple": "#881798",
+ "red": "#C50F1F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#CCCCCC",
+ "yellow": "#C19C00"
+ },
+ {
+ "background": "#012456",
+ "black": "#0C0C0C",
+ "blue": "#0037DA",
+ "brightBlack": "#767676",
+ "brightBlue": "#3B78FF",
+ "brightCyan": "#61D6D6",
+ "brightGreen": "#16C60C",
+ "brightPurple": "#B4009E",
+ "brightRed": "#E74856",
+ "brightWhite": "#F2F2F2",
+ "brightYellow": "#F9F1A5",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#3A96DD",
+ "foreground": "#CCCCCC",
+ "green": "#13A10E",
+ "name": "Campbell Powershell",
+ "purple": "#881798",
+ "red": "#C50F1F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#CCCCCC",
+ "yellow": "#C19C00"
+ },
+ {
+ "background": "#282828",
+ "black": "#282828",
+ "blue": "#458588",
+ "brightBlack": "#928374",
+ "brightBlue": "#83A598",
+ "brightCyan": "#8EC07C",
+ "brightGreen": "#B8BB26",
+ "brightPurple": "#D3869B",
+ "brightRed": "#FB4934",
+ "brightWhite": "#EBDBB2",
+ "brightYellow": "#FABD2F",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#689D6A",
+ "foreground": "#EBDBB2",
+ "green": "#98971A",
+ "name": "Gruvbox Dark",
+ "purple": "#B16286",
+ "red": "#CC241D",
+ "selectionBackground": "#FFFFFF",
+ "white": "#A89984",
+ "yellow": "#D79921"
+ },
+ {
+ "background": "#282C34",
+ "black": "#282C34",
+ "blue": "#61AFEF",
+ "brightBlack": "#5A6374",
+ "brightBlue": "#61AFEF",
+ "brightCyan": "#56B6C2",
+ "brightGreen": "#98C379",
+ "brightPurple": "#C678DD",
+ "brightRed": "#E06C75",
+ "brightWhite": "#DCDFE4",
+ "brightYellow": "#E5C07B",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#56B6C2",
+ "foreground": "#DCDFE4",
+ "green": "#98C379",
+ "name": "One Half Dark",
+ "purple": "#C678DD",
+ "red": "#E06C75",
+ "selectionBackground": "#FFFFFF",
+ "white": "#DCDFE4",
+ "yellow": "#E5C07B"
+ },
+ {
+ "background": "#282C34",
+ "black": "#282C34",
+ "blue": "#61AFEF",
+ "brightBlack": "#282C34",
+ "brightBlue": "#61AFEF",
+ "brightCyan": "#56B6C2",
+ "brightGreen": "#98C379",
+ "brightPurple": "#C678DD",
+ "brightRed": "#E06C75",
+ "brightWhite": "#DCDFE4",
+ "brightYellow": "#E5C07B",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#56B6C2",
+ "foreground": "#DCDFE4",
+ "green": "#98C379",
+ "name": "One Half Dark2",
+ "purple": "#C678DD",
+ "red": "#E06C75",
+ "selectionBackground": "#FFFFFF",
+ "white": "#DCDFE4",
+ "yellow": "#E5C07B"
+ },
+ {
+ "background": "#FAFAFA",
+ "black": "#383A42",
+ "blue": "#0184BC",
+ "brightBlack": "#4F525D",
+ "brightBlue": "#61AFEF",
+ "brightCyan": "#56B5C1",
+ "brightGreen": "#98C379",
+ "brightPurple": "#C577DD",
+ "brightRed": "#DF6C75",
+ "brightWhite": "#FFFFFF",
+ "brightYellow": "#E4C07A",
+ "cursorColor": "#4F525D",
+ "cyan": "#0997B3",
+ "foreground": "#383A42",
+ "green": "#50A14F",
+ "name": "One Half Light",
+ "purple": "#A626A4",
+ "red": "#E45649",
+ "selectionBackground": "#FFFFFF",
+ "white": "#FAFAFA",
+ "yellow": "#C18301"
+ },
+ {
+ "background": "#FAFAFA",
+ "black": "#383A42",
+ "blue": "#0184BC",
+ "brightBlack": "#383A42",
+ "brightBlue": "#0184BC",
+ "brightCyan": "#0997B3",
+ "brightGreen": "#50A14F",
+ "brightPurple": "#A626A4",
+ "brightRed": "#E45649",
+ "brightWhite": "#FAFAFA",
+ "brightYellow": "#C18401",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#0997B3",
+ "foreground": "#383A42",
+ "green": "#50A14F",
+ "name": "One Half Light2",
+ "purple": "#A626A4",
+ "red": "#E45649",
+ "selectionBackground": "#FFFFFF",
+ "white": "#FAFAFA",
+ "yellow": "#C18401"
+ },
+ {
+ "background": "#002B36",
+ "black": "#002B36",
+ "blue": "#268BD2",
+ "brightBlack": "#073642",
+ "brightBlue": "#839496",
+ "brightCyan": "#93A1A1",
+ "brightGreen": "#586E75",
+ "brightPurple": "#6C71C4",
+ "brightRed": "#CB4B16",
+ "brightWhite": "#FDF6E3",
+ "brightYellow": "#657B83",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#2AA198",
+ "foreground": "#839496",
+ "green": "#859900",
+ "name": "Solarized Dark",
+ "purple": "#D33682",
+ "red": "#DC322F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#EEE8D5",
+ "yellow": "#B58900"
+ },
+ {
+ "background": "#001E27",
+ "black": "#002831",
+ "blue": "#2176C7",
+ "brightBlack": "#475B62",
+ "brightBlue": "#708284",
+ "brightCyan": "#819090",
+ "brightGreen": "#475B62",
+ "brightPurple": "#5956BA",
+ "brightRed": "#BD3613",
+ "brightWhite": "#FCF4DC",
+ "brightYellow": "#536870",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#259286",
+ "foreground": "#708284",
+ "green": "#738A05",
+ "name": "Solarized Dark - Patched",
+ "purple": "#C61C6F",
+ "red": "#D11C24",
+ "selectionBackground": "#FFFFFF",
+ "white": "#EAE3CB",
+ "yellow": "#A57706"
+ },
+ {
+ "background": "#FDF6E3",
+ "black": "#002B36",
+ "blue": "#268BD2",
+ "brightBlack": "#073642",
+ "brightBlue": "#839496",
+ "brightCyan": "#93A1A1",
+ "brightGreen": "#586E75",
+ "brightPurple": "#6C71C4",
+ "brightRed": "#CB4B16",
+ "brightWhite": "#FDF6E3",
+ "brightYellow": "#657B83",
+ "cursorColor": "#002B36",
+ "cyan": "#2AA198",
+ "foreground": "#657B83",
+ "green": "#859900",
+ "name": "Solarized Light",
+ "purple": "#D33682",
+ "red": "#DC322F",
+ "selectionBackground": "#FFFFFF",
+ "white": "#EEE8D5",
+ "yellow": "#B58900"
+ },
+ {
+ "background": "#000000",
+ "black": "#000000",
+ "blue": "#3465A4",
+ "brightBlack": "#555753",
+ "brightBlue": "#729FCF",
+ "brightCyan": "#34E2E2",
+ "brightGreen": "#8AE234",
+ "brightPurple": "#AD7FA8",
+ "brightRed": "#EF2929",
+ "brightWhite": "#EEEEEC",
+ "brightYellow": "#FCE94F",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#06989A",
+ "foreground": "#D3D7CF",
+ "green": "#4E9A06",
+ "name": "Tango Dark",
+ "purple": "#75507B",
+ "red": "#CC0000",
+ "selectionBackground": "#FFFFFF",
+ "white": "#D3D7CF",
+ "yellow": "#C4A000"
+ },
+ {
+ "background": "#FFFFFF",
+ "black": "#000000",
+ "blue": "#3465A4",
+ "brightBlack": "#555753",
+ "brightBlue": "#729FCF",
+ "brightCyan": "#34E2E2",
+ "brightGreen": "#8AE234",
+ "brightPurple": "#AD7FA8",
+ "brightRed": "#EF2929",
+ "brightWhite": "#EEEEEC",
+ "brightYellow": "#FCE94F",
+ "cursorColor": "#000000",
+ "cyan": "#06989A",
+ "foreground": "#555753",
+ "green": "#4E9A06",
+ "name": "Tango Light",
+ "purple": "#75507B",
+ "red": "#CC0000",
+ "selectionBackground": "#FFFFFF",
+ "white": "#D3D7CF",
+ "yellow": "#C4A000"
+ },
+ {
+ "background": "#1A1B2C",
+ "black": "#414868",
+ "blue": "#7AA2F7",
+ "brightBlack": "#414868",
+ "brightBlue": "#7AA2F7",
+ "brightCyan": "#7DCFFF",
+ "brightGreen": "#73DACA",
+ "brightPurple": "#BB9AF7",
+ "brightRed": "#F7768E",
+ "brightWhite": "#C0CAF5",
+ "brightYellow": "#E0AF68",
+ "cursorColor": "#C0CAF5",
+ "cyan": "#7DCFFF",
+ "foreground": "#A9B1DC",
+ "green": "#73DACA",
+ "name": "Tokyo Night",
+ "purple": "#BB9AF7",
+ "red": "#F7768E",
+ "selectionBackground": "#28344A",
+ "white": "#C0CAF5",
+ "yellow": "#E0AF68"
+ },
+ {
+ "background": "#24283B",
+ "black": "#414868",
+ "blue": "#7AA2F7",
+ "brightBlack": "#414868",
+ "brightBlue": "#7AA2F7",
+ "brightCyan": "#7DCFFF",
+ "brightGreen": "#73DACA",
+ "brightPurple": "#BB9AF7",
+ "brightRed": "#F7768E",
+ "brightWhite": "#C0CAF5",
+ "brightYellow": "#E0AF68",
+ "cursorColor": "#C0CAF5",
+ "cyan": "#7DCFFF",
+ "foreground": "#A9B1DC",
+ "green": "#73DACA",
+ "name": "Tokyo Night Storm",
+ "purple": "#BB9AF7",
+ "red": "#F7768E",
+ "selectionBackground": "#28344A",
+ "white": "#C0CAF5",
+ "yellow": "#E0AF68"
+ },
+ {
+ "background": "#000000",
+ "black": "#000000",
+ "blue": "#000080",
+ "brightBlack": "#808080",
+ "brightBlue": "#0000FF",
+ "brightCyan": "#00FFFF",
+ "brightGreen": "#00FF00",
+ "brightPurple": "#FF00FF",
+ "brightRed": "#FF0000",
+ "brightWhite": "#FFFFFF",
+ "brightYellow": "#FFFF00",
+ "cursorColor": "#FFFFFF",
+ "cyan": "#008080",
+ "foreground": "#C0C0C0",
+ "green": "#008000",
+ "name": "Vintage",
+ "purple": "#800080",
+ "red": "#800000",
+ "selectionBackground": "#FFFFFF",
+ "white": "#C0C0C0",
+ "yellow": "#808000"
+ }
+ ],
+ "showTabsInTitlebar": true,
+ "showTerminalTitleInTitlebar": true,
+ "tabWidthMode": "titleLength",
+ "trimBlockSelection": true,
+ "useAcrylicInTabRow": false
+}
diff --git a/windows/Documents/Documents/PowerShell/Microsoft.PowerShell_profile.ps1 b/windows/Documents/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
new file mode 100644
index 0000000..40b5879
--- /dev/null
+++ b/windows/Documents/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
@@ -0,0 +1,284 @@
+# Dotfiles Management System
+if (Test-Path "$HOME\.cfg" -and Test-Path "$HOME\.cfg\refs") {
+
+ # Core git wrapper with repository as work-tree
+ function _config {
+ param(
+ [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
+ [String[]]$Args
+ )
+ git --git-dir="$HOME\.cfg" --work-tree="$HOME\.cfg" @Args
+ }
+
+ # Detect OS (cross-platform, PowerShell-native)
+ $osPlatform = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform
+ if ($osPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)) {
+ $global:CFG_OS = "windows"
+ } elseif ($osPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)) {
+ $global:CFG_OS = "linux"
+ } elseif ($osPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)) {
+ $global:CFG_OS = "macos"
+ } else {
+ $global:CFG_OS = "other"
+ }
+
+ # Map system path to repository path
+ function _repo_path {
+ param([string]$FilePath)
+
+ # If it's an absolute path that's not in HOME, handle it specially
+ if (($FilePath.StartsWith("\\") -or $FilePath.Contains(":")) -and -not $FilePath.StartsWith($HOME)) {
+ return "$CFG_OS/" + ($FilePath -replace '^[A-Z]:\\', '' -replace '\\', '/')
+ }
+
+ # Check for paths that should go to the repository root
+ if ($FilePath -match '^(common|linux|macos|windows|profile)/.*|^README\.md$') {
+ return $FilePath -replace '\\', '/'
+ }
+
+ # Remove HOME prefix if present
+ if ($FilePath.StartsWith($HOME)) {
+ $FilePath = $FilePath.Substring($HOME.Length + 1)
+ }
+
+ # Default: put under OS-specific home
+ return "$CFG_OS/home/" + ($FilePath -replace '\\', '/')
+ }
+
+ # Map repository path back to system path
+ function _sys_path {
+ param([string]$RepoPath)
+
+ $osPathPattern = "$CFG_OS/"
+
+ # Handle OS-specific files that are not in the home subdirectory
+ if ($RepoPath.StartsWith($osPathPattern) -and $RepoPath -notmatch '/home/') {
+ return ($RepoPath.Substring($osPathPattern.Length) -replace '/', '\\')
+ }
+
+ switch -Wildcard ($RepoPath) {
+ "common/config/*" {
+ $file = $RepoPath.Substring("common/config/".Length)
+ switch ($CFG_OS) {
+ "linux" { return Join-Path ($env:XDG_CONFIG_HOME ?? "$HOME\.config") $file }
+ "macos" { return Join-Path "$HOME\Library\Application Support" $file }
+ "windows" { return Join-Path $env:LOCALAPPDATA $file }
+ default { return Join-Path "$HOME\.config" $file }
+ }
+ }
+ "common/assets/*" { return Join-Path "$HOME\.cfg" $RepoPath }
+ "common/*" { return Join-Path $HOME ($RepoPath.Substring("common/".Length)) }
+ "*/home/*" { return Join-Path $HOME ($RepoPath.Substring($RepoPath.IndexOf("home/") + 5)) }
+ "profile/*" { return Join-Path "$HOME\.cfg" $RepoPath }
+ "README.md" { return Join-Path "$HOME\.cfg" $RepoPath }
+ default { return Join-Path "$HOME\.cfg" $RepoPath }
+ }
+ }
+
+ # Prompts for administrator permissions if needed and runs the command
+ function _admin_prompt {
+ param(
+ [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
+ [String[]]$Command
+ )
+ if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
+ Write-Host "Warning: This action requires administrator privileges." -ForegroundColor Yellow
+ Start-Process powershell.exe -ArgumentList "-NoProfile", "-Command", "Set-Location '$PWD'; & $Command" -Verb RunAs
+ } else {
+ & $Command
+ }
+ }
+
+ # Main config command
+ function config {
+ param(
+ [string]$Command,
+ [string]$TargetDir = "",
+ [Parameter(ValueFromRemainingArguments=$true)]
+ [string[]]$Args
+ )
+
+ # Parse --target flag for add command
+ if ($Command -eq "add" -and $Args.Count -gt 0) {
+ $i = 0
+ while ($i -lt $Args.Count) {
+ if ($Args[$i] -eq "--target" -or $Args[$i] -eq "-t") {
+ if ($i + 1 -lt $Args.Count) {
+ $TargetDir = $Args[$i + 1]
+ $Args = $Args[0..($i-1)] + $Args[($i+2)..($Args.Count-1)]
+ break
+ } else {
+ Write-Host "Error: --target requires a directory argument" -ForegroundColor Red
+ return
+ }
+ }
+ $i++
+ }
+ }
+
+ switch ($Command) {
+ "add" {
+ foreach ($file in $Args) {
+ if (-not $TargetDir) {
+ $repoPath = _repo_path $file
+ } else {
+ $fileName = if ($file.Contains("\\") -or $file.Contains(":")) { Split-Path $file -Leaf } else { $file }
+ $repoPath = "$TargetDir/$fileName" -replace '\\', '/'
+ }
+
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoPath
+ $dir = Split-Path $fullRepoPath
+ if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
+ Copy-Item -Path $file -Destination $fullRepoPath -Recurse -Force
+ _config add $repoPath
+ Write-Host "Added: $file -> $repoPath" -ForegroundColor Green
+ }
+ }
+ "rm" {
+ $rmOpts = @()
+ $fileList = @()
+
+ foreach ($arg in $Args) {
+ if ($arg.StartsWith("-")) {
+ $rmOpts += $arg
+ } else {
+ $fileList += $arg
+ }
+ }
+
+ foreach ($file in $fileList) {
+ $repoPath = _repo_path $file
+ if ($rmOpts -contains "-r") {
+ _config rm --cached -r $repoPath
+ } else {
+ _config rm --cached $repoPath
+ }
+ Remove-Item -Path $file -Recurse:($rmOpts -contains "-r") -Force
+ Write-Host "Removed: $file" -ForegroundColor Yellow
+ }
+ }
+ "sync" {
+ $direction = if ($Args.Count -gt 0) { $Args[0] } else { "to-repo" }
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+
+ if ($direction -eq "to-repo") {
+ if ((Test-Path $sysFile) -and (Test-Path $fullRepoPath)) {
+ $diff = Compare-Object (Get-Content $fullRepoPath -ErrorAction SilentlyContinue) (Get-Content $sysFile -ErrorAction SilentlyContinue)
+ if ($diff) {
+ Copy-Item $sysFile $fullRepoPath -Force
+ Write-Host "Synced to repo: $sysFile" -ForegroundColor Cyan
+ }
+ }
+ } elseif ($direction -eq "from-repo") {
+ if ((Test-Path $fullRepoPath)) {
+ $diff = if (Test-Path $sysFile) { Compare-Object (Get-Content $fullRepoPath -ErrorAction SilentlyContinue) (Get-Content $sysFile -ErrorAction SilentlyContinue) } else { $true }
+ if ($diff) {
+ $destDir = Split-Path $sysFile
+ if (($sysFile.StartsWith('\\') -or $sysFile.Contains(':')) -and -not $sysFile.StartsWith($HOME)) {
+ _admin_prompt "Copy-Item '$fullRepoPath' '$sysFile' -Recurse -Force"
+ } else {
+ if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
+ Copy-Item $fullRepoPath $sysFile -Recurse -Force
+ }
+ Write-Host "Synced from repo: $sysFile" -ForegroundColor Cyan
+ }
+ }
+ }
+ }
+ }
+ "status" {
+ $autoSynced = @()
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+ if ((Test-Path $sysFile) -and (Test-Path $fullRepoPath)) {
+ $diff = Compare-Object (Get-Content $fullRepoPath -ErrorAction SilentlyContinue) (Get-Content $sysFile -ErrorAction SilentlyContinue)
+ if ($diff) {
+ Copy-Item $sysFile $fullRepoPath -Force
+ $autoSynced += $repoFile
+ }
+ }
+ }
+ if ($autoSynced.Count -gt 0) {
+ Write-Host "=== Auto-synced Files ===" -ForegroundColor Magenta
+ foreach ($repoFile in $autoSynced) {
+ Write-Host "synced: $(_sys_path $repoFile) -> $repoFile" -ForegroundColor Cyan
+ }
+ Write-Host
+ }
+ _config status
+ Write-Host
+ }
+ "deploy" {
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+
+ if ((Test-Path $fullRepoPath) -and $sysFile) {
+ $destDir = Split-Path $sysFile
+ if (($sysFile.StartsWith('\\') -or $sysFile.Contains(':')) -and -not $sysFile.StartsWith($HOME)) {
+ _admin_prompt "New-Item -ItemType Directory -Path '$destDir' -Force; Copy-Item '$fullRepoPath' '$sysFile' -Recurse -Force"
+ } else {
+ if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
+ Copy-Item $fullRepoPath $sysFile -Recurse -Force
+ }
+ Write-Host "Deployed: $repoFile -> $sysFile" -ForegroundColor Green
+ }
+ }
+ }
+ "checkout" {
+ Write-Host "Checking out dotfiles from .cfg..." -ForegroundColor Blue
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+
+ if ((Test-Path $fullRepoPath) -and $sysFile) {
+ $destDir = Split-Path $sysFile
+ if (($sysFile.StartsWith('\\') -or $sysFile.Contains(':')) -and -not $sysFile.StartsWith($HOME)) {
+ _admin_prompt "New-Item -ItemType Directory -Path '$destDir' -Force; Copy-Item '$fullRepoPath' '$sysFile' -Recurse -Force"
+ } else {
+ if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
+ Copy-Item $fullRepoPath $sysFile -Recurse -Force
+ }
+ Write-Host "Checked out: $repoFile -> $sysFile" -ForegroundColor Green
+ }
+ }
+ }
+ "backup" {
+ $timestamp = Get-Date -Format "yyyyMMddHHmmss"
+ $backupDir = Join-Path $HOME ".dotfiles_backup\$timestamp"
+ Write-Host "Backing up existing dotfiles to $backupDir..." -ForegroundColor Blue
+
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ if (Test-Path $sysFile) {
+ $destDirFull = Join-Path $backupDir (Split-Path $repoFile)
+ if (-not (Test-Path $destDirFull)) { New-Item -ItemType Directory -Path $destDirFull -Force | Out-Null }
+ Copy-Item $sysFile (Join-Path $backupDir $repoFile) -Recurse -Force
+ }
+ }
+ Write-Host "Backup complete. To restore, copy files from $backupDir to their original locations." -ForegroundColor Green
+ }
+ default {
+ _config $Command @Args
+ }
+ }
+ }
+}
+
+# Shows navigable menu of all options when hitting Tab
+Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
+
+# Autocompletion for arrow keys
+Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
+Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
+
+New-Alias vi nvim.exe
+
diff --git a/windows/Documents/Documents/PowerShell/bloatware.ps1 b/windows/Documents/Documents/PowerShell/bloatware.ps1
new file mode 100644
index 0000000..2c8dacb
--- /dev/null
+++ b/windows/Documents/Documents/PowerShell/bloatware.ps1
@@ -0,0 +1,335 @@
+# bloatware.ps1
+
+# Check if the powershell-yaml module is installed, if not, install it
+if (-not (Get-Module powershell-yaml -ListAvailable)) {
+ $policy = Get-PSRepository -Name 'PSGallery' | Select-Object -ExpandProperty InstallationPolicy
+ Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
+ Install-Module powershell-yaml
+ Set-PSRepository -Name 'PSGallery' -InstallationPolicy $policy
+}
+
+Import-Module powershell-yaml
+
+# Define the path to the YAML file
+$yamlFilePath = "$HOME\packages.yml"
+
+# Parse the YAML file
+$packages = ConvertFrom-Yaml -Path $yamlFilePath
+$bloatware = $packages.bloatware
+$defaultApps = $packages.defaultApps
+
+# Check if Registry key exists
+function Check-RegistryKeyExists {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$KeyPath
+ )
+
+ if (Test-Path $KeyPath) {
+ Write-Host "Registry key exists: $KeyPath"
+ return $true
+ } else {
+ Write-Host "Registry key does not exist: $KeyPath"
+ return $false
+ }
+}
+
+# Helper functions ------------------------
+function force-mkdir($path) {
+ if (!(Test-Path $path)) {
+ Write-Host "-- Creating full path to: " $path -ForegroundColor White -BackgroundColor DarkGreen
+ New-Item -ItemType Directory -Force -Path $path
+ }
+}
+
+function Takeown-Registry($key) {
+ # TODO does not work for all root keys yet
+ switch ($key.split('\')[0]) {
+ "HKEY_CLASSES_ROOT" {
+ $reg = [Microsoft.Win32.Registry]::ClassesRoot
+ $key = $key.substring(18)
+ }
+ "HKEY_CURRENT_USER" {
+ $reg = [Microsoft.Win32.Registry]::CurrentUser
+ $key = $key.substring(18)
+ }
+ "HKEY_LOCAL_MACHINE" {
+ $reg = [Microsoft.Win32.Registry]::LocalMachine
+ $key = $key.substring(19)
+ }
+ }
+
+ # get administrator group
+ $admins = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
+ $admins = $admins.Translate([System.Security.Principal.NTAccount])
+
+ # set owner
+ $key = $reg.OpenSubKey($key, "ReadWriteSubTree", "TakeOwnership")
+ $acl = $key.GetAccessControl()
+ $acl.SetOwner($admins)
+ $key.SetAccessControl($acl)
+
+ # set FullControl
+ $acl = $key.GetAccessControl()
+ $rule = New-Object System.Security.AccessControl.RegistryAccessRule($admins, "FullControl", "Allow")
+ $acl.SetAccessRule($rule)
+ $key.SetAccessControl($acl)
+}
+
+# Function to take ownership of registry keys
+function Takeown-Registry($keyPath) {
+ $regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [System.Security.AccessControl.RegistryRights]::TakeOwnership)
+ $acl = $regKey.GetAccessControl()
+ $acl.SetOwner([System.Security.Principal.NTAccount]"Administrators")
+ $regKey.SetAccessControl($acl)
+ $regKey.Close()
+}
+
+# Remove Features
+function Takeown-File($path) {
+ takeown.exe /A /F $path
+ $acl = Get-Acl $path
+
+ # get administrator group
+ $admins = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
+ $admins = $admins.Translate([System.Security.Principal.NTAccount])
+
+ # add NT Authority\SYSTEM
+ $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($admins, "FullControl", "None", "None", "Allow")
+ $acl.AddAccessRule($rule)
+
+ Set-Acl -Path $path -AclObject $acl
+}
+
+function Takeown-Folder($path) {
+ Takeown-File $path
+ foreach ($item in Get-ChildItem $path) {
+ if (Test-Path $item -PathType Container) {
+ Takeown-Folder $item.FullName
+ }
+ else {
+ Takeown-File $item.FullName
+ }
+ }
+}
+
+function Elevate-Privileges {
+ param($Privilege)
+ $Definition = @"
+ using System;
+ using System.Runtime.InteropServices;
+
+ public class AdjPriv {
+ [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
+ internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr rele);
+
+ [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
+ internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ internal struct TokPriv1Luid {
+ public int Count;
+ public long Luid;
+ public int Attr;
+ }
+
+ internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
+ internal const int TOKEN_QUERY = 0x00000008;
+ internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
+
+ public static bool EnablePrivilege(long processHandle, string privilege) {
+ bool retVal;
+ TokPriv1Luid tp;
+ IntPtr hproc = new IntPtr(processHandle);
+ IntPtr htok = IntPtr.Zero;
+ retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
+ tp.Count = 1;
+ tp.Luid = 0;
+ tp.Attr = SE_PRIVILEGE_ENABLED;
+ retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
+ retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
+ return retVal;
+ }
+ }
+"@
+ $ProcessHandle = (Get-Process -id $pid).Handle
+ $type = Add-Type $definition -PassThru
+ $type[0]::EnablePrivilege($processHandle, $Privilege)
+}
+
+# Remove Features ------------------------
+foreach ($bloat in $bloatware) {
+ Write-Output "Removing packages containing $bloat"
+ $pkgs = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\Packages" | Where-Object Name -Like "*$bloat*"
+
+ foreach ($pkg in $pkgs) {
+ $pkgname = $pkg.Name.Split('\')[-1]
+ Takeown-Registry $pkg.Name
+ Takeown-Registry ($pkg.Name + "\Owners")
+ Set-ItemProperty -Path ("HKLM:" + $pkg.Name.Substring(18)) -Name Visibility -Value 1
+ New-ItemProperty -Path ("HKLM:" + $pkg.Name.Substring(18)) -Name DefVis -PropertyType DWord -Value 2
+ Remove-Item -Path ("HKLM:" + $pkg.Name.Substring(18) + "\Owners")
+ dism.exe /Online /Remove-Package /PackageName:$pkgname /NoRestart
+ }
+}
+
+# Remove default apps and bloat
+Write-Output "Uninstalling default apps"
+foreach ($app in $defaultApps) {
+ Write-Output "Trying to remove $app"
+ Get-AppxPackage -Name $app -AllUsers | Remove-AppxPackage -AllUsers
+ Get-AppXProvisionedPackage -Online | Where-Object DisplayName -EQ $app | Remove-AppxProvisionedPackage -Online
+}
+
+# Disable Microsoft Edge sidebar
+$RegistryPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Edge'
+$Name = 'HubsSidebarEnabled'
+$Value = '00000000'
+# Create the key if it does not exist
+If (-NOT (Test-Path $RegistryPath)) {
+ New-Item -Path $RegistryPath -Force | Out-Null
+}
+New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force
+
+# Disable Microsoft Edge first-run Welcome screen
+$RegistryPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Edge'
+$Name = 'HideFirstRunExperience'
+$Value = '00000001'
+# Create the key if it does not exist
+If (-NOT (Test-Path $RegistryPath)) {
+ New-Item -Path $RegistryPath -Force | Out-Null
+}
+New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force
+
+# Remove Microsoft Edge ------------------------
+$ErrorActionPreference = "Stop"
+$regView = [Microsoft.Win32.RegistryView]::Registry32
+$microsoft = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $regView).
+OpenSubKey('SOFTWARE\Microsoft', $true)
+$edgeUWP = "$env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe"
+$uninstallRegKey = $microsoft.OpenSubKey('Windows\CurrentVersion\Uninstall\Microsoft Edge')
+$uninstallString = $uninstallRegKey.GetValue('UninstallString') + ' --force-uninstall'
+Write-Host "Removed Microsoft Edge"
+
+$edgeClient = $microsoft.OpenSubKey('EdgeUpdate\ClientState\{56EB18F8-B008-4CBD-B6D2-8C97FE7E9062}', $true)
+if ($null -ne $edgeClient.GetValue('experiment_control_labels')) {
+ $edgeClient.DeleteValue('experiment_control_labels')
+}
+$microsoft.CreateSubKey('EdgeUpdateDev').SetValue('AllowUninstall', '')
+[void](New-Item $edgeUWP -ItemType Directory -ErrorVariable fail -ErrorAction SilentlyContinue)
+[void](New-Item "$edgeUWP\MicrosoftEdge.exe" -ErrorAction Continue)
+Start-Process cmd.exe "/c $uninstallString" -WindowStyle Hidden -Wait
+[void](Remove-Item "$edgeUWP\MicrosoftEdge.exe" -ErrorAction Continue)
+
+if (-not $fail) {
+ [void](Remove-Item "$edgeUWP")
+}
+
+Write-Output "Edge should now be uninstalled!"
+
+# Kill OneDrive with fire ------------------------
+Write-Output "Kill OneDrive process"
+taskkill.exe /F /IM "OneDrive.exe"
+taskkill.exe /F /IM "explorer.exe"
+Write-Output "Remove OneDrive"
+if (Test-Path "$env:systemroot\System32\OneDriveSetup.exe") {
+ & "$env:systemroot\System32\OneDriveSetup.exe" /uninstall
+}
+if (Test-Path "$env:systemroot\SysWOW64\OneDriveSetup.exe") {
+ & "$env:systemroot\SysWOW64\OneDriveSetup.exe" /uninstall
+}
+
+Write-Output "Removing OneDrive leftovers"
+Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$env:localappdata\Microsoft\OneDrive"
+Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$env:programdata\Microsoft OneDrive"
+Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$env:systemdrive\OneDriveTemp"
+# check if directory is empty before removing:
+If ((Get-ChildItem "$env:userprofile\OneDrive" -Recurse | Measure-Object).Count -eq 0) {
+ Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$env:userprofile\OneDrive"
+}
+
+Write-Output "Disable OneDrive via Group Policies"
+force-mkdir "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\OneDrive"
+Set-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\OneDrive" "DisableFileSyncNGSC" 1
+
+Write-Output "Remove Onedrive from explorer sidebar"
+New-PSDrive -PSProvider "Registry" -Root "HKEY_CLASSES_ROOT" -Name "HKCR"
+force-mkdir "HKCR:\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}"
+Set-ItemProperty "HKCR:\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}" "System.IsPinnedToNameSpaceTree" 0
+force-mkdir "HKCR:\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}"
+Set-ItemProperty "HKCR:\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}" "System.IsPinnedToNameSpaceTree" 0
+Remove-PSDrive "HKCR"
+
+# Thank you Matthew Israelsson
+Write-Output "Removing run hook for new users"
+reg load "hku\Default" "C:\Users\Default\NTUSER.DAT"
+reg delete "HKEY_USERS\Default\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v "OneDriveSetup" /f
+reg unload "hku\Default"
+
+Write-Output "Removing startmenu entry"
+Remove-Item -Force -ErrorAction SilentlyContinue "$env:userprofile\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk"
+
+Write-Output "Removing scheduled task"
+$scheduledTasks = Get-ScheduledTask -TaskPath '\' -TaskName 'OneDrive*' -ErrorAction SilentlyContinue
+if ($scheduledTasks) {
+ try {
+ $scheduledTasks | Unregister-ScheduledTask -Confirm:$false
+ Write-Output "OneDrive scheduled tasks removed."
+ } catch {
+ Write-Warning "Failed to unregister scheduled tasks: $_"
+ }
+} else {
+ Write-Output "No OneDrive scheduled tasks found."
+}
+
+Write-Output "Restarting explorer"
+Start-Process "explorer.exe"
+
+Write-Output "Waiting for explorer to complete loading"
+Start-Sleep 10
+
+Write-Output "Removing additional OneDrive leftovers"
+foreach ($item in (Get-ChildItem "$env:WinDir\WinSxS\*onedrive*")) {
+ Takeown-Folder $item.FullName
+ try {
+ Remove-Item -Recurse -Force -ErrorAction Continue -ErrorVariable RemoveError $item.FullName
+ if ($RemoveError) {
+ Write-Warning "Failed to remove $($item.FullName): $($RemoveError.Exception.Message)"
+ } else {
+ Write-Output "Successfully removed: $($item.FullName)"
+ }
+ } catch {
+ Write-Warning "Failed to remove $($item.FullName): $_"
+ }
+}
+
+# As a last step, disable UAC ------------------------
+New-ItemProperty -Path HKLM:Software\Microsoft\Windows\CurrentVersion\policies\system -Name EnableLUA -PropertyType DWord -Value 0 -Force
+
+# Remove OneDrive directory if it exists
+Write-Host "Removing OneDrive directory"
+
+# Change directory to user's home directory
+Set-Location $HOME
+
+# Check if OneDrive directory exists
+$OneDrivePath = Join-Path $HOME "OneDrive"
+if (Test-Path -Path $OneDrivePath -PathType Container) {
+ # Remove OneDrive directory recursively and forcefully
+ Remove-Item -Path $OneDrivePath -Recurse -Force -ErrorAction Continue
+ if ($?) {
+ Write-Output "OneDrive directory removed successfully."
+ } else {
+ Write-Warning "Failed to remove OneDrive directory."
+ }
+} else {
+ Write-Output "OneDrive directory not found."
+}
+
+# Prevents "Suggested Applications" returning
+if (Check-RegistryKeyExists -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Cloud Content") {
+ Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Cloud Content" "DisableWindowsConsumerFeatures" 1
+}
diff --git a/windows/Documents/Documents/PowerShell/bootstrap.ps1 b/windows/Documents/Documents/PowerShell/bootstrap.ps1
new file mode 100644
index 0000000..d2f4369
--- /dev/null
+++ b/windows/Documents/Documents/PowerShell/bootstrap.ps1
@@ -0,0 +1,669 @@
+#!/usr/bin/env pwsh
+
+# Created By: srdusr
+# Created On: Windows PowerShell Bootstrap Script
+# Project: Dotfiles installation script for Windows
+
+# Dependencies: git, powershell
+
+param(
+ [string]$Profile = "essentials",
+ [switch]$Force = $false,
+ [switch]$Ask = $false,
+ [switch]$Help = $false
+)
+
+# Color definitions for pretty UI
+$Script:Colors = @{
+ Reset = "`e[0m"
+ Red = "`e[0;31m"
+ Green = "`e[0;32m"
+ Yellow = "`e[0;33m"
+ Blue = "`e[0;34m"
+ Cyan = "`e[0;36m"
+ White = "`e[0;37m"
+ Bold = "`e[1m"
+}
+
+# Prompt helper: Yes/No with default; in non-Ask mode, returns default immediately
+function Prompt-YesNo {
+ param(
+ [Parameter(Mandatory=$true)][string]$Question,
+ [ValidateSet('Y','N')][string]$Default = 'Y'
+ )
+ if (-not $Script:AskPreference) {
+ return $Default -eq 'Y'
+ }
+ $suffix = if ($Default -eq 'Y') { '[Y/n]' } else { '[y/N]' }
+ while ($true) {
+ Write-Host -NoNewline "$Question $suffix: " -ForegroundColor Yellow
+ $resp = Read-Host
+ if ([string]::IsNullOrWhiteSpace($resp)) { $resp = $Default }
+ switch ($resp.ToUpper()) {
+ 'Y' { return $true }
+ 'YES' { return $true }
+ 'N' { return $false }
+ 'NO' { return $false }
+ default { Write-Warning "Please answer Y/yes or N/no" }
+ }
+ }
+}
+
+# Configuration
+$Script:Config = @{
+ DotfilesUrl = 'https://github.com/srdusr/dotfiles.git'
+ DotfilesDir = "$HOME\.cfg"
+ LogFile = "$HOME\AppData\Local\dotfiles_install.log"
+ StateFile = "$HOME\AppData\Local\dotfiles_install_state"
+ BackupDir = "$HOME\.dotfiles-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
+ PackagesFile = "packages.yml"
+ OS = "windows"
+}
+
+# Logging functions
+function Write-Log {
+ param([string]$Message, [string]$Level = "INFO")
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
+ $logEntry = "[$timestamp] [$Level] $Message"
+ Add-Content -Path $Script:Config.LogFile -Value $logEntry -Force
+}
+
+function Write-ColorOutput {
+ param([string]$Message, [string]$Color = "White")
+ Write-Host $Message -ForegroundColor $Color
+ Write-Log $Message
+}
+
+function Write-Success { param([string]$Message) Write-ColorOutput "✓ $Message" "Green" }
+function Write-Info { param([string]$Message) Write-ColorOutput "ℹ $Message" "Cyan" }
+function Write-Warning { param([string]$Message) Write-ColorOutput "⚠ $Message" "Yellow" }
+function Write-Error { param([string]$Message) Write-ColorOutput "✗ $Message" "Red" }
+
+function Write-Header {
+ param([string]$Title)
+ Write-Host ""
+ Write-Host "=" * 60 -ForegroundColor Blue
+ Write-Host " $Title" -ForegroundColor Bold
+ Write-Host "=" * 60 -ForegroundColor Blue
+ Write-Host ""
+}
+
+# Utility functions
+function Test-CommandExists {
+ param([string]$Command)
+ return [bool](Get-Command $Command -ErrorAction SilentlyContinue)
+}
+
+function Test-IsAdmin {
+ $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+}
+
+function Invoke-AdminCommand {
+ param([string]$Command)
+ if (-not (Test-IsAdmin)) {
+ Write-Warning "Elevating privileges for: $Command"
+ Start-Process powershell.exe -ArgumentList "-NoProfile", "-Command", $Command -Verb RunAs -Wait
+ } else {
+ Invoke-Expression $Command
+ }
+}
+
+# Package management functions
+function Get-PackageManager {
+ if (Test-CommandExists "choco") { return "chocolatey" }
+ if (Test-CommandExists "winget") { return "winget" }
+ if (Test-CommandExists "scoop") { return "scoop" }
+ return $null
+}
+
+# Return $true if a package appears to be installed for the given manager
+function Test-PackageInstalled {
+ param(
+ [Parameter(Mandatory=$true)][string]$Manager,
+ [Parameter(Mandatory=$true)][string]$Name
+ )
+ switch ($Manager) {
+ "chocolatey" {
+ $out = choco list --local-only --exact $Name 2>$null
+ return ($out | Select-String -Pattern "^\s*$([regex]::Escape($Name))\s").Length -gt 0
+ }
+ "winget" {
+ # Winget list may return multiple rows; use --exact name match when possible
+ $out = winget list --name $Name 2>$null
+ return ($out | Select-String -SimpleMatch $Name).Length -gt 0
+ }
+ "scoop" {
+ # scoop list <name> returns 0 when installed
+ scoop list $Name *> $null
+ return $LASTEXITCODE -eq 0
+ }
+ default { return $false }
+ }
+}
+
+function Install-PackageManager {
+ Write-Header "Installing Package Manager"
+
+ if (-not (Test-CommandExists "choco")) {
+ Write-Info "Installing Chocolatey..."
+ Set-ExecutionPolicy Bypass -Scope Process -Force
+ [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
+ Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+
+ if (Test-CommandExists "choco") {
+ Write-Success "Chocolatey installed successfully"
+ } else {
+ Write-Error "Failed to install Chocolatey"
+ return $false
+ }
+ } else {
+ Write-Info "Chocolatey already installed"
+ }
+ return $true
+}
+
+function Install-Packages {
+ param([string]$PackagesFile, [string]$Profile)
+
+ if (-not (Test-Path $PackagesFile)) {
+ Write-Warning "Packages file not found: $PackagesFile"
+ return
+ }
+
+ Write-Header "Installing Packages"
+
+ # Install powershell-yaml if not available
+ if (-not (Get-Module powershell-yaml -ListAvailable)) {
+ Write-Info "Installing powershell-yaml module..."
+ $policy = Get-PSRepository -Name 'PSGallery' | Select-Object -ExpandProperty InstallationPolicy
+ Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
+ Install-Module powershell-yaml -Force
+ Set-PSRepository -Name 'PSGallery' -InstallationPolicy $policy
+ }
+
+ Import-Module powershell-yaml
+
+ # Helper: run custom_installs.<name>.windows if condition passes
+ function Invoke-CustomInstallsWindows {
+ param([Parameter(Mandatory=$true)]$Yaml)
+ if (-not $Yaml.custom_installs) { return }
+ foreach ($name in $Yaml.custom_installs.PSObject.Properties.Name) {
+ $entry = $Yaml.custom_installs.$name
+ if (-not $entry) { continue }
+ $winCmd = $entry.windows
+ if (-not $winCmd) { continue }
+ $shouldRun = $true
+ if ($entry.condition) {
+ $cond = [string]$entry.condition
+ if ($cond -match "!\s*command\s+-v\s+([A-Za-z0-9._-]+)") {
+ $shouldRun = -not (Test-CommandExists $Matches[1])
+ } elseif ($cond -match "command\s+-v\s+([A-Za-z0-9._-]+)") {
+ $shouldRun = (Test-CommandExists $Matches[1])
+ }
+ }
+ if (-not $shouldRun) { Write-Info "Skipping custom install: $name"; continue }
+ Write-Info "Running custom install: $name"
+ try { Invoke-Expression $winCmd; Write-Success "Custom install completed: $name" }
+ catch { Write-Warning "Custom install failed for '$name': $_" }
+ }
+ }
+
+ try {
+ $packages = Get-Content $PackagesFile | ConvertFrom-Yaml
+ $packageManager = Get-PackageManager
+
+ if (-not $packageManager) {
+ Write-Error "No package manager available"
+ return
+ }
+
+ # Get packages for current profile and OS
+ $profilePackages = @()
+ if ($packages.profiles.$Profile.windows) {
+ $profilePackages += $packages.profiles.$Profile.windows
+ }
+ if ($packages.profiles.$Profile.common) {
+ $profilePackages += $packages.profiles.$Profile.common
+ }
+
+ foreach ($package in $profilePackages) {
+ $packageName = if ($packages.packages.$package.windows) {
+ $packages.packages.$package.windows
+ } else {
+ $package
+ }
+
+ if (Test-PackageInstalled -Manager $packageManager -Name $packageName) {
+ Write-Info "Already installed: $packageName"
+ continue
+ }
+
+ Write-Info "Installing package: $packageName"
+
+ switch ($packageManager) {
+ "chocolatey" {
+ if (-not (choco list --local-only | Select-String -Pattern "^$packageName\s")) {
+ choco install $packageName -y
+ if ($LASTEXITCODE -eq 0) {
+ Write-Success "Installed: $packageName"
+ } else {
+ Write-Error "Failed to install: $packageName"
+ }
+ } else {
+ Write-Info "Already installed: $packageName"
+ }
+ }
+ "winget" {
+ winget install $packageName --accept-package-agreements --accept-source-agreements
+ }
+ "scoop" {
+ scoop install $packageName
+ }
+ }
+ }
+
+ # Also install top-level Windows packages list if present
+ if ($packages.windows) {
+ foreach ($pkg in $packages.windows) {
+ if ([string]::IsNullOrWhiteSpace($pkg)) { continue }
+ if (Test-PackageInstalled -Manager $packageManager -Name $pkg) { Write-Info "Already installed: $pkg"; continue }
+ Write-Info "Installing package: $pkg"
+ switch ($packageManager) {
+ "chocolatey" {
+ if (-not (choco list --local-only | Select-String -Pattern "^$([regex]::Escape($pkg))\s")) { choco install $pkg -y }
+ }
+ "winget" { winget install --id $pkg --silent --accept-package-agreements --accept-source-agreements }
+ "scoop" { scoop install $pkg }
+ }
+ }
+ }
+
+ # Execute Windows custom installs from packages.yml
+ Invoke-CustomInstallsWindows -Yaml $packages
+ } catch {
+ Write-Error "Error processing packages: $_"
+ }
+}
+
+# Dotfiles management functions
+function Install-Dotfiles {
+ Write-Header "Installing Dotfiles"
+
+ if (Test-Path $Script:Config.DotfilesDir) {
+ Write-Info "Updating existing dotfiles repository..."
+ & git --git-dir="$($Script:Config.DotfilesDir)" --work-tree="$($Script:Config.DotfilesDir)" pull origin main
+ } else {
+ Write-Info "Cloning dotfiles repository..."
+ git clone --bare $Script:Config.DotfilesUrl $Script:Config.DotfilesDir
+
+ if (-not (Test-Path $Script:Config.DotfilesDir)) {
+ Write-Error "Failed to clone dotfiles repository"
+ return $false
+ }
+ }
+
+ # Set up config alias for this session
+ function script:config {
+ git --git-dir="$($Script:Config.DotfilesDir)" --work-tree="$($Script:Config.DotfilesDir)" @args
+ }
+
+ # Configure repository
+ config config --local status.showUntrackedFiles no
+
+ # Checkout files to restore directory structure
+ Write-Info "Checking out dotfiles..."
+ try {
+ config checkout 2>$null
+ if ($LASTEXITCODE -ne 0) {
+ Write-Info "Forcing checkout to overwrite existing files..."
+ config checkout -f
+ }
+ Write-Success "Dotfiles checked out successfully"
+ } catch {
+ Write-Error "Failed to checkout dotfiles: $_"
+ return $false
+ }
+
+ return $true
+}
+
+function Deploy-Dotfiles {
+ Write-Header "Deploying Dotfiles"
+
+ if (-not (Test-Path $Script:Config.DotfilesDir)) {
+ Write-Error "Dotfiles directory not found. Run Install-Dotfiles first."
+ return $false
+ }
+
+ # Source the config command from profile if available
+ $profilePath = "$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
+ if (Test-Path $profilePath) {
+ Write-Info "Loading config command from profile..."
+ . $profilePath
+ }
+
+ # Deploy using config command if available, otherwise manual deployment
+ if (Get-Command config -ErrorAction SilentlyContinue) {
+ Write-Info "Deploying dotfiles using config command..."
+ config deploy
+ } else {
+ Write-Warning "Config command not available, using manual deployment..."
+ # Manual deployment fallback would go here
+ }
+
+ Write-Success "Dotfiles deployment completed"
+ return $true
+}
+
+# Locate profile-specific packages.yml similar to Linux installer
+function Get-ProfilePackagesFile {
+ param([string]$Profile)
+ $candidates = @(
+ Join-Path $HOME ".cfg/profile/$Profile/packages.yml",
+ Join-Path $HOME "profile/$Profile/packages.yml",
+ Join-Path $HOME "dot_setup/profile/$Profile/packages.yml",
+ Join-Path $Script:Config.DotfilesDir "common/$($Script:Config.PackagesFile)"
+ )
+ foreach ($pf in $candidates) {
+ if (Test-Path $pf) { return $pf }
+ }
+ return $null
+}
+
+# System configuration functions
+function Set-WindowsConfiguration {
+ param(
+ [string]$PackagesFile
+ )
+
+ Write-Header "Configuring Windows Settings"
+
+ if (-not $PackagesFile -or -not (Test-Path $PackagesFile)) {
+ Write-Warning "Packages file not found, skipping Windows configuration"
+ return
+ }
+
+ try {
+ # Load YAML content
+ $yamlContent = Get-Content $PackagesFile -Raw | ConvertFrom-Yaml
+ $registrySettings = $yamlContent.system_tweaks.windows.registry
+
+ if (-not $registrySettings) {
+ Write-Warning "No Windows registry settings found in packages.yml"
+ return
+ }
+
+ Write-Info "Applying registry settings from packages.yml..."
+
+ foreach ($setting in $registrySettings) {
+ try {
+ $path = $setting.path
+ $name = $setting.name
+ $value = $setting.value
+ $type = $setting.type
+ $description = $setting.description
+
+ Write-Info "Setting: $description"
+
+ # Ensure the registry path exists
+ $pathParts = $path -split '\\'
+ $currentPath = $pathParts[0]
+ for ($i = 1; $i -lt $pathParts.Length; $i++) {
+ $currentPath = "$currentPath\$($pathParts[$i])"
+ if (-not (Test-Path $currentPath)) {
+ New-Item -Path $currentPath -Force | Out-Null
+ }
+ }
+
+ # Set the registry value
+ Set-ItemProperty -Path $path -Name $name -Value $value -Type $type -Force
+ Write-Success "Applied: $description"
+
+ } catch {
+ Write-Warning "Failed to apply setting '$($setting.description)': $_"
+ }
+ }
+
+ Write-Success "Windows configuration applied"
+
+ # Restart explorer to apply changes
+ Write-Info "Restarting Windows Explorer..."
+ Stop-Process -Name explorer -Force
+ Start-Process explorer.exe
+
+ } catch {
+ Write-Warning "Failed to process Windows configuration: $_"
+ }
+}
+
+function Enable-WindowsFeatures {
+ param(
+ [string]$PackagesFile
+ )
+
+ Write-Header "Enabling Windows Features"
+
+ if (-not $PackagesFile -or -not (Test-Path $PackagesFile)) {
+ Write-Warning "Packages file not found, skipping Windows features"
+ return
+ }
+
+ try {
+ # Load YAML content
+ $yamlContent = Get-Content $PackagesFile -Raw | ConvertFrom-Yaml
+ $features = $yamlContent.system_tweaks.windows.features
+
+ if (-not $features) {
+ Write-Warning "No Windows features found in packages.yml"
+ return
+ }
+
+ foreach ($feature in $features) {
+ $featureName = $feature.name
+ $description = $feature.description
+ $requiresAdmin = $feature.requires_admin
+
+ if ($requiresAdmin -and -not (Test-IsAdmin)) {
+ Write-Warning "Skipping '$description' - requires administrator privileges"
+ continue
+ }
+
+ try {
+ Write-Info "Enabling: $description"
+ dism.exe /online /enable-feature /featurename:$featureName /all /norestart
+ Write-Success "Enabled: $description"
+ } catch {
+ Write-Warning "Failed to enable '$description': $_"
+ }
+ }
+
+ Write-Success "Windows features processing complete (restart may be required)"
+
+ } catch {
+ Write-Warning "Failed to process Windows features: $_"
+ }
+}
+
+function Install-PowerShellProfile {
+ Write-Header "Setting up PowerShell Profile"
+
+ $documentsPath = [System.Environment]::GetFolderPath('MyDocuments')
+ $powerShellProfileDir = "$documentsPath\PowerShell"
+ $profilePath = "$powerShellProfileDir\Microsoft.PowerShell_profile.ps1"
+
+ Write-Info "PowerShell profile directory: $powerShellProfileDir"
+
+ if (-not (Test-Path $powerShellProfileDir)) {
+ New-Item -ItemType Directory -Path $powerShellProfileDir -Force | Out-Null
+ Write-Success "Created PowerShell profile directory"
+ }
+
+ # Copy profile from dotfiles if it exists
+ $dotfilesProfile = "$($Script:Config.DotfilesDir)\windows\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
+ if (Test-Path $dotfilesProfile) {
+ Copy-Item $dotfilesProfile $profilePath -Force
+ Write-Success "PowerShell profile installed from dotfiles"
+ } else {
+ Write-Warning "PowerShell profile not found in dotfiles"
+ }
+}
+
+# Main execution function
+function Start-Bootstrap {
+ param([string]$Profile, [switch]$Force, [switch]$Ask)
+
+ Write-Header "Windows Dotfiles Bootstrap"
+ Write-Info "Profile: $Profile"
+ Write-Info "Force mode: $Force"
+ Write-Info "Interactive mode: $Ask"
+
+ # Initialize logging
+ $logDir = Split-Path $Script:Config.LogFile
+ if (-not (Test-Path $logDir)) {
+ New-Item -ItemType Directory -Path $logDir -Force | Out-Null
+ }
+
+ Write-Log "Bootstrap started with profile: $Profile"
+
+ # Set Ask preference for all prompts
+ $Script:AskPreference = [bool]$Ask
+
+ # Check dependencies
+ Write-Header "Checking Dependencies"
+ $requiredCommands = @("git", "powershell")
+ $missingCommands = @()
+
+ foreach ($cmd in $requiredCommands) {
+ if (-not (Test-CommandExists $cmd)) {
+ $missingCommands += $cmd
+ Write-Error "Required command not found: $cmd"
+ } else {
+ Write-Success "Found: $cmd"
+ }
+ }
+
+ if ($missingCommands.Count -gt 0) {
+ Write-Error "Missing required dependencies. Please install: $($missingCommands -join ', ')"
+ return $false
+ }
+
+ # Install package manager (skippable)
+ if (Prompt-YesNo -Question "Install/check package manager?" -Default 'Y') {
+ if (-not (Install-PackageManager)) {
+ Write-Error "Failed to install package manager"
+ return $false
+ }
+ } else {
+ Write-Warning "Skipped package manager step by user choice"
+ }
+
+ # Install dotfiles
+ if (Prompt-YesNo -Question "Install or update dotfiles?" -Default 'Y') {
+ if (-not (Install-Dotfiles)) {
+ Write-Error "Failed to install dotfiles"
+ return $false
+ }
+ } else {
+ Write-Warning "Skipped dotfiles installation by user choice"
+ }
+
+ # Get packages file (profile-aware)
+ $packagesFile = Get-ProfilePackagesFile -Profile $Profile
+ if (-not $packagesFile) {
+ Write-Error "Failed to get packages file for profile '$Profile'"
+ return $false
+ }
+
+ # Install packages
+ if (Prompt-YesNo -Question "Install profile packages?" -Default 'Y') {
+ Install-Packages -PackagesFile $packagesFile -Profile $Profile
+ } else {
+ Write-Warning "Skipped package installation by user choice"
+ }
+
+ # Set up PowerShell profile
+ if (Prompt-YesNo -Question "Install PowerShell profile?" -Default 'Y') {
+ Install-PowerShellProfile
+ } else {
+ Write-Warning "Skipped PowerShell profile setup by user choice"
+ }
+
+ # Deploy dotfiles
+ if (Prompt-YesNo -Question "Deploy dotfiles to system locations?" -Default 'Y') {
+ if (-not (Deploy-Dotfiles)) {
+ Write-Error "Failed to deploy dotfiles"
+ return $false
+ }
+ } else {
+ Write-Warning "Skipped dotfiles deployment by user choice"
+ }
+
+ # Configure Windows
+ if (Prompt-YesNo -Question "Apply Windows configuration from packages.yml?" -Default 'N') {
+ Set-WindowsConfiguration -PackagesFile $packagesPath
+ } else {
+ Write-Warning "Skipped Windows configuration by user choice"
+ }
+
+ # Enable Windows features (if admin)
+ if (Prompt-YesNo -Question "Enable Windows optional features?" -Default 'N') {
+ Enable-WindowsFeatures -PackagesFile $packagesPath
+ } else {
+ Write-Warning "Skipped enabling Windows features by user choice"
+ }
+
+ Write-Header "Bootstrap Complete"
+ Write-Success "Windows dotfiles bootstrap completed successfully!"
+ Write-Info "Please restart your computer to apply all changes."
+ Write-Log "Bootstrap completed successfully"
+
+ return $true
+}
+
+# Help function
+function Show-Help {
+ Write-Host @"
+Windows Dotfiles Bootstrap Script
+
+USAGE:
+ .\bootstrap.ps1 [-Profile <profile>] [-Force] [-Ask] [-Help]
+
+PARAMETERS:
+ -Profile <string> Installation profile (default: essentials)
+ Available: essentials, minimal, dev, server, full, or a custom profile.
+ Custom profile files are resolved from:
+ - %USERPROFILE%\.cfg\profile\<name>\packages.yml
+ - %USERPROFILE%\profile\<name>\packages.yml
+ - %USERPROFILE%\dot_setup\profile\<name>\packages.yml
+ -Force Force installation without prompts
+ -Ask Interactive mode with step-by-step prompts
+ -Help Show this help message
+
+EXAMPLES:
+ .\bootstrap.ps1 # Install with essentials profile
+ .\bootstrap.ps1 -Profile dev # Install development profile
+ .\bootstrap.ps1 -Profile full -Force # Force install full profile
+ .\bootstrap.ps1 -Ask # Interactive installation
+
+"@ -ForegroundColor Cyan
+}
+
+# Script entry point
+if ($Help) {
+ Show-Help
+ exit 0
+}
+
+# Run bootstrap
+try {
+ $result = Start-Bootstrap -Profile $Profile -Force:$Force -Ask:$Ask
+ if (-not $result) {
+ exit 1
+ }
+} catch {
+ Write-Error "Bootstrap failed: $_"
+ Write-Log "Bootstrap failed: $_" "ERROR"
+ exit 1
+}
diff --git a/windows/Documents/Documents/PowerShell/initialize.ps1 b/windows/Documents/Documents/PowerShell/initialize.ps1
new file mode 100644
index 0000000..a616998
--- /dev/null
+++ b/windows/Documents/Documents/PowerShell/initialize.ps1
@@ -0,0 +1,227 @@
+<#
+ .SYNOPSIS
+ Bootstrap Windows command prompts (cmd, PS, PSCore) with my dotfiles and apps.
+
+ .DESCRIPTION
+ To bootstrap directly from GitHub, run these 2 cmdlets in a PowerShell prompt:
+ > Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
+ > irm 'https://raw.githubusercontent.com/srdusr/dotfiles/main/bootstrap.ps1' | iex
+#>
+[CmdletBinding()]
+param (
+ [ValidateSet('clone', 'setup', 'apps', 'env', IgnoreCase = $true)]
+ [Parameter(Position = 0)] [string]
+ $verb = 'clone',
+ [Parameter()] [string]
+ $userName = $null,
+ [Parameter()] [string]
+ $email = $null,
+ [Parameter()] [switch]
+ $runAsAdmin = $false
+)
+
+$ErrorActionPreference = 'Stop'
+
+$originGitHub = 'https://github.com/srdusr/dotfiles.git'
+$dotPath = (Join-Path $env:USERPROFILE '.cfg')
+
+# Ensure Tls12
+[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
+
+function ensureLocalGit {
+ if (Get-Command 'git' -ErrorAction SilentlyContinue) {
+ return
+ }
+
+ $localGitFolder = (Join-Path $env:USERPROFILE (Join-Path "Downloads" "localGit"))
+ Write-Host "Installing ad-hoc git into $localGitFolder..."
+
+ $gitUrl = Invoke-RestMethod 'https://api.github.com/repos/git-for-windows/git/releases/latest' |
+ Select-Object -ExpandProperty 'assets' |
+ Where-Object { $_.name -Match 'MinGit' -and $_.name -Match '64-bit' -and $_.name -notmatch 'busybox' } |
+ Select-Object -ExpandProperty 'browser_download_url'
+ $localGitZip = (Join-Path $localGitFolder "MinGit.zip")
+ New-Item -ItemType Directory -Path $localGitFolder -Force | Out-Null
+ (New-Object Net.WebClient).DownloadFile($gitUrl, $localGitZip)
+ Expand-Archive -Path $localGitZip -DestinationPath $localGitFolder -Force
+
+ $gitPath = (Join-Path $localGitFolder 'cmd')
+ $env:Path += ";$gitPath"
+}
+
+function cloneDotfiles {
+ Write-Host "Cloning $originGitHub -> $dotPath"
+ Write-Host -NoNewline "OK to proceed with setup? [Y/n] "
+ $answer = (Read-Host).ToUpper()
+ if ($answer -ne 'Y' -and $answer -ne '') {
+ Write-Warning "Aborting."
+ return 4
+ }
+
+ ensureLocalGit
+
+ if (-not $userName -or $userName -eq '') {
+ $userName = (& git config --global --get user.name)
+ }
+ if (-not $userName -or $userName -eq '') {
+ $userName = "$env:USERNAME@$env:COMPUTERNAME"
+ }
+
+ if (-not $email -or $email -eq '') {
+ $email = (& git config --global --get user.email)
+ }
+ if (-not $email -or $email -eq '') {
+ $email = Read-Host "Enter your email address for git commits"
+ if ($email -eq '') {
+ Write-Warning "Need email address, aborting."
+ return 3
+ }
+ }
+
+ & git.exe config --global user.name $userName
+ & git.exe config --global user.email $email
+
+
+ function global:config {
+ git --git-dir="$dotPath" --work-tree="$env:USERPROFILE" $args
+ }
+
+ Add-Content -Path "$env:USERPROFILE\.gitignore" -Value ".cfg"
+
+ if (Test-Path -Path $dotfiles_dir) {
+ config pull | Out-Null
+ $update = $true
+ } else {
+ git clone --bare $originGitHub $dotPath | Out-Null
+ $update = $false
+ }
+
+ $std_err_output = config checkout 1>&1
+
+ if ($std_err_output -match "following untracked working tree files would be overwritten") {
+ if (-not $update) {
+ config checkout | Out-Null
+ }
+ }
+ config config --local status.showUntrackedFiles no
+
+ if ($update -or (Read-Host "Do you want to overwrite existing files and continue with the dotfiles setup? [Y/n]" -eq "Y")) {
+ config fetch origin main:main | Out-Null
+ config reset --hard main | Out-Null
+ config checkout -f
+ if ($?) {
+ Write-Host "Successfully imported $dotPath."
+ } else {
+ handle_error "Mission failed."
+ }
+ } else {
+ handle_error "Aborted by user. Exiting..."
+ }
+
+ return 0
+}
+
+function setup {
+ ensureLocalGit
+}
+
+function installApps {
+ ensureLocalGit
+}
+
+function writeGitConfig {
+ param (
+ [Parameter(Mandatory = $true)] [string] $configIniFile
+ )
+
+ if ((Test-Path (Join-Path $env:USERPROFILE '.gitconfig')) -and -not (Test-Path (Join-Path $env:USERPROFILE '.gitconfig.bak'))) {
+ $userName = (& git config --global --get user.name)
+ $email = (& git config --global --get user.email)
+
+ Move-Item -Path (Join-Path $env:USERPROFILE '.gitconfig') -Destination (Join-Path $env:USERPROFILE '.gitconfig.bak')
+
+ if ($userName -and $userName -ne '') {
+ & git.exe config --global user.name $userName
+ }
+ if ($email -and $email -ne '') {
+ & git.exe config --global user.email $email
+ }
+ }
+
+ Get-Content $configIniFile | ForEach-Object {
+ if ($_.TrimStart().StartsWith('#')) { return }
+ $key, $value = $_.Split('=', 2)
+ Write-Verbose "git config --global $key $value"
+ & git.exe config --global $key "$value"
+ }
+}
+
+function setupShellEnvs {
+ Write-Host "Setting cmd console properties:"
+ $consolePath = 'HKCU\Console'
+ & reg add $consolePath /v QuickEdit /d 0x1 /t REG_DWORD /f | Out-Null
+ & reg add $consolePath /v WindowSize /d 0x00320078 /t REG_DWORD /f | Out-Null
+ & reg add $consolePath /v ScreenBufferSize /d 0x23280078 /t REG_DWORD /f | Out-Null
+ & reg add $consolePath /v FontFamily /d 0x36 /t REG_DWORD /f | Out-Null
+ & reg add $consolePath /v HistoryBufferSize /d 0x64 /t REG_DWORD /f | Out-Null
+ & reg add $consolePath /v FaceName /d "Hack Nerd Font Mono" /t REG_SZ /f | Out-Null
+ & reg add $consolePath /v FontSize /d 0x00100000 /t REG_DWORD /f | Out-Null
+
+ $win32rc = (Join-Path $PSScriptRoot (Join-Path 'win' 'win32-rc.cmd'))
+ Write-Host "Setting up cmd autorun: $win32rc"
+ & reg add "HKCU\Software\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d $win32rc /f | Out-Null
+
+ Write-Host "Configuring user home dir..."
+ $configDir = (Join-Path $env:USERPROFILE '.config')
+ New-Item -ItemType Directory -Path $configDir -ErrorAction SilentlyContinue | Out-Null
+
+ $sshDir = (Join-Path $env:USERPROFILE '.ssh')
+ Remove-Item (Join-Path $sshDir 'config') -ErrorAction SilentlyContinue -Force | Out-Null
+ $openSsh = ((Join-Path $env:windir 'System32\OpenSSH\ssh.exe').Replace("\", "/"))
+ & git config --global core.sshCommand $openSsh
+}
+
+function main {
+ param (
+ [Parameter(Mandatory = $true)] [string] $verbAction
+ )
+
+ Write-Verbose "PS: $($PSVersionTable.PSVersion)-$($PSVersionTable.PSEdition)"
+ switch ($verbAction) {
+ 'clone' {
+ Write-Host
+ if (Test-Path (Join-Path $dotPath '.git')) {
+ Write-Host "Local git repo already exists, skipping."
+ main setup
+ return
+ }
+
+ $rc = cloneDotfiles
+ if ($rc -ne 0) {
+ Write-Error "Cloning dotfiles failed, aborting."
+ return
+ }
+
+ $script = (Join-Path $dotPath '.config\powershell\bootstrap.ps1')
+ Write-Host "Continue $script in child process"
+ Start-Process -PassThru -NoNewWindow -FilePath "powershell.exe" -ArgumentList "-NoProfile -File $script setup" | Wait-Process
+ }
+
+ 'setup' {
+ Write-Host "Setting up..."
+ setup
+ installApps
+ setupShellEnvs
+ Write-Host "Done (setup)."
+ exit
+ }
+
+ 'apps' { installApps }
+
+ 'env' { setupShellEnvs }
+ }
+
+ Write-Host "Done."
+}
+
+main $verb
diff --git a/windows/Documents/PowerShell/Microsoft.PowerShell_profile.ps1 b/windows/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
index 349f7ab..40b5879 100644
--- a/windows/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
+++ b/windows/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
@@ -1,6 +1,276 @@
-# Dotfiles special git command
-function global:config {
- git --git-dir="$env:USERPROFILE\.cfg" --work-tree="$env:USERPROFILE" $args
+# Dotfiles Management System
+if (Test-Path "$HOME\.cfg" -and Test-Path "$HOME\.cfg\refs") {
+
+ # Core git wrapper with repository as work-tree
+ function _config {
+ param(
+ [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
+ [String[]]$Args
+ )
+ git --git-dir="$HOME\.cfg" --work-tree="$HOME\.cfg" @Args
+ }
+
+ # Detect OS (cross-platform, PowerShell-native)
+ $osPlatform = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform
+ if ($osPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)) {
+ $global:CFG_OS = "windows"
+ } elseif ($osPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)) {
+ $global:CFG_OS = "linux"
+ } elseif ($osPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)) {
+ $global:CFG_OS = "macos"
+ } else {
+ $global:CFG_OS = "other"
+ }
+
+ # Map system path to repository path
+ function _repo_path {
+ param([string]$FilePath)
+
+ # If it's an absolute path that's not in HOME, handle it specially
+ if (($FilePath.StartsWith("\\") -or $FilePath.Contains(":")) -and -not $FilePath.StartsWith($HOME)) {
+ return "$CFG_OS/" + ($FilePath -replace '^[A-Z]:\\', '' -replace '\\', '/')
+ }
+
+ # Check for paths that should go to the repository root
+ if ($FilePath -match '^(common|linux|macos|windows|profile)/.*|^README\.md$') {
+ return $FilePath -replace '\\', '/'
+ }
+
+ # Remove HOME prefix if present
+ if ($FilePath.StartsWith($HOME)) {
+ $FilePath = $FilePath.Substring($HOME.Length + 1)
+ }
+
+ # Default: put under OS-specific home
+ return "$CFG_OS/home/" + ($FilePath -replace '\\', '/')
+ }
+
+ # Map repository path back to system path
+ function _sys_path {
+ param([string]$RepoPath)
+
+ $osPathPattern = "$CFG_OS/"
+
+ # Handle OS-specific files that are not in the home subdirectory
+ if ($RepoPath.StartsWith($osPathPattern) -and $RepoPath -notmatch '/home/') {
+ return ($RepoPath.Substring($osPathPattern.Length) -replace '/', '\\')
+ }
+
+ switch -Wildcard ($RepoPath) {
+ "common/config/*" {
+ $file = $RepoPath.Substring("common/config/".Length)
+ switch ($CFG_OS) {
+ "linux" { return Join-Path ($env:XDG_CONFIG_HOME ?? "$HOME\.config") $file }
+ "macos" { return Join-Path "$HOME\Library\Application Support" $file }
+ "windows" { return Join-Path $env:LOCALAPPDATA $file }
+ default { return Join-Path "$HOME\.config" $file }
+ }
+ }
+ "common/assets/*" { return Join-Path "$HOME\.cfg" $RepoPath }
+ "common/*" { return Join-Path $HOME ($RepoPath.Substring("common/".Length)) }
+ "*/home/*" { return Join-Path $HOME ($RepoPath.Substring($RepoPath.IndexOf("home/") + 5)) }
+ "profile/*" { return Join-Path "$HOME\.cfg" $RepoPath }
+ "README.md" { return Join-Path "$HOME\.cfg" $RepoPath }
+ default { return Join-Path "$HOME\.cfg" $RepoPath }
+ }
+ }
+
+ # Prompts for administrator permissions if needed and runs the command
+ function _admin_prompt {
+ param(
+ [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
+ [String[]]$Command
+ )
+ if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
+ Write-Host "Warning: This action requires administrator privileges." -ForegroundColor Yellow
+ Start-Process powershell.exe -ArgumentList "-NoProfile", "-Command", "Set-Location '$PWD'; & $Command" -Verb RunAs
+ } else {
+ & $Command
+ }
+ }
+
+ # Main config command
+ function config {
+ param(
+ [string]$Command,
+ [string]$TargetDir = "",
+ [Parameter(ValueFromRemainingArguments=$true)]
+ [string[]]$Args
+ )
+
+ # Parse --target flag for add command
+ if ($Command -eq "add" -and $Args.Count -gt 0) {
+ $i = 0
+ while ($i -lt $Args.Count) {
+ if ($Args[$i] -eq "--target" -or $Args[$i] -eq "-t") {
+ if ($i + 1 -lt $Args.Count) {
+ $TargetDir = $Args[$i + 1]
+ $Args = $Args[0..($i-1)] + $Args[($i+2)..($Args.Count-1)]
+ break
+ } else {
+ Write-Host "Error: --target requires a directory argument" -ForegroundColor Red
+ return
+ }
+ }
+ $i++
+ }
+ }
+
+ switch ($Command) {
+ "add" {
+ foreach ($file in $Args) {
+ if (-not $TargetDir) {
+ $repoPath = _repo_path $file
+ } else {
+ $fileName = if ($file.Contains("\\") -or $file.Contains(":")) { Split-Path $file -Leaf } else { $file }
+ $repoPath = "$TargetDir/$fileName" -replace '\\', '/'
+ }
+
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoPath
+ $dir = Split-Path $fullRepoPath
+ if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
+ Copy-Item -Path $file -Destination $fullRepoPath -Recurse -Force
+ _config add $repoPath
+ Write-Host "Added: $file -> $repoPath" -ForegroundColor Green
+ }
+ }
+ "rm" {
+ $rmOpts = @()
+ $fileList = @()
+
+ foreach ($arg in $Args) {
+ if ($arg.StartsWith("-")) {
+ $rmOpts += $arg
+ } else {
+ $fileList += $arg
+ }
+ }
+
+ foreach ($file in $fileList) {
+ $repoPath = _repo_path $file
+ if ($rmOpts -contains "-r") {
+ _config rm --cached -r $repoPath
+ } else {
+ _config rm --cached $repoPath
+ }
+ Remove-Item -Path $file -Recurse:($rmOpts -contains "-r") -Force
+ Write-Host "Removed: $file" -ForegroundColor Yellow
+ }
+ }
+ "sync" {
+ $direction = if ($Args.Count -gt 0) { $Args[0] } else { "to-repo" }
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+
+ if ($direction -eq "to-repo") {
+ if ((Test-Path $sysFile) -and (Test-Path $fullRepoPath)) {
+ $diff = Compare-Object (Get-Content $fullRepoPath -ErrorAction SilentlyContinue) (Get-Content $sysFile -ErrorAction SilentlyContinue)
+ if ($diff) {
+ Copy-Item $sysFile $fullRepoPath -Force
+ Write-Host "Synced to repo: $sysFile" -ForegroundColor Cyan
+ }
+ }
+ } elseif ($direction -eq "from-repo") {
+ if ((Test-Path $fullRepoPath)) {
+ $diff = if (Test-Path $sysFile) { Compare-Object (Get-Content $fullRepoPath -ErrorAction SilentlyContinue) (Get-Content $sysFile -ErrorAction SilentlyContinue) } else { $true }
+ if ($diff) {
+ $destDir = Split-Path $sysFile
+ if (($sysFile.StartsWith('\\') -or $sysFile.Contains(':')) -and -not $sysFile.StartsWith($HOME)) {
+ _admin_prompt "Copy-Item '$fullRepoPath' '$sysFile' -Recurse -Force"
+ } else {
+ if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
+ Copy-Item $fullRepoPath $sysFile -Recurse -Force
+ }
+ Write-Host "Synced from repo: $sysFile" -ForegroundColor Cyan
+ }
+ }
+ }
+ }
+ }
+ "status" {
+ $autoSynced = @()
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+ if ((Test-Path $sysFile) -and (Test-Path $fullRepoPath)) {
+ $diff = Compare-Object (Get-Content $fullRepoPath -ErrorAction SilentlyContinue) (Get-Content $sysFile -ErrorAction SilentlyContinue)
+ if ($diff) {
+ Copy-Item $sysFile $fullRepoPath -Force
+ $autoSynced += $repoFile
+ }
+ }
+ }
+ if ($autoSynced.Count -gt 0) {
+ Write-Host "=== Auto-synced Files ===" -ForegroundColor Magenta
+ foreach ($repoFile in $autoSynced) {
+ Write-Host "synced: $(_sys_path $repoFile) -> $repoFile" -ForegroundColor Cyan
+ }
+ Write-Host
+ }
+ _config status
+ Write-Host
+ }
+ "deploy" {
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+
+ if ((Test-Path $fullRepoPath) -and $sysFile) {
+ $destDir = Split-Path $sysFile
+ if (($sysFile.StartsWith('\\') -or $sysFile.Contains(':')) -and -not $sysFile.StartsWith($HOME)) {
+ _admin_prompt "New-Item -ItemType Directory -Path '$destDir' -Force; Copy-Item '$fullRepoPath' '$sysFile' -Recurse -Force"
+ } else {
+ if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
+ Copy-Item $fullRepoPath $sysFile -Recurse -Force
+ }
+ Write-Host "Deployed: $repoFile -> $sysFile" -ForegroundColor Green
+ }
+ }
+ }
+ "checkout" {
+ Write-Host "Checking out dotfiles from .cfg..." -ForegroundColor Blue
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ $fullRepoPath = Join-Path "$HOME\.cfg" $repoFile
+
+ if ((Test-Path $fullRepoPath) -and $sysFile) {
+ $destDir = Split-Path $sysFile
+ if (($sysFile.StartsWith('\\') -or $sysFile.Contains(':')) -and -not $sysFile.StartsWith($HOME)) {
+ _admin_prompt "New-Item -ItemType Directory -Path '$destDir' -Force; Copy-Item '$fullRepoPath' '$sysFile' -Recurse -Force"
+ } else {
+ if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
+ Copy-Item $fullRepoPath $sysFile -Recurse -Force
+ }
+ Write-Host "Checked out: $repoFile -> $sysFile" -ForegroundColor Green
+ }
+ }
+ }
+ "backup" {
+ $timestamp = Get-Date -Format "yyyyMMddHHmmss"
+ $backupDir = Join-Path $HOME ".dotfiles_backup\$timestamp"
+ Write-Host "Backing up existing dotfiles to $backupDir..." -ForegroundColor Blue
+
+ _config ls-files | ForEach-Object {
+ $repoFile = $_
+ $sysFile = _sys_path $repoFile
+ if (Test-Path $sysFile) {
+ $destDirFull = Join-Path $backupDir (Split-Path $repoFile)
+ if (-not (Test-Path $destDirFull)) { New-Item -ItemType Directory -Path $destDirFull -Force | Out-Null }
+ Copy-Item $sysFile (Join-Path $backupDir $repoFile) -Recurse -Force
+ }
+ }
+ Write-Host "Backup complete. To restore, copy files from $backupDir to their original locations." -ForegroundColor Green
+ }
+ default {
+ _config $Command @Args
+ }
+ }
+ }
}
# Shows navigable menu of all options when hitting Tab
diff --git a/windows/Documents/PowerShell/bootstrap.ps1 b/windows/Documents/PowerShell/bootstrap.ps1
index 73d53b5..d2f4369 100644
--- a/windows/Documents/PowerShell/bootstrap.ps1
+++ b/windows/Documents/PowerShell/bootstrap.ps1
@@ -1,396 +1,669 @@
-# Requires -RunAsAdministrator
-
-# Set execution policy to remote signed
-Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
-
-# Set network category to private
-Set-NetConnectionProfile -NetworkCategory Private
-
-# Variables
-$dotfiles_url = 'https://github.com/srdusr/dotfiles.git'
-$dotfiles_dir = "$HOME\.cfg"
-
-# Function to handle errors
-function handle_error {
- param ($message)
- Write-Host $message -ForegroundColor Red
- exit 1
+#!/usr/bin/env pwsh
+
+# Created By: srdusr
+# Created On: Windows PowerShell Bootstrap Script
+# Project: Dotfiles installation script for Windows
+
+# Dependencies: git, powershell
+
+param(
+ [string]$Profile = "essentials",
+ [switch]$Force = $false,
+ [switch]$Ask = $false,
+ [switch]$Help = $false
+)
+
+# Color definitions for pretty UI
+$Script:Colors = @{
+ Reset = "`e[0m"
+ Red = "`e[0;31m"
+ Green = "`e[0;32m"
+ Yellow = "`e[0;33m"
+ Blue = "`e[0;34m"
+ Cyan = "`e[0;36m"
+ White = "`e[0;37m"
+ Bold = "`e[1m"
}
-# Logs
-New-Item -Path $Env:USERPROFILE\Logs -ItemType directory -Force
-Start-Transcript -Path $Env:USERPROFILE\Logs\Bootstrap.log
-$ErrorActionPreference = 'SilentlyContinue'
-Write-Host "Bootstrap.log generated in Logs\"
-
-# Function to check if the current session is elevated
-function Test-IsAdmin {
- $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
- $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
- return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+# Prompt helper: Yes/No with default; in non-Ask mode, returns default immediately
+function Prompt-YesNo {
+ param(
+ [Parameter(Mandatory=$true)][string]$Question,
+ [ValidateSet('Y','N')][string]$Default = 'Y'
+ )
+ if (-not $Script:AskPreference) {
+ return $Default -eq 'Y'
+ }
+ $suffix = if ($Default -eq 'Y') { '[Y/n]' } else { '[y/N]' }
+ while ($true) {
+ Write-Host -NoNewline "$Question $suffix: " -ForegroundColor Yellow
+ $resp = Read-Host
+ if ([string]::IsNullOrWhiteSpace($resp)) { $resp = $Default }
+ switch ($resp.ToUpper()) {
+ 'Y' { return $true }
+ 'YES' { return $true }
+ 'N' { return $false }
+ 'NO' { return $false }
+ default { Write-Warning "Please answer Y/yes or N/no" }
+ }
+ }
}
-# Ensure the script is run as administrator
-if (-not (Test-IsAdmin)) {
- handle_error "This script must be run as an administrator."
+# Configuration
+$Script:Config = @{
+ DotfilesUrl = 'https://github.com/srdusr/dotfiles.git'
+ DotfilesDir = "$HOME\.cfg"
+ LogFile = "$HOME\AppData\Local\dotfiles_install.log"
+ StateFile = "$HOME\AppData\Local\dotfiles_install_state"
+ BackupDir = "$HOME\.dotfiles-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
+ PackagesFile = "packages.yml"
+ OS = "windows"
}
-# Imports
-. $HOME\.config\powershell\initialize.ps1
-. $HOME\.config\powershell\bloatware.ps1
-
-# Configure PowerShell
-Write-Host "Configuring PowerShell"
-Write-Host "----------------------------------------"
-
-# Get the "MyDocuments" path for the current user, excluding OneDrive
-$UserMyDocumentsPath = [System.Environment]::GetFolderPath('MyDocuments').Replace("OneDrive", "")
-
-$PowerShellProfileDirectory = "$UserMyDocumentsPath\PowerShell"
-$PowerShellLegacySymlink = "$UserMyDocumentsPath\WindowsPowerShell"
-
-$PowerShellProfileTemplate = "$PSScriptRoot\$USERNAME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
-$env:PSModulePath = $env:PSModulePath -replace "\\OneDrive\\Documents\\WindowsPowerShell\\","\.powershell\"
-
-# Set documents path to user's local Documents folder
-$documentsPath = "$UserMyDocumentsPath"
-$powerShellProfileDir = "$documentsPath\PowerShell"
-
-# Output the chosen PowerShell profile directory
-$PROFILE = "$powerShellProfileDir\Microsoft.PowerShell_profile.ps1"
-Write-Host "PowerShell profile directory set to: $powerShellProfileDir"
-
-if (-not (Test-Path -Path $powerShellProfileDir)) {
- New-Item -ItemType Directory -Path $powerShellProfileDir -Force
+# Logging functions
+function Write-Log {
+ param([string]$Message, [string]$Level = "INFO")
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
+ $logEntry = "[$timestamp] [$Level] $Message"
+ Add-Content -Path $Script:Config.LogFile -Value $logEntry -Force
}
-New-Item -ItemType HardLink -Force `
- -Path "$powerShellProfileDir\Microsoft.PowerShell_profile.ps1" `
- -Target "$home\.config\powershell\Microsoft.PowerShell_profile.ps1"
-
-# Set environment variable
-[System.Environment]::SetEnvironmentVariable('PowerShellProfileDir', $powerShellProfileDir, [System.EnvironmentVariableTarget]::User)
-
-Write-Host "PowerShell profile directory set to: $powerShellProfileDir"
-Write-Host "Environment variable 'PowerShellProfileDir' set to: $powerShellProfileDir"
-
-# Verify profile sourcing
-if (!(Test-Path -Path "$home\.config\powershell\Microsoft.PowerShell_profile.ps1")) {
- handle_error "PowerShell profile does not exist. Please create it at $home\.config\powershell\Microsoft.PowerShell_profile.ps1"
+function Write-ColorOutput {
+ param([string]$Message, [string]$Color = "White")
+ Write-Host $Message -ForegroundColor $Color
+ Write-Log $Message
}
-# Install Chocolatey if not installed
-Write-Host "Installing Chocolatey"
-Write-Host "----------------------------------------"
-
-if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
- [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
- Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
-
- # Check if Chocolatey installed successfully
- if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
- handle_error "Chocolatey installation failed."
- }
-} else {
- Write-Host "Chocolatey is already installed."
+function Write-Success { param([string]$Message) Write-ColorOutput "✓ $Message" "Green" }
+function Write-Info { param([string]$Message) Write-ColorOutput "ℹ $Message" "Cyan" }
+function Write-Warning { param([string]$Message) Write-ColorOutput "⚠ $Message" "Yellow" }
+function Write-Error { param([string]$Message) Write-ColorOutput "✗ $Message" "Red" }
+
+function Write-Header {
+ param([string]$Title)
+ Write-Host ""
+ Write-Host "=" * 60 -ForegroundColor Blue
+ Write-Host " $Title" -ForegroundColor Bold
+ Write-Host "=" * 60 -ForegroundColor Blue
+ Write-Host ""
}
-# Install Applications
-Write-Host "Installing Applications"
-Write-Host "----------------------------------------"
-
-# Check if the powershell-yaml module is installed, if not, install it
-if (-not (Get-Module powershell-yaml -ListAvailable)) {
- $policy = Get-PSRepository -Name 'PSGallery' | Select-Object -ExpandProperty InstallationPolicy
- Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
- Install-Module powershell-yaml
- Set-PSRepository -Name 'PSGallery' -InstallationPolicy $policy
+# Utility functions
+function Test-CommandExists {
+ param([string]$Command)
+ return [bool](Get-Command $Command -ErrorAction SilentlyContinue)
}
-Import-Module powershell-yaml
-
-# Load packages.yml
-$packagesFile = "$HOME\packages.yml"
-$packages = Get-Content $packagesFile | ConvertFrom-Yaml
-
-# Ensure 'windows' section exists and has applications listed
-if ($packages.windows) {
- foreach ($app in $packages.windows) {
- # Check if the application is already installed
- if (-not (choco list --local-only | Select-String -Pattern "^$app\s")) {
- Write-Host "Installing $app"
- choco install $app -y
-
- if ($LASTEXITCODE -ne 0) {
- handle_error "Installation of $app failed."
- } else {
- Write-Host "$app installed successfully."
- }
- } else {
- Write-Host "$app is already installed."
- }
- }
-} else {
- Write-Host "No applications specified under the 'windows' section in $packagesFile."
+function Test-IsAdmin {
+ $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
-# Set Chrome as default browser ------------------------
-#Add-Type -AssemblyName 'System.Windows.Forms'
-#Start-Process $env:windir\system32\control.exe -ArgumentList '/name Microsoft.DefaultPrograms /page pageDefaultProgram\pageAdvancedSettings?pszAppName=google%20chrome'
-#Sleep 2
-#[System.Windows.Forms.SendKeys]::SendWait("{TAB} {TAB}{TAB} ")
-SetDefaultBrowser firefox
-
-# Refresh the environment variables
-Write-Host "Refreshing environment variables"
-
-# Update the current session environment variables
-Write-Host "Setting environment variables" -ForegroundColor Cyan
-$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
-[System.Environment]::SetEnvironmentVariable("Path", $env:Path, [System.EnvironmentVariableTarget]::Process)
-[Environment]::SetEnvironmentVariable("HOME", "$env:USERPROFILE", "User")
-[Environment]::SetEnvironmentVariable("LC_ALL", "C.UTF-8", "User")
-refreshenv
-
-# Add Git to PATH if it's installed via Chocolatey
-Write-Host "Checking for Git installation"
-$gitBinPath = "C:\Program Files\Git\bin"
-$gitCmdPath = "C:\Program Files\Git\cmd"
-$gitPaths = @($gitBinPath, $gitCmdPath)
-
-foreach ($path in $gitPaths) {
- if (Test-Path $path) {
- Write-Host "Adding $path to PATH"
- [System.Environment]::SetEnvironmentVariable("Path", "$env:Path;$path", [System.EnvironmentVariableTarget]::Machine)
- [System.Environment]::SetEnvironmentVariable("Path", "$env:Path;$path", [System.EnvironmentVariableTarget]::User)
- [System.Environment]::SetEnvironmentVariable("Path", "$env:Path;$path", [System.EnvironmentVariableTarget]::Process)
+function Invoke-AdminCommand {
+ param([string]$Command)
+ if (-not (Test-IsAdmin)) {
+ Write-Warning "Elevating privileges for: $Command"
+ Start-Process powershell.exe -ArgumentList "-NoProfile", "-Command", $Command -Verb RunAs -Wait
} else {
- Write-Host "$path does not exist."
+ Invoke-Expression $Command
}
}
-# Check if Git is installed
-Write-Host "Checking for Git installation"
-if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
- handle_error "Git is not installed or not found in PATH after installation."
-} else {
- Write-Host "Git is installed and available in PATH."
+# Package management functions
+function Get-PackageManager {
+ if (Test-CommandExists "choco") { return "chocolatey" }
+ if (Test-CommandExists "winget") { return "winget" }
+ if (Test-CommandExists "scoop") { return "scoop" }
+ return $null
}
-# Define the `config` alias in the current session
-function global:config {
- git --git-dir="$env:USERPROFILE\.cfg" --work-tree="$env:USERPROFILE" $args
-}
-
-# Add .gitignore entries
-Add-Content -Path "$HOME\.gitignore" -Value ".cfg"
-Add-Content -Path "$HOME\.gitignore" -Value "install.bat"
-Add-Content -Path "$HOME\.gitignore" -Value ".config/powershell/bootstrap.ps1"
-
-# Create symbolic links
-Write-Host "Create symbolic links"
-Write-Host "----------------------------------------"
-
-# Visual Studio Code settings.json
-New-Item -Force -ItemType SymbolicLink $HOME\AppData\Roaming\Code\User\ -Name settings.json -Value $HOME\.config\Code\User\settings.json
-
-# Visual Studio Code keybindings
-New-Item -Force -ItemType SymbolicLink $HOME\AppData\Roaming\Code\User\ -Name keybindings.json -Value $HOME\.config\Code\User\keybindings.json
-
-# Function to install dotfiles
-function install_dotfiles {
- if (Test-Path -Path $dotfiles_dir) {
- config pull | Out-Null
- $update = $true
- } else {
- git clone --bare $dotfiles_url $dotfiles_dir | Out-Null
- $update = $false
- }
-
- $std_err_output = config checkout 2>&1
-
- if ($std_err_output -match "following untracked working tree files would be overwritten") {
- if (-not $update) {
- config checkout | Out-Null
+# Return $true if a package appears to be installed for the given manager
+function Test-PackageInstalled {
+ param(
+ [Parameter(Mandatory=$true)][string]$Manager,
+ [Parameter(Mandatory=$true)][string]$Name
+ )
+ switch ($Manager) {
+ "chocolatey" {
+ $out = choco list --local-only --exact $Name 2>$null
+ return ($out | Select-String -Pattern "^\s*$([regex]::Escape($Name))\s").Length -gt 0
+ }
+ "winget" {
+ # Winget list may return multiple rows; use --exact name match when possible
+ $out = winget list --name $Name 2>$null
+ return ($out | Select-String -SimpleMatch $Name).Length -gt 0
}
+ "scoop" {
+ # scoop list <name> returns 0 when installed
+ scoop list $Name *> $null
+ return $LASTEXITCODE -eq 0
+ }
+ default { return $false }
}
- config config status.showUntrackedFiles no
-
- git config --global include.path "$HOME\.gitconfig.aliases"
+}
- if ($update -or (Read-Host "Do you want to overwrite existing files and continue with the dotfiles setup? [Y/n]" -eq "Y")) {
- config fetch origin main:main | Out-Null
- config reset --hard main | Out-Null
- config checkout -f
- if ($?) {
- Write-Host "Successfully imported $dotfiles_dir."
+function Install-PackageManager {
+ Write-Header "Installing Package Manager"
+
+ if (-not (Test-CommandExists "choco")) {
+ Write-Info "Installing Chocolatey..."
+ Set-ExecutionPolicy Bypass -Scope Process -Force
+ [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
+ Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+
+ if (Test-CommandExists "choco") {
+ Write-Success "Chocolatey installed successfully"
} else {
- handle_error "Mission failed."
+ Write-Error "Failed to install Chocolatey"
+ return $false
}
} else {
- handle_error "Aborted by user. Exiting..."
+ Write-Info "Chocolatey already installed"
}
+ return $true
}
-install_dotfiles
-
-# Install python
-Write-Host "Updating python packages" -ForegroundColor Cyan
-python -m pip install --upgrade pip
-
-# Enable WSL feature
-dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
-Write-Host "Enable WSL feature"
-wsl --install -d ubuntu
-wsl --set-default-version 2
-
-# Enable Virtual Machine feature
-#dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
-#Write-Host "Enable Virtual Machine feature"
-
-Write-Header "Installing Hyper-V"
-
-# Install Hyper-V
-Write-Host "Installing Hyper-V and restart"
-Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -NoRestart
-Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -NoRestart
-Install-WindowsFeature -Name Hyper-V -IncludeAllSubFeature -IncludeManagementTools -NoRestart
-
-# Configure Neovim
-Write-Host "Configuring Neovim"
-Write-Host "----------------------------------------"
-
-$neovimLocalPath = "$home\AppData\Local\nvim"
-$neovimConfigPath = "$home\.config\nvim"
+function Install-Packages {
+ param([string]$PackagesFile, [string]$Profile)
+
+ if (-not (Test-Path $PackagesFile)) {
+ Write-Warning "Packages file not found: $PackagesFile"
+ return
+ }
+
+ Write-Header "Installing Packages"
+
+ # Install powershell-yaml if not available
+ if (-not (Get-Module powershell-yaml -ListAvailable)) {
+ Write-Info "Installing powershell-yaml module..."
+ $policy = Get-PSRepository -Name 'PSGallery' | Select-Object -ExpandProperty InstallationPolicy
+ Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
+ Install-Module powershell-yaml -Force
+ Set-PSRepository -Name 'PSGallery' -InstallationPolicy $policy
+ }
+
+ Import-Module powershell-yaml
+
+ # Helper: run custom_installs.<name>.windows if condition passes
+ function Invoke-CustomInstallsWindows {
+ param([Parameter(Mandatory=$true)]$Yaml)
+ if (-not $Yaml.custom_installs) { return }
+ foreach ($name in $Yaml.custom_installs.PSObject.Properties.Name) {
+ $entry = $Yaml.custom_installs.$name
+ if (-not $entry) { continue }
+ $winCmd = $entry.windows
+ if (-not $winCmd) { continue }
+ $shouldRun = $true
+ if ($entry.condition) {
+ $cond = [string]$entry.condition
+ if ($cond -match "!\s*command\s+-v\s+([A-Za-z0-9._-]+)") {
+ $shouldRun = -not (Test-CommandExists $Matches[1])
+ } elseif ($cond -match "command\s+-v\s+([A-Za-z0-9._-]+)") {
+ $shouldRun = (Test-CommandExists $Matches[1])
+ }
+ }
+ if (-not $shouldRun) { Write-Info "Skipping custom install: $name"; continue }
+ Write-Info "Running custom install: $name"
+ try { Invoke-Expression $winCmd; Write-Success "Custom install completed: $name" }
+ catch { Write-Warning "Custom install failed for '$name': $_" }
+ }
+ }
+
+ try {
+ $packages = Get-Content $PackagesFile | ConvertFrom-Yaml
+ $packageManager = Get-PackageManager
+
+ if (-not $packageManager) {
+ Write-Error "No package manager available"
+ return
+ }
+
+ # Get packages for current profile and OS
+ $profilePackages = @()
+ if ($packages.profiles.$Profile.windows) {
+ $profilePackages += $packages.profiles.$Profile.windows
+ }
+ if ($packages.profiles.$Profile.common) {
+ $profilePackages += $packages.profiles.$Profile.common
+ }
+
+ foreach ($package in $profilePackages) {
+ $packageName = if ($packages.packages.$package.windows) {
+ $packages.packages.$package.windows
+ } else {
+ $package
+ }
-# Check if nvim directory already exists in AppData\Local
-if (-not (Test-Path -Path $neovimLocalPath)) {
- New-Item -ItemType Junction -Force -Path $neovimLocalPath -Target $neovimConfigPath
-} else {
- Write-Host "Neovim directory ($neovimLocalPath) already exists."
-}
+ if (Test-PackageInstalled -Manager $packageManager -Name $packageName) {
+ Write-Info "Already installed: $packageName"
+ continue
+ }
-# Install Windows Terminal, and configure
-Write-Host "Install Windows Terminal, and configure"
-Write-Host "----------------------------------------"
+ Write-Info "Installing package: $packageName"
+
+ switch ($packageManager) {
+ "chocolatey" {
+ if (-not (choco list --local-only | Select-String -Pattern "^$packageName\s")) {
+ choco install $packageName -y
+ if ($LASTEXITCODE -eq 0) {
+ Write-Success "Installed: $packageName"
+ } else {
+ Write-Error "Failed to install: $packageName"
+ }
+ } else {
+ Write-Info "Already installed: $packageName"
+ }
+ }
+ "winget" {
+ winget install $packageName --accept-package-agreements --accept-source-agreements
+ }
+ "scoop" {
+ scoop install $packageName
+ }
+ }
+ }
-$windowsTerminalSettingsPath = "$home\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
-$windowsTerminalConfigPath = "$home\.config\windows-terminal\settings.json"
+ # Also install top-level Windows packages list if present
+ if ($packages.windows) {
+ foreach ($pkg in $packages.windows) {
+ if ([string]::IsNullOrWhiteSpace($pkg)) { continue }
+ if (Test-PackageInstalled -Manager $packageManager -Name $pkg) { Write-Info "Already installed: $pkg"; continue }
+ Write-Info "Installing package: $pkg"
+ switch ($packageManager) {
+ "chocolatey" {
+ if (-not (choco list --local-only | Select-String -Pattern "^$([regex]::Escape($pkg))\s")) { choco install $pkg -y }
+ }
+ "winget" { winget install --id $pkg --silent --accept-package-agreements --accept-source-agreements }
+ "scoop" { scoop install $pkg }
+ }
+ }
+ }
-# Check if Windows Terminal settings.json already exists
-if (Test-Path -Path $windowsTerminalSettingsPath) {
- # Backup existing settings.json
- Move-Item -Force $windowsTerminalSettingsPath "$windowsTerminalSettingsPath.old"
-} else {
- Write-Host "Windows Terminal settings.json not found, no need to backup."
+ # Execute Windows custom installs from packages.yml
+ Invoke-CustomInstallsWindows -Yaml $packages
+ } catch {
+ Write-Error "Error processing packages: $_"
+ }
}
-# Create a hard link to the settings.json file in .config\windows-terminal
-New-Item -ItemType HardLink -Force -Path $windowsTerminalSettingsPath -Target $windowsTerminalConfigPath
-
-# Function to check if a registry key exists
-function Test-RegistryKeyExists {
- param ($path)
- return (Test-Path $path -PathType Container)
+# Dotfiles management functions
+function Install-Dotfiles {
+ Write-Header "Installing Dotfiles"
+
+ if (Test-Path $Script:Config.DotfilesDir) {
+ Write-Info "Updating existing dotfiles repository..."
+ & git --git-dir="$($Script:Config.DotfilesDir)" --work-tree="$($Script:Config.DotfilesDir)" pull origin main
+ } else {
+ Write-Info "Cloning dotfiles repository..."
+ git clone --bare $Script:Config.DotfilesUrl $Script:Config.DotfilesDir
+
+ if (-not (Test-Path $Script:Config.DotfilesDir)) {
+ Write-Error "Failed to clone dotfiles repository"
+ return $false
+ }
+ }
+
+ # Set up config alias for this session
+ function script:config {
+ git --git-dir="$($Script:Config.DotfilesDir)" --work-tree="$($Script:Config.DotfilesDir)" @args
+ }
+
+ # Configure repository
+ config config --local status.showUntrackedFiles no
+
+ # Checkout files to restore directory structure
+ Write-Info "Checking out dotfiles..."
+ try {
+ config checkout 2>$null
+ if ($LASTEXITCODE -ne 0) {
+ Write-Info "Forcing checkout to overwrite existing files..."
+ config checkout -f
+ }
+ Write-Success "Dotfiles checked out successfully"
+ } catch {
+ Write-Error "Failed to checkout dotfiles: $_"
+ return $false
+ }
+
+ return $true
}
-# Function to check if a registry property exists
-function Test-RegistryPropertyExists {
- param ($keyPath, $propertyName)
- if (Test-Path $keyPath) {
- $properties = Get-ItemProperty -Path $keyPath
- return $properties.PSObject.Properties.Name -contains $propertyName
+function Deploy-Dotfiles {
+ Write-Header "Deploying Dotfiles"
+
+ if (-not (Test-Path $Script:Config.DotfilesDir)) {
+ Write-Error "Dotfiles directory not found. Run Install-Dotfiles first."
+ return $false
+ }
+
+ # Source the config command from profile if available
+ $profilePath = "$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
+ if (Test-Path $profilePath) {
+ Write-Info "Loading config command from profile..."
+ . $profilePath
+ }
+
+ # Deploy using config command if available, otherwise manual deployment
+ if (Get-Command config -ErrorAction SilentlyContinue) {
+ Write-Info "Deploying dotfiles using config command..."
+ config deploy
+ } else {
+ Write-Warning "Config command not available, using manual deployment..."
+ # Manual deployment fallback would go here
}
- return $false
+
+ Write-Success "Dotfiles deployment completed"
+ return $true
}
-# Registry Tweaks
-Write-Host "Registry Tweaks"
-Write-Host "----------------------------------------"
-
-# Show hidden files
-$advancedKeyPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
-if (-not (Test-RegistryPropertyExists $advancedKeyPath "Hidden")) {
- Set-ItemProperty -Path $advancedKeyPath -Name Hidden -Value 1
+# Locate profile-specific packages.yml similar to Linux installer
+function Get-ProfilePackagesFile {
+ param([string]$Profile)
+ $candidates = @(
+ Join-Path $HOME ".cfg/profile/$Profile/packages.yml",
+ Join-Path $HOME "profile/$Profile/packages.yml",
+ Join-Path $HOME "dot_setup/profile/$Profile/packages.yml",
+ Join-Path $Script:Config.DotfilesDir "common/$($Script:Config.PackagesFile)"
+ )
+ foreach ($pf in $candidates) {
+ if (Test-Path $pf) { return $pf }
+ }
+ return $null
}
-# Show file extensions in Windows Explorer
-$hideFileExtPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
-if (-not (Test-RegistryPropertyExists $hideFileExtPath "HideFileExt")) {
- Set-ItemProperty -Path $hideFileExtPath -Name HideFileExt -Value 0
+# System configuration functions
+function Set-WindowsConfiguration {
+ param(
+ [string]$PackagesFile
+ )
+
+ Write-Header "Configuring Windows Settings"
+
+ if (-not $PackagesFile -or -not (Test-Path $PackagesFile)) {
+ Write-Warning "Packages file not found, skipping Windows configuration"
+ return
+ }
+
+ try {
+ # Load YAML content
+ $yamlContent = Get-Content $PackagesFile -Raw | ConvertFrom-Yaml
+ $registrySettings = $yamlContent.system_tweaks.windows.registry
+
+ if (-not $registrySettings) {
+ Write-Warning "No Windows registry settings found in packages.yml"
+ return
+ }
+
+ Write-Info "Applying registry settings from packages.yml..."
+
+ foreach ($setting in $registrySettings) {
+ try {
+ $path = $setting.path
+ $name = $setting.name
+ $value = $setting.value
+ $type = $setting.type
+ $description = $setting.description
+
+ Write-Info "Setting: $description"
+
+ # Ensure the registry path exists
+ $pathParts = $path -split '\\'
+ $currentPath = $pathParts[0]
+ for ($i = 1; $i -lt $pathParts.Length; $i++) {
+ $currentPath = "$currentPath\$($pathParts[$i])"
+ if (-not (Test-Path $currentPath)) {
+ New-Item -Path $currentPath -Force | Out-Null
+ }
+ }
+
+ # Set the registry value
+ Set-ItemProperty -Path $path -Name $name -Value $value -Type $type -Force
+ Write-Success "Applied: $description"
+
+ } catch {
+ Write-Warning "Failed to apply setting '$($setting.description)': $_"
+ }
+ }
+
+ Write-Success "Windows configuration applied"
+
+ # Restart explorer to apply changes
+ Write-Info "Restarting Windows Explorer..."
+ Stop-Process -Name explorer -Force
+ Start-Process explorer.exe
+
+ } catch {
+ Write-Warning "Failed to process Windows configuration: $_"
+ }
}
-# Never Combine taskbar buttons when the taskbar is full
-$taskbarGlomLevelPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
-if (-not (Test-RegistryPropertyExists $taskbarGlomLevelPath "TaskbarGlomLevel")) {
- Set-ItemProperty -Path $taskbarGlomLevelPath -Name TaskbarGlomLevel -Value 2
+function Enable-WindowsFeatures {
+ param(
+ [string]$PackagesFile
+ )
+
+ Write-Header "Enabling Windows Features"
+
+ if (-not $PackagesFile -or -not (Test-Path $PackagesFile)) {
+ Write-Warning "Packages file not found, skipping Windows features"
+ return
+ }
+
+ try {
+ # Load YAML content
+ $yamlContent = Get-Content $PackagesFile -Raw | ConvertFrom-Yaml
+ $features = $yamlContent.system_tweaks.windows.features
+
+ if (-not $features) {
+ Write-Warning "No Windows features found in packages.yml"
+ return
+ }
+
+ foreach ($feature in $features) {
+ $featureName = $feature.name
+ $description = $feature.description
+ $requiresAdmin = $feature.requires_admin
+
+ if ($requiresAdmin -and -not (Test-IsAdmin)) {
+ Write-Warning "Skipping '$description' - requires administrator privileges"
+ continue
+ }
+
+ try {
+ Write-Info "Enabling: $description"
+ dism.exe /online /enable-feature /featurename:$featureName /all /norestart
+ Write-Success "Enabled: $description"
+ } catch {
+ Write-Warning "Failed to enable '$description': $_"
+ }
+ }
+
+ Write-Success "Windows features processing complete (restart may be required)"
+
+ } catch {
+ Write-Warning "Failed to process Windows features: $_"
+ }
}
-# Taskbar small icons
-$taskbarSmallIconsPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
-if (-not (Test-RegistryPropertyExists $taskbarSmallIconsPath "TaskbarSmallIcons")) {
- Set-ItemProperty -Path $taskbarSmallIconsPath -Name TaskbarSmallIcons -Value 1
+function Install-PowerShellProfile {
+ Write-Header "Setting up PowerShell Profile"
+
+ $documentsPath = [System.Environment]::GetFolderPath('MyDocuments')
+ $powerShellProfileDir = "$documentsPath\PowerShell"
+ $profilePath = "$powerShellProfileDir\Microsoft.PowerShell_profile.ps1"
+
+ Write-Info "PowerShell profile directory: $powerShellProfileDir"
+
+ if (-not (Test-Path $powerShellProfileDir)) {
+ New-Item -ItemType Directory -Path $powerShellProfileDir -Force | Out-Null
+ Write-Success "Created PowerShell profile directory"
+ }
+
+ # Copy profile from dotfiles if it exists
+ $dotfilesProfile = "$($Script:Config.DotfilesDir)\windows\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
+ if (Test-Path $dotfilesProfile) {
+ Copy-Item $dotfilesProfile $profilePath -Force
+ Write-Success "PowerShell profile installed from dotfiles"
+ } else {
+ Write-Warning "PowerShell profile not found in dotfiles"
+ }
}
-# Set Windows to use UTC time instead of local time for system clock
-$timeZoneInfoPath = "HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
-if (-not (Test-RegistryPropertyExists $timeZoneInfoPath "RealTimeIsUniversal")) {
- Set-ItemProperty -Path $timeZoneInfoPath -Name RealTimeIsUniversal -Value 1
+# Main execution function
+function Start-Bootstrap {
+ param([string]$Profile, [switch]$Force, [switch]$Ask)
+
+ Write-Header "Windows Dotfiles Bootstrap"
+ Write-Info "Profile: $Profile"
+ Write-Info "Force mode: $Force"
+ Write-Info "Interactive mode: $Ask"
+
+ # Initialize logging
+ $logDir = Split-Path $Script:Config.LogFile
+ if (-not (Test-Path $logDir)) {
+ New-Item -ItemType Directory -Path $logDir -Force | Out-Null
+ }
+
+ Write-Log "Bootstrap started with profile: $Profile"
+
+ # Set Ask preference for all prompts
+ $Script:AskPreference = [bool]$Ask
+
+ # Check dependencies
+ Write-Header "Checking Dependencies"
+ $requiredCommands = @("git", "powershell")
+ $missingCommands = @()
+
+ foreach ($cmd in $requiredCommands) {
+ if (-not (Test-CommandExists $cmd)) {
+ $missingCommands += $cmd
+ Write-Error "Required command not found: $cmd"
+ } else {
+ Write-Success "Found: $cmd"
+ }
+ }
+
+ if ($missingCommands.Count -gt 0) {
+ Write-Error "Missing required dependencies. Please install: $($missingCommands -join ', ')"
+ return $false
+ }
+
+ # Install package manager (skippable)
+ if (Prompt-YesNo -Question "Install/check package manager?" -Default 'Y') {
+ if (-not (Install-PackageManager)) {
+ Write-Error "Failed to install package manager"
+ return $false
+ }
+ } else {
+ Write-Warning "Skipped package manager step by user choice"
+ }
+
+ # Install dotfiles
+ if (Prompt-YesNo -Question "Install or update dotfiles?" -Default 'Y') {
+ if (-not (Install-Dotfiles)) {
+ Write-Error "Failed to install dotfiles"
+ return $false
+ }
+ } else {
+ Write-Warning "Skipped dotfiles installation by user choice"
+ }
+
+ # Get packages file (profile-aware)
+ $packagesFile = Get-ProfilePackagesFile -Profile $Profile
+ if (-not $packagesFile) {
+ Write-Error "Failed to get packages file for profile '$Profile'"
+ return $false
+ }
+
+ # Install packages
+ if (Prompt-YesNo -Question "Install profile packages?" -Default 'Y') {
+ Install-Packages -PackagesFile $packagesFile -Profile $Profile
+ } else {
+ Write-Warning "Skipped package installation by user choice"
+ }
+
+ # Set up PowerShell profile
+ if (Prompt-YesNo -Question "Install PowerShell profile?" -Default 'Y') {
+ Install-PowerShellProfile
+ } else {
+ Write-Warning "Skipped PowerShell profile setup by user choice"
+ }
+
+ # Deploy dotfiles
+ if (Prompt-YesNo -Question "Deploy dotfiles to system locations?" -Default 'Y') {
+ if (-not (Deploy-Dotfiles)) {
+ Write-Error "Failed to deploy dotfiles"
+ return $false
+ }
+ } else {
+ Write-Warning "Skipped dotfiles deployment by user choice"
+ }
+
+ # Configure Windows
+ if (Prompt-YesNo -Question "Apply Windows configuration from packages.yml?" -Default 'N') {
+ Set-WindowsConfiguration -PackagesFile $packagesPath
+ } else {
+ Write-Warning "Skipped Windows configuration by user choice"
+ }
+
+ # Enable Windows features (if admin)
+ if (Prompt-YesNo -Question "Enable Windows optional features?" -Default 'N') {
+ Enable-WindowsFeatures -PackagesFile $packagesPath
+ } else {
+ Write-Warning "Skipped enabling Windows features by user choice"
+ }
+
+ Write-Header "Bootstrap Complete"
+ Write-Success "Windows dotfiles bootstrap completed successfully!"
+ Write-Info "Please restart your computer to apply all changes."
+ Write-Log "Bootstrap completed successfully"
+
+ return $true
}
-# Disable the search in taskbar
-$searchBoxTaskbarPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search"
-if (-not (Test-RegistryPropertyExists $searchBoxTaskbarPath "SearchBoxTaskbarMode")) {
- New-ItemProperty -Path $searchBoxTaskbarPath -Name SearchBoxTaskbarMode -Value 0 -Type DWord -Force
+# Help function
+function Show-Help {
+ Write-Host @"
+Windows Dotfiles Bootstrap Script
+
+USAGE:
+ .\bootstrap.ps1 [-Profile <profile>] [-Force] [-Ask] [-Help]
+
+PARAMETERS:
+ -Profile <string> Installation profile (default: essentials)
+ Available: essentials, minimal, dev, server, full, or a custom profile.
+ Custom profile files are resolved from:
+ - %USERPROFILE%\.cfg\profile\<name>\packages.yml
+ - %USERPROFILE%\profile\<name>\packages.yml
+ - %USERPROFILE%\dot_setup\profile\<name>\packages.yml
+ -Force Force installation without prompts
+ -Ask Interactive mode with step-by-step prompts
+ -Help Show this help message
+
+EXAMPLES:
+ .\bootstrap.ps1 # Install with essentials profile
+ .\bootstrap.ps1 -Profile dev # Install development profile
+ .\bootstrap.ps1 -Profile full -Force # Force install full profile
+ .\bootstrap.ps1 -Ask # Interactive installation
+
+"@ -ForegroundColor Cyan
}
-# Dark mode:
-$personalizePath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"
-if (-not (Test-RegistryPropertyExists $personalizePath "AppsUseLightTheme")) {
- Set-ItemProperty -Path $personalizePath -Name AppsUseLightTheme -Value 0 -Type Dword -Force
-}
-if (-not (Test-RegistryPropertyExists $personalizePath "SystemUsesLightTheme")) {
- Set-ItemProperty -Path $personalizePath -Name SystemUsesLightTheme -Value 0 -Type Dword -Force
+# Script entry point
+if ($Help) {
+ Show-Help
+ exit 0
}
-# Restart explorer so the rest of the settings take effect:
-Stop-Process -f -ProcessName explorer
-Start-Process explorer.exe
-
-# Function to disable the Windows key
-function Disable-WindowsKey {
- $regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Keyboard Layout"
- $regName = "Scancode Map"
-
- # Binary data to remap the Windows key to F24 (an unused key)
- $binaryValue = [byte[]](
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00,
- 0x3A, 0x00, 0x5B, 0xE0,
- 0x00, 0x00, 0x00, 0x00
- )
-
- # Create the registry key if it doesn't exist
- if (-not (Test-RegistryKeyExists $regPath)) {
- New-Item -Path $regPath -Force | Out-Null
- }
-
- # Set the Scancode Map value if it doesn't exist
- if (-not (Test-RegistryPropertyExists $regPath $regName)) {
- Set-ItemProperty -Path $regPath -Name $regName -Value $binaryValue
+# Run bootstrap
+try {
+ $result = Start-Bootstrap -Profile $Profile -Force:$Force -Ask:$Ask
+ if (-not $result) {
+ exit 1
}
-
- Write-Output "Windows key has been disabled from opening the start menu. Please restart your computer for the changes to take effect."
+} catch {
+ Write-Error "Bootstrap failed: $_"
+ Write-Log "Bootstrap failed: $_" "ERROR"
+ exit 1
}
-
-#Disable-WindowsKey
-
-Write-Host "Bootstrap script completed."
-Write-Host "Please Restart."
-
-# Clean up Bootstrap.log
-Write-Host "Clean up Bootstrap.log"
-Stop-Transcript
-$logSuppress = Get-Content $Env:USERPROFILE\Logs\Bootstrap.log | Where-Object { $_ -notmatch "Host Application: powershell.exe" }
-$logSuppress | Set-Content $Env:USERPROFILE\Logs\Bootstrap.log -Force
diff --git a/windows/install.bat b/windows/install.bat
index d5298f1..89b3ac6 100644
--- a/windows/install.bat
+++ b/windows/install.bat
@@ -1,6 +1,11 @@
-REM Installing Dotfiles
+@echo off
+REM === Installing Dotfiles ===
-powershell . $HOME\.config\powershell\Microsoft.PowerShell_profile.ps1
-powershell Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
-powershell . $HOME\.config\powershell\bootstrap.ps1
+REM Set execution policy for current user
+powershell -NoProfile -Command "Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force"
+REM Run PowerShell profile (dotfiles)
+powershell -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
+
+REM Run bootstrap script
+powershell -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\Documents\PowerShell\bootstrap.ps1"