configs-all 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -0
- package/CLAUDE.md +94 -0
- package/README.md +424 -0
- package/TERMINAL_SHORTCUTS.md +96 -0
- package/WINDOWS_COMPATIBILITY.md +85 -0
- package/WINDOWS_MCP_SETUP.md +133 -0
- package/apps/RectangleConfig.plist +0 -0
- package/apps/Synergy +84 -0
- package/apps/iStat Menus Settings.ismp7 +0 -0
- package/claude/CLAUDE.md +228 -0
- package/claude/commands/changelog.md +36 -0
- package/claude/commands/commit.md +29 -0
- package/claude/commands/context.md +112 -0
- package/claude/commands/dash.md +37 -0
- package/claude/commands/deploy-check.md +37 -0
- package/claude/commands/deps.md +26 -0
- package/claude/commands/duplo.md +56 -0
- package/claude/commands/explain.md +43 -0
- package/claude/commands/fix-and-test.md +46 -0
- package/claude/commands/game-debug.md +66 -0
- package/claude/commands/games.md +53 -0
- package/claude/commands/go.md +147 -0
- package/claude/commands/guard.md +102 -0
- package/claude/commands/handoff.md +66 -0
- package/claude/commands/incident.md +144 -0
- package/claude/commands/init.md +78 -0
- package/claude/commands/k8s-debug.md +31 -0
- package/claude/commands/lint.md +27 -0
- package/claude/commands/merge-all.md +115 -0
- package/claude/commands/merge.md +129 -0
- package/claude/commands/mikpc.md +54 -0
- package/claude/commands/morning.md +72 -0
- package/claude/commands/partymode.md +105 -0
- package/claude/commands/plans.md +88 -0
- package/claude/commands/pr.md +41 -0
- package/claude/commands/prep.md +132 -0
- package/claude/commands/push-sync.md +82 -0
- package/claude/commands/push.md +34 -0
- package/claude/commands/research.md +73 -0
- package/claude/commands/retro.md +95 -0
- package/claude/commands/review-pr.md +96 -0
- package/claude/commands/review.md +41 -0
- package/claude/commands/scaffold-agent.md +45 -0
- package/claude/commands/setup.md +92 -0
- package/claude/commands/ship-prod.md +97 -0
- package/claude/commands/ship.md +82 -0
- package/claude/commands/simplify.md +42 -0
- package/claude/commands/spike.md +110 -0
- package/claude/commands/status.md +37 -0
- package/claude/commands/sync.md +72 -0
- package/claude/commands/test.md +29 -0
- package/claude/commands/triage.md +72 -0
- package/claude/desktop/claude_desktop_config.json +9 -0
- package/claude/hooks.json +15 -0
- package/claude/mcp-servers.duplo.json +8 -0
- package/claude/mcp-servers.json +62 -0
- package/claude/scripts/psdebug.ps1 +7 -0
- package/claude/settings.json +38 -0
- package/claude/settings.local.json +4 -0
- package/claude/statusline-command.sh +94 -0
- package/claude/templates/CLAUDE-k8s-devops.md +58 -0
- package/claude/templates/CLAUDE-python-agent.md +47 -0
- package/claude/templates/CLAUDE-typescript-frontend.md +50 -0
- package/docker/ai-stack/docker-compose.yml +76 -0
- package/docker/ai-stack/searxng/limiter.toml +3 -0
- package/docker/ai-stack/searxng/settings.yml +39 -0
- package/docker/cli/config.json.template +15 -0
- package/docker/cli/daemon.json +9 -0
- package/docker/cli/features.json +3 -0
- package/docker/mcp/catalog.json +9 -0
- package/docker/mcp/catalogs/docker-mcp.yaml +15107 -0
- package/docker/mcp/config.yaml +0 -0
- package/docker/mcp/registry.yaml +37 -0
- package/docker/mcp/tools.yaml +0 -0
- package/docs/context/.gitkeep +0 -0
- package/docs/context/2026-03-02-configs.md +142 -0
- package/docs/handoff/.gitkeep +0 -0
- package/docs/incidents/.gitkeep +0 -0
- package/docs/plans/2026-02-28-autonomous-command-suite-design.md +250 -0
- package/docs/plans/2026-02-28-autonomous-command-suite.md +682 -0
- package/docs/plans/2026-03-01-ai-stack-split-architecture.md +72 -0
- package/docs/plans/2026-03-02-ai-stack-expansion.md +33 -0
- package/docs/plans/2026-03-02-merge-commands-design.md +58 -0
- package/docs/plans/2026-03-02-merge-commands.md +354 -0
- package/docs/research/.gitkeep +0 -0
- package/docs/research/2026-03-02-configs-repo-architecture.md +152 -0
- package/docs/retros/.gitkeep +0 -0
- package/docs/retros/2026-03-01-ai-stack-split-architecture.md +38 -0
- package/docs/spikes/.gitkeep +0 -0
- package/gh/config.yml +16 -0
- package/gh/hosts.yml +5 -0
- package/gh/main.json +103 -0
- package/ghostty/config +90 -0
- package/git/config/base.gitconfig +46 -0
- package/git/config/chiefmikey.gitconfig +11 -0
- package/git/config/personal.gitconfig +10 -0
- package/git/config/work.gitconfig +14 -0
- package/ide/cursor/extensions.txt +111 -0
- package/ide/cursor/keybindings.json +307 -0
- package/ide/cursor/mcp.json +92 -0
- package/ide/cursor/settings.json +544 -0
- package/ide/vscode/extensions.txt +120 -0
- package/ide/vscode/insiders/extensions.txt +119 -0
- package/ide/vscode/insiders/keybindings.json +294 -0
- package/ide/vscode/insiders/settings.json +518 -0
- package/ide/vscode/keybindings.json +294 -0
- package/ide/vscode/settings.json +526 -0
- package/ide/vscode/vscode/extensions.txt +43 -0
- package/iterm/Mikey Pro.json +951 -0
- package/iterm/com.googlecode.iterm2.plist +5549 -0
- package/iterm/font/MesloLGS NF Bold Italic.ttf +0 -0
- package/iterm/font/MesloLGS NF Bold.ttf +0 -0
- package/iterm/font/MesloLGS NF Italic.ttf +0 -0
- package/iterm/font/MesloLGS NF Regular.ttf +0 -0
- package/package.json +15 -0
- package/scripts/ai/deploy-ai-stack.sh +119 -0
- package/scripts/ai/fix-ai-proxy.service +12 -0
- package/scripts/ai/fix-ai-proxy.sh +25 -0
- package/scripts/brew/search/brew-search-results.sh +19 -0
- package/scripts/brew/search/brew-search.sh +34 -0
- package/scripts/brew/upgrade/brew-upgrade-autoupdate.sh +5 -0
- package/scripts/brew/upgrade/brew-upgrade-full-auto.sh +89 -0
- package/scripts/brew/upgrade/brew-upgrade-full.sh +159 -0
- package/scripts/docker/cleanup/docker-cleanup-manage.sh +163 -0
- package/scripts/docker/cleanup/docker-cleanup.cron +12 -0
- package/scripts/docker/cleanup/docker-cleanup.sh +280 -0
- package/scripts/docker/install/README.md +23 -0
- package/scripts/docker/install/docker-al2.sh +7 -0
- package/scripts/docker/install/docker-compose-al2.sh +15 -0
- package/scripts/gh/auth/auth.sh +12 -0
- package/scripts/gh/config/gh-config.sh +3 -0
- package/scripts/gh/gist/gh-gist-create.sh +29 -0
- package/scripts/gh/gist/gh-gist-delete.sh +1 -0
- package/scripts/gh/gist/gh-gist-edit.sh +8 -0
- package/scripts/gh/gpg-key/gh-gpg-key-add.sh +3 -0
- package/scripts/gh/install/install.sh +7 -0
- package/scripts/gh/label/gh-label-clone.sh +0 -0
- package/scripts/gh/label/gh-label-create.sh +0 -0
- package/scripts/gh/label/gh-label-delete.sh +0 -0
- package/scripts/gh/label/gh-label-edit.sh +0 -0
- package/scripts/gh/label/gh-label-list.sh +0 -0
- package/scripts/gh/secret/gh-secret-delete.sh +24 -0
- package/scripts/gh/secret/gh-secret-set.sh +70 -0
- package/scripts/gh/ssh-key/gh-ssh-key-add.sh +8 -0
- package/scripts/git/add/git-add.sh +3 -0
- package/scripts/git/auth/README.md +11 -0
- package/scripts/git/auth/https.sh +20 -0
- package/scripts/git/auth/ssh-mac.sh +41 -0
- package/scripts/git/branch-delete/git-branch-delete.sh +16 -0
- package/scripts/git/checkout/git-checkout-stash.sh +32 -0
- package/scripts/git/temp/git-temp-pull.sh +6 -0
- package/scripts/git/temp/git-temp-push.sh +5 -0
- package/scripts/install/fresh.zsh +34 -0
- package/scripts/install/full-install.zsh +193 -0
- package/scripts/linux/codedeploy/README.md +19 -0
- package/scripts/linux/codedeploy/linux-codedeploy-al2.sh +13 -0
- package/scripts/linux/codedeploy/linux-codedeploy-index.sh +16 -0
- package/scripts/linux/codedeploy/linux-codedeploy-ubuntu.sh +14 -0
- package/scripts/linux/coredns/README.md +17 -0
- package/scripts/linux/coredns/linux-coredns-al2.sh +29 -0
- package/scripts/linux/wifi/01-netconf.yaml +21 -0
- package/scripts/linux/wifi/wifi-ubuntu.sh +17 -0
- package/scripts/mac/dock-sort/mac-dock-sort.sh +87 -0
- package/scripts/mac/dropbox-ignore/mac-dropbox-ignore.sh +12 -0
- package/scripts/mac/sudo-askpass/mac-sudo-askpass.sh +50 -0
- package/scripts/mac/sudo-askpass/setup-sudo-password.sh +49 -0
- package/scripts/mac/upgrade/mac-upgrade.sh +21 -0
- package/scripts/mac/vpn/mac-vpn.sh +4 -0
- package/scripts/mcp/aws-mcp-wrapper.ps1 +97 -0
- package/scripts/mcp/aws-mcp-wrapper.sh +53 -0
- package/scripts/mcp/duplo-mcp-wrapper.sh +31 -0
- package/scripts/mcp/filesystem-mcp-wrapper.ps1 +43 -0
- package/scripts/mcp/filesystem-mcp-wrapper.sh +34 -0
- package/scripts/mcp/git-mcp-wrapper.ps1 +42 -0
- package/scripts/mcp/git-mcp-wrapper.sh +33 -0
- package/scripts/mcp/github-mcp-wrapper.ps1 +43 -0
- package/scripts/mcp/github-mcp-wrapper.sh +19 -0
- package/scripts/mcp/kubernetes-mcp-wrapper.ps1 +22 -0
- package/scripts/mcp/kubernetes-mcp-wrapper.sh +16 -0
- package/scripts/mcp/mcp-launcher.ps1 +56 -0
- package/scripts/mcp/mcp-launcher.sh +71 -0
- package/scripts/mcp/mongodb-mcp-wrapper.ps1 +26 -0
- package/scripts/mcp/mongodb-mcp-wrapper.sh +17 -0
- package/scripts/mcp/notion-mcp-wrapper.ps1 +23 -0
- package/scripts/mcp/notion-mcp-wrapper.sh +14 -0
- package/scripts/mcp/postgres-mcp-wrapper.ps1 +23 -0
- package/scripts/mcp/postgres-mcp-wrapper.sh +16 -0
- package/scripts/npm/ncu/npm-ncu.sh +24 -0
- package/scripts/npm/upgrade/npm-upgrade.sh +51 -0
- package/scripts/qmk/build_reviung41.sh +28 -0
- package/scripts/qmk/sync_unicorne.sh +44 -0
- package/scripts/sync/README.md +64 -0
- package/scripts/sync/config-common.zsh +882 -0
- package/scripts/sync/pull-configs.ps1 +33 -0
- package/scripts/sync/pull-configs.zsh +278 -0
- package/scripts/sync/push-configs.ps1 +91 -0
- package/scripts/sync/push-configs.zsh +384 -0
- package/shell/alias/alias-d.zsh +333 -0
- package/shell/alias/alias.zsh +36 -0
- package/shell/alias/categories/development.zsh +157 -0
- package/shell/alias/categories/environment.zsh +13 -0
- package/shell/alias/categories/git.zsh +40 -0
- package/shell/alias/categories/github-functions.zsh +459 -0
- package/shell/alias/categories/network.zsh +46 -0
- package/shell/alias/categories/path.zsh +46 -0
- package/shell/alias/categories/system.zsh +78 -0
- package/shell/alias/categories/wolfe-server.zsh +11 -0
- package/shell/powershell/Microsoft.PowerShell_profile.ps1 +208 -0
- package/shell/zsh/.p10k.zsh +1832 -0
- package/shell/zsh/.zshrc +87 -0
- package/shell/zsh/config/completion.zsh +31 -0
- package/shell/zsh/config/functions.zsh +31 -0
- package/shell/zsh/config/keybindings.zsh +13 -0
- package/shell/zsh/config/options.zsh +56 -0
- package/shell/zsh/config/plugins.zsh +83 -0
- package/shell/zsh/config/variables.zsh +191 -0
- package/shell/zsh/powerlevel10k.zsh-theme +83 -0
- package/shell/zsh/zsh-autosuggestions.zsh +871 -0
- package/ssh/config +46 -0
- package/ssh/config.duplo +21 -0
- package/ssh/config.mikpc +35 -0
- package/ssh/personal_signing.pub +1 -0
- package/templates/.envrc.example +34 -0
- package/templates/.envrc.quickstart +17 -0
- package/wezterm/wezterm.lua +249 -0
- package/wsl/.wslconfig +3 -0
- package/wsl/wsl.conf +18 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
|
|
3
|
+
# Common configuration management functions
|
|
4
|
+
# Used by both push-configs.zsh and pull-configs.zsh
|
|
5
|
+
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
# Detect platform and set HOME appropriately
|
|
9
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
10
|
+
HOME="${HOME:-$(cd ~ && pwd)}"
|
|
11
|
+
IS_MAC=true
|
|
12
|
+
IS_WINDOWS=false
|
|
13
|
+
elif [[ -d "/mnt/c" ]]; then
|
|
14
|
+
# WSL or Windows environment - HOME should already be set correctly
|
|
15
|
+
HOME="${HOME:-$(cd ~ && pwd)}"
|
|
16
|
+
IS_MAC=false
|
|
17
|
+
IS_WINDOWS=true
|
|
18
|
+
# Get Windows username from WSL
|
|
19
|
+
WINDOWS_USER="wolfe"
|
|
20
|
+
else
|
|
21
|
+
# Linux or other Unix - use standard HOME
|
|
22
|
+
HOME="${HOME:-$(cd ~ && pwd)}"
|
|
23
|
+
IS_MAC=false
|
|
24
|
+
IS_WINDOWS=false
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Resolve repo root if CONFIG_DIR isn't provided by the environment
|
|
28
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
29
|
+
|
|
30
|
+
# Detect CONFIG_DIR dynamically based on hostname
|
|
31
|
+
# On Windows/PowerShell, hostname might return incorrect value, so try alternatives
|
|
32
|
+
if [[ "$IS_WINDOWS" == "true" ]]; then
|
|
33
|
+
# Try to detect based on current directory
|
|
34
|
+
if [[ "$PWD" == *"mikpc"* ]]; then
|
|
35
|
+
HOST="mikpc"
|
|
36
|
+
elif [[ "$PWD" == *"duplo"* ]]; then
|
|
37
|
+
HOST="duplo"
|
|
38
|
+
elif [[ "$PWD" == *"mikbook"* ]]; then
|
|
39
|
+
HOST="mikbook"
|
|
40
|
+
elif [[ "$PWD" == *"mikmac"* ]]; then
|
|
41
|
+
HOST="mikmac"
|
|
42
|
+
else
|
|
43
|
+
HOST="mikpc" # Default fallback for Windows
|
|
44
|
+
fi
|
|
45
|
+
else
|
|
46
|
+
HOST="$(hostname 2>/dev/null || echo 'unknown')"
|
|
47
|
+
fi
|
|
48
|
+
case "${HOST}" in
|
|
49
|
+
"mikmac")
|
|
50
|
+
CONFIG_DIR="${HOME}/Dropbox/dev/configs"
|
|
51
|
+
;;
|
|
52
|
+
"mikbook")
|
|
53
|
+
CONFIG_DIR="${HOME}/Dropbox/mikbook/dev/configs"
|
|
54
|
+
;;
|
|
55
|
+
"duplo")
|
|
56
|
+
CONFIG_DIR="${HOME}/Dropbox/duplo/dev/chiefmikey/configs"
|
|
57
|
+
;;
|
|
58
|
+
"mikpc")
|
|
59
|
+
# Windows PC - handle both WSL and native paths
|
|
60
|
+
if [[ -n "${WSL_DISTRO_NAME:-}" ]] || [[ -f /proc/version ]] && grep -qi microsoft /proc/version 2>/dev/null; then
|
|
61
|
+
# WSL path - check if it exists, otherwise use SCRIPT_DIR fallback
|
|
62
|
+
if [[ -d "${HOME}/Dropbox/mikpc/dev/configs" ]]; then
|
|
63
|
+
CONFIG_DIR="${HOME}/Dropbox/mikpc/dev/configs"
|
|
64
|
+
elif [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/Dropbox/mikpc/dev/configs" ]]; then
|
|
65
|
+
CONFIG_DIR="/mnt/c/Users/${WINDOWS_USER:-$USER}/Dropbox/mikpc/dev/configs"
|
|
66
|
+
else
|
|
67
|
+
# Fallback to relative path resolution
|
|
68
|
+
CONFIG_DIR="${CONFIG_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}"
|
|
69
|
+
fi
|
|
70
|
+
else
|
|
71
|
+
# Fallback to relative path resolution
|
|
72
|
+
CONFIG_DIR="${CONFIG_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}"
|
|
73
|
+
fi
|
|
74
|
+
;;
|
|
75
|
+
*)
|
|
76
|
+
# Fallback to relative path resolution
|
|
77
|
+
CONFIG_DIR="${CONFIG_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}"
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
|
|
81
|
+
# Backup configuration
|
|
82
|
+
# On Windows/WSL, use Linux filesystem for backups (NTFS has permission issues)
|
|
83
|
+
if [[ "$IS_WINDOWS" == "true" ]]; then
|
|
84
|
+
BACKUP_DIR="${HOME}/.config/config-backups"
|
|
85
|
+
else
|
|
86
|
+
BACKUP_DIR="${CONFIG_DIR}/backups"
|
|
87
|
+
fi
|
|
88
|
+
MAX_BACKUPS=5 # Keep only 5 most recent backups per file
|
|
89
|
+
|
|
90
|
+
# Load variable definitions if available
|
|
91
|
+
if [ -f "${CONFIG_DIR}/shell/zsh/config/variables.zsh" ]; then
|
|
92
|
+
# Source the variables
|
|
93
|
+
source "${CONFIG_DIR}/shell/zsh/config/variables.zsh"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Color output functions
|
|
97
|
+
info() { echo -e "\033[0;34m[INFO]\033[0m $1"; }
|
|
98
|
+
success() { echo -e "\033[0;32m[SUCCESS]\033[0m $1"; }
|
|
99
|
+
warn() { echo -e "\033[0;33m[WARN]\033[0m $1"; }
|
|
100
|
+
error() { echo -e "\033[0;31m[ERROR]\033[0m $1"; }
|
|
101
|
+
|
|
102
|
+
# Ensure directory exists
|
|
103
|
+
ensure_dir() {
|
|
104
|
+
local dir="$1"
|
|
105
|
+
if [[ ! -d "$dir" ]]; then
|
|
106
|
+
mkdir -p "$dir"
|
|
107
|
+
info "Created directory: $dir"
|
|
108
|
+
fi
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Create backup directory structure for a file
|
|
112
|
+
create_backup_dir() {
|
|
113
|
+
local file_path="$1"
|
|
114
|
+
# Convert absolute path to relative path for backup structure
|
|
115
|
+
local relative_path="${file_path#"${HOME}"}"
|
|
116
|
+
local backup_subdir="${BACKUP_DIR}${relative_path}"
|
|
117
|
+
local backup_dir
|
|
118
|
+
backup_dir="$(dirname "$backup_subdir")"
|
|
119
|
+
ensure_dir "$backup_dir" >/dev/null
|
|
120
|
+
echo "$backup_dir"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Clean up old backups for a specific file
|
|
124
|
+
cleanup_old_backups() {
|
|
125
|
+
local backup_dir="$1"
|
|
126
|
+
local file_basename="$2"
|
|
127
|
+
|
|
128
|
+
# Find all backup files for this specific file and sort by modification time (newest first)
|
|
129
|
+
local backup_files
|
|
130
|
+
backup_files="$(find "$backup_dir" -name "${file_basename}.backup.*" -type f -printf '%T@ %p\n' 2>/dev/null | sort -nr | cut -d' ' -f2- || true)"
|
|
131
|
+
|
|
132
|
+
if [[ -n "$backup_files" ]]; then
|
|
133
|
+
local count=0
|
|
134
|
+
while IFS= read -r backup_file; do
|
|
135
|
+
count=$((count + 1))
|
|
136
|
+
if [[ $count -gt $MAX_BACKUPS ]]; then
|
|
137
|
+
rm -f "$backup_file"
|
|
138
|
+
info "Removed old backup: $(basename "$backup_file")"
|
|
139
|
+
fi
|
|
140
|
+
done <<< "$backup_files"
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Validation functions
|
|
145
|
+
validate_json() {
|
|
146
|
+
local file="$1"
|
|
147
|
+
if command -v jq >/dev/null 2>&1; then
|
|
148
|
+
if ! jq empty "$file" 2>/dev/null; then
|
|
149
|
+
error "Invalid JSON in $file"
|
|
150
|
+
return 1
|
|
151
|
+
fi
|
|
152
|
+
else
|
|
153
|
+
warn "jq not available, skipping JSON validation for $file"
|
|
154
|
+
fi
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
validate_xml() {
|
|
158
|
+
local file="$1"
|
|
159
|
+
if command -v xmllint >/dev/null 2>&1; then
|
|
160
|
+
if ! xmllint --noout "$file" 2>/dev/null; then
|
|
161
|
+
error "Invalid XML in $file"
|
|
162
|
+
return 1
|
|
163
|
+
fi
|
|
164
|
+
else
|
|
165
|
+
warn "xmllint not available, skipping XML validation for $file"
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
validate_plist() {
|
|
170
|
+
local file="$1"
|
|
171
|
+
if command -v plutil >/dev/null 2>&1; then
|
|
172
|
+
if ! plutil -lint "$file" >/dev/null 2>&1; then
|
|
173
|
+
error "Invalid plist in $file"
|
|
174
|
+
return 1
|
|
175
|
+
fi
|
|
176
|
+
else
|
|
177
|
+
warn "plutil not available, skipping plist validation for $file"
|
|
178
|
+
fi
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
validate_yaml() {
|
|
182
|
+
local file="$1"
|
|
183
|
+
if command -v yq >/dev/null 2>&1; then
|
|
184
|
+
if ! yq '.' "$file" >/dev/null 2>&1; then
|
|
185
|
+
error "Invalid YAML in $file"
|
|
186
|
+
return 1
|
|
187
|
+
fi
|
|
188
|
+
else
|
|
189
|
+
warn "yq not available, skipping YAML validation for $file"
|
|
190
|
+
fi
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
validate_shell() {
|
|
194
|
+
local file="$1"
|
|
195
|
+
local shell_type="$2"
|
|
196
|
+
if [[ "$shell_type" == "zsh" ]]; then
|
|
197
|
+
if ! zsh -n "$file" 2>/dev/null; then
|
|
198
|
+
error "Invalid zsh syntax in $file"
|
|
199
|
+
return 1
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Safe copy with validation and backup
|
|
205
|
+
safe_copy() {
|
|
206
|
+
local src="$1"
|
|
207
|
+
local dest="$2"
|
|
208
|
+
local validate_func="$3"
|
|
209
|
+
local backup_suffix
|
|
210
|
+
backup_suffix=".backup.$(date +%Y%m%d_%H%M%S)"
|
|
211
|
+
|
|
212
|
+
if [[ ! -f "$src" ]]; then
|
|
213
|
+
warn "Source file not found: $src"
|
|
214
|
+
return 0 # Return success to allow script to continue processing other files
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
# If destination is a dangling symlink, remove it so we can write the real file.
|
|
218
|
+
if [[ -L "$dest" && ! -e "$dest" ]]; then
|
|
219
|
+
rm -f "$dest"
|
|
220
|
+
warn "Removed dangling symlink: $dest"
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Validate source file if validation function provided
|
|
224
|
+
if [[ -n "$validate_func" ]]; then
|
|
225
|
+
if ! "$validate_func" "$src"; then
|
|
226
|
+
error "Validation failed for $src, skipping copy"
|
|
227
|
+
return 0 # Return success to allow script to continue processing other files
|
|
228
|
+
fi
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# Create backup if destination exists
|
|
232
|
+
if [[ -f "$dest" ]]; then
|
|
233
|
+
local backup_dir
|
|
234
|
+
backup_dir="$(create_backup_dir "$dest")"
|
|
235
|
+
local file_basename
|
|
236
|
+
file_basename="$(basename "$dest")"
|
|
237
|
+
local backup_file="${backup_dir}/${file_basename}${backup_suffix}"
|
|
238
|
+
|
|
239
|
+
cp "$dest" "$backup_file" 2>/dev/null || warn "Could not backup: $dest"
|
|
240
|
+
info "Backed up existing file: $backup_file"
|
|
241
|
+
|
|
242
|
+
# Clean up old backups for this file
|
|
243
|
+
cleanup_old_backups "$backup_dir" "$file_basename"
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
ensure_dir "$(dirname "$dest")"
|
|
247
|
+
if cp "$src" "$dest" 2>/dev/null; then
|
|
248
|
+
success "Copied: $src → $dest"
|
|
249
|
+
else
|
|
250
|
+
error "Failed to copy: $src → $dest (permission denied?)"
|
|
251
|
+
return 0 # Continue processing other files
|
|
252
|
+
fi
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# Copy if source exists with validation
|
|
256
|
+
copy_if_exists() {
|
|
257
|
+
local src="$1"
|
|
258
|
+
local dest="$2"
|
|
259
|
+
local validate_func="$3"
|
|
260
|
+
|
|
261
|
+
if [[ -f "$src" ]]; then
|
|
262
|
+
# Validate source file if validation function provided
|
|
263
|
+
if [[ -n "$validate_func" ]]; then
|
|
264
|
+
if ! "$validate_func" "$src"; then
|
|
265
|
+
error "Validation failed for $src, skipping copy"
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
ensure_dir "$(dirname "$dest")"
|
|
271
|
+
|
|
272
|
+
# Special handling for Rectangle config (use defaults command)
|
|
273
|
+
if [[ "$src" == *"RectangleConfig.plist" ]]; then
|
|
274
|
+
# Use defaults command to load Rectangle preferences
|
|
275
|
+
if defaults import com.knollsoft.Rectangle "$src" 2>/dev/null; then
|
|
276
|
+
success "Imported Rectangle preferences: $src → Rectangle defaults"
|
|
277
|
+
else
|
|
278
|
+
error "Failed to import Rectangle preferences"
|
|
279
|
+
return 1
|
|
280
|
+
fi
|
|
281
|
+
else
|
|
282
|
+
if cp "$src" "$dest" 2>/dev/null; then
|
|
283
|
+
success "Copied: $src → $dest"
|
|
284
|
+
else
|
|
285
|
+
error "Failed to copy: $src → $dest (permission denied?)"
|
|
286
|
+
fi
|
|
287
|
+
fi
|
|
288
|
+
else
|
|
289
|
+
warn "Source file not found: $src"
|
|
290
|
+
fi
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# Determine git config based on hostname
|
|
294
|
+
get_git_config() {
|
|
295
|
+
case "${HOST}" in
|
|
296
|
+
"mikbook") echo "personal.gitconfig" ;;
|
|
297
|
+
"duplo") echo "work.gitconfig" ;;
|
|
298
|
+
"mikpc") echo "personal.gitconfig" ;; # Windows PC - default to personal
|
|
299
|
+
*) echo "personal.gitconfig" ;; # Default fallback
|
|
300
|
+
esac
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# Determine SSH config based on hostname (allows host-specific SSH settings)
|
|
304
|
+
get_ssh_config() {
|
|
305
|
+
case "${HOST}" in
|
|
306
|
+
"duplo") echo "config.duplo" ;;
|
|
307
|
+
"mikpc") echo "config.mikpc" ;;
|
|
308
|
+
*) echo "config" ;;
|
|
309
|
+
esac
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# Configuration file mappings (using functions instead of associative arrays for compatibility)
|
|
313
|
+
get_config_dest() {
|
|
314
|
+
local config_file="$1"
|
|
315
|
+
case "$config_file" in
|
|
316
|
+
"shell/zsh/.zshrc") echo "${HOME}/.zshrc" ;;
|
|
317
|
+
"git/config/base.gitconfig") echo "${HOME}/.config/git/base.gitconfig" ;;
|
|
318
|
+
"git/config/chiefmikey.gitconfig") echo "${HOME}/.config/git/chiefmikey.gitconfig" ;;
|
|
319
|
+
"git/config/$(get_git_config)") echo "${HOME}/.gitconfig" ;;
|
|
320
|
+
"ide/vscode/settings.json")
|
|
321
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
322
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Code/User/settings.json"
|
|
323
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
324
|
+
echo "${HOME}/Library/Application Support/Code/User/settings.json"
|
|
325
|
+
else
|
|
326
|
+
echo "${HOME}/.config/Code/User/settings.json"
|
|
327
|
+
fi
|
|
328
|
+
;;
|
|
329
|
+
"ide/vscode/keybindings.json")
|
|
330
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
331
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Code/User/keybindings.json"
|
|
332
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
333
|
+
echo "${HOME}/Library/Application Support/Code/User/keybindings.json"
|
|
334
|
+
else
|
|
335
|
+
echo "${HOME}/.config/Code/User/keybindings.json"
|
|
336
|
+
fi
|
|
337
|
+
;;
|
|
338
|
+
"ide/vscode/insiders/settings.json")
|
|
339
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
340
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Code - Insiders/User/settings.json"
|
|
341
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
342
|
+
echo "${HOME}/Library/Application Support/Code - Insiders/User/settings.json"
|
|
343
|
+
else
|
|
344
|
+
echo "${HOME}/.config/Code - Insiders/User/settings.json"
|
|
345
|
+
fi
|
|
346
|
+
;;
|
|
347
|
+
"ide/vscode/insiders/keybindings.json")
|
|
348
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
349
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Code - Insiders/User/keybindings.json"
|
|
350
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
351
|
+
echo "${HOME}/Library/Application Support/Code - Insiders/User/keybindings.json"
|
|
352
|
+
else
|
|
353
|
+
echo "${HOME}/.config/Code - Insiders/User/keybindings.json"
|
|
354
|
+
fi
|
|
355
|
+
;;
|
|
356
|
+
"ide/cursor/settings.json")
|
|
357
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
358
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Cursor/User/settings.json"
|
|
359
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
360
|
+
echo "${HOME}/Library/Application Support/Cursor/User/settings.json"
|
|
361
|
+
else
|
|
362
|
+
echo "${HOME}/.config/Cursor/User/settings.json"
|
|
363
|
+
fi
|
|
364
|
+
;;
|
|
365
|
+
"ide/cursor/keybindings.json")
|
|
366
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
367
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Cursor/User/keybindings.json"
|
|
368
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
369
|
+
echo "${HOME}/Library/Application Support/Cursor/User/keybindings.json"
|
|
370
|
+
else
|
|
371
|
+
echo "${HOME}/.config/Cursor/User/keybindings.json"
|
|
372
|
+
fi
|
|
373
|
+
;;
|
|
374
|
+
"ide/cursor/mcp.json")
|
|
375
|
+
# Mac uses mcp.json directly, Windows transforms it
|
|
376
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
377
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.cursor/mcp.json"
|
|
378
|
+
else
|
|
379
|
+
echo "${HOME}/.cursor/mcp.json"
|
|
380
|
+
fi
|
|
381
|
+
;;
|
|
382
|
+
"scripts/mcp/mcp-launcher.sh")
|
|
383
|
+
# Install launcher into a stable per-user location (Mac/Unix)
|
|
384
|
+
if [[ "$IS_WINDOWS" == "true" ]]; then
|
|
385
|
+
echo ""
|
|
386
|
+
else
|
|
387
|
+
echo "${HOME}/.local/bin/mcp-launcher.sh"
|
|
388
|
+
fi
|
|
389
|
+
;;
|
|
390
|
+
"scripts/mcp/mcp-launcher.ps1")
|
|
391
|
+
# Install launcher into a stable per-user location (Windows filesystem via WSL)
|
|
392
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
393
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.local/bin/mcp-launcher.ps1"
|
|
394
|
+
else
|
|
395
|
+
echo ""
|
|
396
|
+
fi
|
|
397
|
+
;;
|
|
398
|
+
"gh/config.yml") echo "${HOME}/.config/gh/config.yml" ;;
|
|
399
|
+
"gh/hosts.yml") echo "${HOME}/.config/gh/hosts.yml" ;;
|
|
400
|
+
"ssh/$(get_ssh_config)") echo "${HOME}/.ssh/config" ;;
|
|
401
|
+
"ssh/personal_signing.pub")
|
|
402
|
+
# Only deploy on non-duplo hosts
|
|
403
|
+
if [[ "${HOST}" != "duplo" ]]; then
|
|
404
|
+
echo "${HOME}/.ssh/personal_signing.pub"
|
|
405
|
+
else
|
|
406
|
+
echo ""
|
|
407
|
+
fi
|
|
408
|
+
;;
|
|
409
|
+
"iterm/com.googlecode.iterm2.plist")
|
|
410
|
+
# Mac only
|
|
411
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
412
|
+
echo "${HOME}/Library/Preferences/com.googlecode.iterm2.plist"
|
|
413
|
+
else
|
|
414
|
+
echo ""
|
|
415
|
+
fi
|
|
416
|
+
;;
|
|
417
|
+
"wezterm/wezterm.lua")
|
|
418
|
+
# Cross-platform (primarily Windows/Linux, but works on Mac too)
|
|
419
|
+
echo "${HOME}/.config/wezterm/wezterm.lua"
|
|
420
|
+
;;
|
|
421
|
+
"ghostty/config")
|
|
422
|
+
# Cross-platform (Mac/Linux — Ghostty not available on Windows yet)
|
|
423
|
+
if [[ "$IS_WINDOWS" == "true" ]]; then
|
|
424
|
+
echo ""
|
|
425
|
+
else
|
|
426
|
+
echo "${HOME}/.config/ghostty/config"
|
|
427
|
+
fi
|
|
428
|
+
;;
|
|
429
|
+
"shell/powershell/Microsoft.PowerShell_profile.ps1")
|
|
430
|
+
# Windows only — PowerShell profile with debug helpers
|
|
431
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
432
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/Documents/WindowsPowerShell/Microsoft.PowerShell_profile.ps1"
|
|
433
|
+
else
|
|
434
|
+
echo ""
|
|
435
|
+
fi
|
|
436
|
+
;;
|
|
437
|
+
"ide/vscode/vscode/extensions.txt")
|
|
438
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming" ]]; then
|
|
439
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Code/User/extensions.txt"
|
|
440
|
+
elif [[ "$IS_MAC" == "true" ]]; then
|
|
441
|
+
echo "${HOME}/Library/Application Support/Code/User/extensions.txt"
|
|
442
|
+
else
|
|
443
|
+
echo "${HOME}/.config/Code/User/extensions.txt"
|
|
444
|
+
fi
|
|
445
|
+
;;
|
|
446
|
+
"apps/RectangleConfig.plist")
|
|
447
|
+
# Mac only
|
|
448
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
449
|
+
echo "${HOME}/Library/Preferences/com.knollsoft.Rectangle.plist"
|
|
450
|
+
else
|
|
451
|
+
echo ""
|
|
452
|
+
fi
|
|
453
|
+
;;
|
|
454
|
+
"apps/iStat Menus Settings.ismp7")
|
|
455
|
+
# Mac only
|
|
456
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
457
|
+
echo "${HOME}/Library/Preferences/com.bjango.istatmenus.plist"
|
|
458
|
+
else
|
|
459
|
+
echo ""
|
|
460
|
+
fi
|
|
461
|
+
;;
|
|
462
|
+
"apps/Synergy")
|
|
463
|
+
# Can work on both, but path differs
|
|
464
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
465
|
+
echo "${HOME}/Library/Preferences/Synergy/synergy.conf"
|
|
466
|
+
elif [[ "$IS_WINDOWS" == "true" ]]; then
|
|
467
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/AppData/Roaming/Synergy/synergy.conf"
|
|
468
|
+
else
|
|
469
|
+
echo "${HOME}/.synergy.conf"
|
|
470
|
+
fi
|
|
471
|
+
;;
|
|
472
|
+
"docker/mcp/registry.yaml")
|
|
473
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
474
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.docker/mcp/registry.yaml"
|
|
475
|
+
else
|
|
476
|
+
echo "${HOME}/.docker/mcp/registry.yaml"
|
|
477
|
+
fi
|
|
478
|
+
;;
|
|
479
|
+
"docker/mcp/catalog.json")
|
|
480
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
481
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.docker/mcp/catalog.json"
|
|
482
|
+
else
|
|
483
|
+
echo "${HOME}/.docker/mcp/catalog.json"
|
|
484
|
+
fi
|
|
485
|
+
;;
|
|
486
|
+
"docker/mcp/config.yaml")
|
|
487
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
488
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.docker/mcp/config.yaml"
|
|
489
|
+
else
|
|
490
|
+
echo "${HOME}/.docker/mcp/config.yaml"
|
|
491
|
+
fi
|
|
492
|
+
;;
|
|
493
|
+
"docker/mcp/tools.yaml")
|
|
494
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
495
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.docker/mcp/tools.yaml"
|
|
496
|
+
else
|
|
497
|
+
echo "${HOME}/.docker/mcp/tools.yaml"
|
|
498
|
+
fi
|
|
499
|
+
;;
|
|
500
|
+
"docker/mcp/catalogs/docker-mcp.yaml")
|
|
501
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
502
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.docker/mcp/catalogs/docker-mcp.yaml"
|
|
503
|
+
else
|
|
504
|
+
echo "${HOME}/.docker/mcp/catalogs/docker-mcp.yaml"
|
|
505
|
+
fi
|
|
506
|
+
;;
|
|
507
|
+
"docker/cli/daemon.json") echo "${HOME}/.docker/daemon.json" ;;
|
|
508
|
+
"docker/cli/features.json") echo "${HOME}/.docker/features.json" ;;
|
|
509
|
+
# Claude Code configs
|
|
510
|
+
"claude/settings.json") echo "${HOME}/.claude/settings.json" ;;
|
|
511
|
+
"claude/settings.local.json") echo "${HOME}/.claude/settings.local.json" ;;
|
|
512
|
+
"claude/hooks.json") echo "${HOME}/.claude/hooks.json" ;;
|
|
513
|
+
"claude/statusline-command.sh") echo "${HOME}/.claude/statusline-command.sh" ;;
|
|
514
|
+
"claude/CLAUDE.md") echo "${HOME}/CLAUDE.md" ;;
|
|
515
|
+
"claude/desktop/claude_desktop_config.json")
|
|
516
|
+
# Mac only - Claude Desktop app
|
|
517
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
518
|
+
echo "${HOME}/Library/Application Support/Claude/claude_desktop_config.json"
|
|
519
|
+
else
|
|
520
|
+
echo ""
|
|
521
|
+
fi
|
|
522
|
+
;;
|
|
523
|
+
# WSL configs (Windows only)
|
|
524
|
+
"wsl/.wslconfig")
|
|
525
|
+
if [[ "$IS_WINDOWS" == "true" ]] && [[ -d "/mnt/c/Users/${WINDOWS_USER:-$USER}" ]]; then
|
|
526
|
+
echo "/mnt/c/Users/${WINDOWS_USER:-$USER}/.wslconfig"
|
|
527
|
+
else
|
|
528
|
+
echo ""
|
|
529
|
+
fi
|
|
530
|
+
;;
|
|
531
|
+
"wsl/wsl.conf")
|
|
532
|
+
if [[ "$IS_WINDOWS" == "true" ]]; then
|
|
533
|
+
echo "/etc/wsl.conf"
|
|
534
|
+
else
|
|
535
|
+
echo ""
|
|
536
|
+
fi
|
|
537
|
+
;;
|
|
538
|
+
*) echo "" ;;
|
|
539
|
+
esac
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
get_validation_func() {
|
|
543
|
+
local config_file="$1"
|
|
544
|
+
case "$config_file" in
|
|
545
|
+
"shell/zsh/.zshrc") echo "validate_shell_zsh" ;;
|
|
546
|
+
"ide/vscode/settings.json") echo "validate_json" ;;
|
|
547
|
+
"ide/vscode/keybindings.json") echo "validate_json" ;;
|
|
548
|
+
"ide/vscode/insiders/settings.json") echo "validate_json" ;;
|
|
549
|
+
"ide/vscode/insiders/keybindings.json") echo "validate_json" ;;
|
|
550
|
+
"ide/cursor/settings.json") echo "validate_json" ;;
|
|
551
|
+
"ide/cursor/keybindings.json") echo "validate_json" ;;
|
|
552
|
+
"ide/cursor/mcp.json") echo "validate_json" ;;
|
|
553
|
+
"gh/config.yml") echo "validate_yaml" ;;
|
|
554
|
+
"gh/hosts.yml") echo "validate_yaml" ;;
|
|
555
|
+
"iterm/com.googlecode.iterm2.plist") echo "validate_plist" ;;
|
|
556
|
+
"wezterm/wezterm.lua") echo "" ;;
|
|
557
|
+
"ghostty/config") echo "" ;;
|
|
558
|
+
"shell/powershell/Microsoft.PowerShell_profile.ps1") echo "" ;;
|
|
559
|
+
"ide/vscode/vscode/extensions.txt") echo "" ;;
|
|
560
|
+
"apps/RectangleConfig.plist") echo "validate_plist" ;;
|
|
561
|
+
"apps/iStat Menus Settings.ismp7") echo "validate_plist" ;;
|
|
562
|
+
"apps/Synergy") echo "" ;;
|
|
563
|
+
"docker/mcp/registry.yaml") echo "validate_yaml" ;;
|
|
564
|
+
"docker/mcp/catalog.json") echo "validate_json" ;;
|
|
565
|
+
"docker/mcp/config.yaml") echo "validate_yaml" ;;
|
|
566
|
+
"docker/mcp/tools.yaml") echo "validate_yaml" ;;
|
|
567
|
+
"docker/mcp/catalogs/docker-mcp.yaml") echo "validate_yaml" ;;
|
|
568
|
+
"docker/cli/daemon.json") echo "validate_json" ;;
|
|
569
|
+
"docker/cli/features.json") echo "validate_json" ;;
|
|
570
|
+
"scripts/mcp/mcp-launcher.sh") echo "validate_shell_zsh" ;;
|
|
571
|
+
"scripts/mcp/mcp-launcher.ps1") echo "" ;;
|
|
572
|
+
# Claude Code configs
|
|
573
|
+
"claude/settings.json") echo "validate_json" ;;
|
|
574
|
+
"claude/settings.local.json") echo "validate_json" ;;
|
|
575
|
+
"claude/hooks.json") echo "validate_json" ;;
|
|
576
|
+
"claude/statusline-command.sh") echo "validate_shell_zsh" ;;
|
|
577
|
+
"claude/CLAUDE.md") echo "" ;;
|
|
578
|
+
"claude/desktop/claude_desktop_config.json") echo "validate_json" ;;
|
|
579
|
+
"wsl/.wslconfig") echo "" ;;
|
|
580
|
+
"wsl/wsl.conf") echo "" ;;
|
|
581
|
+
*) echo "" ;;
|
|
582
|
+
esac
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
# Wrapper functions for shell validation
|
|
586
|
+
validate_shell_zsh() { validate_shell "$1" "zsh"; }
|
|
587
|
+
|
|
588
|
+
# Transform MCP config from Mac to Windows format
|
|
589
|
+
transform_mcp_mac_to_windows() {
|
|
590
|
+
local input_file="$1"
|
|
591
|
+
local output_file="$2"
|
|
592
|
+
local windows_user="${WINDOWS_USER:-$USER}"
|
|
593
|
+
local windows_config_dir="C:\\\\Users\\\\${windows_user}\\\\Dropbox\\\\mikpc\\\\dev\\\\configs"
|
|
594
|
+
local windows_home="C:\\\\Users\\\\${windows_user}"
|
|
595
|
+
local windows_npx_cmd=""
|
|
596
|
+
|
|
597
|
+
# On Windows, Cursor spawns MCP servers directly (not via PowerShell),
|
|
598
|
+
# so relying on PATH/PATHEXT for `npx` can be flaky. Prefer an absolute npx.cmd.
|
|
599
|
+
if [[ -f "/mnt/c/Program Files/nodejs/npx.cmd" ]]; then
|
|
600
|
+
windows_npx_cmd="C:\\Program Files\\nodejs\\npx.cmd"
|
|
601
|
+
elif [[ -f "/mnt/c/Users/${windows_user}/AppData/Roaming/npm/npx.cmd" ]]; then
|
|
602
|
+
windows_npx_cmd="C:\\Users\\${windows_user}\\AppData\\Roaming\\npm\\npx.cmd"
|
|
603
|
+
fi
|
|
604
|
+
|
|
605
|
+
# Use jq to transform the JSON
|
|
606
|
+
if command -v jq >/dev/null 2>&1; then
|
|
607
|
+
jq --arg win_dir "$windows_config_dir" --arg win_user "$windows_user" --arg win_home "$windows_home" --arg win_npx "$windows_npx_cmd" '
|
|
608
|
+
.mcpServers |= with_entries(
|
|
609
|
+
.value |= (
|
|
610
|
+
# Transform filesystem and git paths
|
|
611
|
+
if .args then
|
|
612
|
+
.args = (.args | map(
|
|
613
|
+
if type == "string" then
|
|
614
|
+
if . == "/Users/mikl/Dropbox/dev" or (test("^\\$\\{userHome\\}/Dropbox/dev$")) then
|
|
615
|
+
"C:\\\\Users\\\\\($win_user)\\\\Dropbox\\\\mikpc\\\\dev"
|
|
616
|
+
elif . == "/Users/mikl/Dropbox/dev/configs" or (test("^\\$\\{userHome\\}/Dropbox/dev/configs$")) then
|
|
617
|
+
$win_dir
|
|
618
|
+
elif startswith("/Users/mikl/Dropbox/dev/configs/scripts/mcp/") then
|
|
619
|
+
$win_dir + "\\\\scripts\\\\mcp\\\\" + (. | split("/") | .[-1] | sub("\\.sh$"; ".ps1"))
|
|
620
|
+
elif test("^\\$\\{userHome\\}/Dropbox/dev/configs/scripts/mcp/") then
|
|
621
|
+
$win_dir + "\\\\scripts\\\\mcp\\\\" + (. | split("/") | .[-1] | sub("\\.sh$"; ".ps1"))
|
|
622
|
+
elif contains("${userHome}") then
|
|
623
|
+
gsub("\\$\\{userHome\\}"; $win_home) | gsub("/"; "\\\\")
|
|
624
|
+
else
|
|
625
|
+
.
|
|
626
|
+
end
|
|
627
|
+
else
|
|
628
|
+
.
|
|
629
|
+
end
|
|
630
|
+
))
|
|
631
|
+
else
|
|
632
|
+
.
|
|
633
|
+
end |
|
|
634
|
+
# Transform wrapper script commands
|
|
635
|
+
if .command then
|
|
636
|
+
if (.command | endswith("mcp-launcher.sh")) then
|
|
637
|
+
# Launcher: preserve wrapper arg, translate launcher + wrapper to ps1
|
|
638
|
+
.command as $orig_cmd
|
|
639
|
+
| (.args // []) as $orig_args
|
|
640
|
+
| .command = "powershell"
|
|
641
|
+
| .args = (
|
|
642
|
+
[
|
|
643
|
+
"-ExecutionPolicy",
|
|
644
|
+
"Bypass",
|
|
645
|
+
"-File",
|
|
646
|
+
(
|
|
647
|
+
$orig_cmd
|
|
648
|
+
| sub("\\.sh$"; ".ps1")
|
|
649
|
+
| sub("/Users/mikl/Dropbox/dev/configs"; $win_dir)
|
|
650
|
+
| gsub("^\\$\\{userHome\\}/Dropbox/dev/configs"; $win_dir)
|
|
651
|
+
| gsub("\\$\\{userHome\\}"; $win_home)
|
|
652
|
+
| gsub("/"; "\\\\")
|
|
653
|
+
)
|
|
654
|
+
]
|
|
655
|
+
+ ($orig_args | map(
|
|
656
|
+
if type == "string" then
|
|
657
|
+
sub("\\.sh$"; ".ps1")
|
|
658
|
+
else
|
|
659
|
+
.
|
|
660
|
+
end
|
|
661
|
+
))
|
|
662
|
+
)
|
|
663
|
+
elif .command | test("\\.sh$") then
|
|
664
|
+
.command as $orig_cmd |
|
|
665
|
+
.command = "powershell" |
|
|
666
|
+
.args = ["-ExecutionPolicy", "Bypass", "-File", ($orig_cmd | sub("\\.sh$"; ".ps1") | sub("/Users/mikl/Dropbox/dev/configs"; $win_dir) | gsub("/"; "\\\\"))]
|
|
667
|
+
elif .command | startswith("/Users/mikl/Dropbox/dev/configs/scripts/mcp/") then
|
|
668
|
+
.command as $orig_cmd |
|
|
669
|
+
.command = "powershell" |
|
|
670
|
+
.args = ["-ExecutionPolicy", "Bypass", "-File", ($orig_cmd | sub("/Users/mikl/Dropbox/dev/configs"; $win_dir) | gsub("/"; "\\\\") | sub("\\.sh$"; ".ps1"))]
|
|
671
|
+
elif ($win_npx != "" and .command == "npx") then
|
|
672
|
+
.command = $win_npx
|
|
673
|
+
else
|
|
674
|
+
.
|
|
675
|
+
end
|
|
676
|
+
else
|
|
677
|
+
.
|
|
678
|
+
end
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
' "$input_file" > "$output_file"
|
|
682
|
+
else
|
|
683
|
+
# Fallback: simple sed-based transformation (less reliable)
|
|
684
|
+
warn "jq not available, using sed for MCP transformation (may be less accurate)"
|
|
685
|
+
sed -e "s|/Users/mikl/Dropbox/dev|C:\\\\\\\\Users\\\\\\\\${windows_user}\\\\\\\\Dropbox\\\\\\\\mikpc\\\\\\\\dev|g" \
|
|
686
|
+
-e "s|/Users/mikl/Dropbox/dev/configs|${windows_config_dir}|g" \
|
|
687
|
+
-e "s|\\.sh|.ps1|g" \
|
|
688
|
+
-e "s|\"command\": \"/[^\"]*mcp/[^\"]*\\.ps1\"|\"command\": \"powershell\", \"args\": [\"-ExecutionPolicy\", \"Bypass\", \"-File\", \"&\"|g" \
|
|
689
|
+
"$input_file" > "$output_file"
|
|
690
|
+
fi
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
# Transform Cursor settings from Mac to Windows format
|
|
694
|
+
transform_cursor_settings_mac_to_windows() {
|
|
695
|
+
local input_file="$1"
|
|
696
|
+
local output_file="$2"
|
|
697
|
+
local windows_user="${WINDOWS_USER:-$USER}"
|
|
698
|
+
|
|
699
|
+
# Best-practice approach:
|
|
700
|
+
# - Keep the repo settings as Mac-first source of truth.
|
|
701
|
+
# - On Windows, strip Mac-only keys/values to avoid breaking Cursor UI/theme.
|
|
702
|
+
#
|
|
703
|
+
# We intentionally avoid "translating" most paths, because Windows layouts and
|
|
704
|
+
# installed locations vary. Cursor/VS Code will usually pick correct defaults.
|
|
705
|
+
if command -v jq >/dev/null 2>&1; then
|
|
706
|
+
jq '
|
|
707
|
+
# Remove obviously Mac-only keys (keep theme/editor behavior intact)
|
|
708
|
+
del(
|
|
709
|
+
.["terminal.integrated.env.osx"],
|
|
710
|
+
.["terminal.external.osxExec"],
|
|
711
|
+
.["terminal.integrated.defaultProfile.osx"],
|
|
712
|
+
.["git.path"],
|
|
713
|
+
.["sonarlint.pathToNodeExecutable"]
|
|
714
|
+
)
|
|
715
|
+
|
|
|
716
|
+
# Drop Parallels-specific settings (Mac-only)
|
|
717
|
+
with_entries(select(.key | test("^parallels-desktop\\.") | not))
|
|
718
|
+
|
|
|
719
|
+
# Drop any remaining macOS-scoped keys (e.g. *.osx)
|
|
720
|
+
with_entries(select(.key | test("(\\.|^)osx($|\\.)") | not))
|
|
721
|
+
|
|
|
722
|
+
# Drop string values that are Mac paths (avoid breaking Windows UI/features)
|
|
723
|
+
with_entries(
|
|
724
|
+
select(
|
|
725
|
+
(.value | type) != "string"
|
|
726
|
+
or
|
|
727
|
+
((.value | test("^(\\/Users\\/|\\/opt\\/homebrew\\/|\\/Library\\/|\\$HOME\\/Library\\/)") ) | not)
|
|
728
|
+
)
|
|
729
|
+
)
|
|
730
|
+
' "$input_file" > "$output_file"
|
|
731
|
+
else
|
|
732
|
+
warn "jq not available; copying Cursor settings without transformation"
|
|
733
|
+
cp "$input_file" "$output_file"
|
|
734
|
+
fi
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
# Transform MCP config from Windows to Mac format
|
|
738
|
+
transform_mcp_windows_to_mac() {
|
|
739
|
+
local input_file="$1"
|
|
740
|
+
local output_file="$2"
|
|
741
|
+
local mac_user="${USER}"
|
|
742
|
+
local mac_config_dir="/Users/${mac_user}/Dropbox/dev/configs"
|
|
743
|
+
|
|
744
|
+
# Use jq to transform the JSON
|
|
745
|
+
if command -v jq >/dev/null 2>&1; then
|
|
746
|
+
jq --arg mac_dir "$mac_config_dir" --arg mac_user "$mac_user" '
|
|
747
|
+
.mcpServers |= with_entries(
|
|
748
|
+
.value |= (
|
|
749
|
+
# Transform PowerShell commands back to shell scripts
|
|
750
|
+
if .command == "powershell" and (.args | length > 0) and (.args[-1] | test("\\.ps1$")) then
|
|
751
|
+
.command = (.args[-1] | sub("\\.ps1$"; ".sh") | sub("C:\\\\\\\\Users\\\\\\\\[^\\\\\\\\]+\\\\\\\\Dropbox\\\\\\\\mikpc\\\\\\\\dev\\\\\\\\configs"; $mac_dir) | gsub("\\\\"; "/"))
|
|
752
|
+
| .args = []
|
|
753
|
+
else
|
|
754
|
+
.
|
|
755
|
+
end |
|
|
756
|
+
# Transform Windows paths back to Mac paths
|
|
757
|
+
if .args then
|
|
758
|
+
.args = (.args | map(
|
|
759
|
+
if type == "string" then
|
|
760
|
+
if test("C:\\\\\\\\Users\\\\\\\\[^\\\\\\\\]+\\\\\\\\Dropbox\\\\\\\\mikpc\\\\\\\\dev$") then
|
|
761
|
+
"/Users/\($mac_user)/Dropbox/dev"
|
|
762
|
+
elif test("C:\\\\\\\\Users\\\\\\\\[^\\\\\\\\]+\\\\\\\\Dropbox\\\\\\\\mikpc\\\\\\\\dev\\\\\\\\configs") then
|
|
763
|
+
$mac_dir
|
|
764
|
+
elif test(".*\\\\\\\\scripts\\\\\\\\mcp\\\\\\\\.*\\.ps1$") then
|
|
765
|
+
(. | gsub("\\\\"; "/") | sub("C:/Users/[^/]+/Dropbox/mikpc/dev/configs"; $mac_dir) | sub("\\.ps1$"; ".sh"))
|
|
766
|
+
else
|
|
767
|
+
.
|
|
768
|
+
end
|
|
769
|
+
else
|
|
770
|
+
.
|
|
771
|
+
end
|
|
772
|
+
))
|
|
773
|
+
else
|
|
774
|
+
.
|
|
775
|
+
end
|
|
776
|
+
)
|
|
777
|
+
)
|
|
778
|
+
' "$input_file" > "$output_file"
|
|
779
|
+
else
|
|
780
|
+
# Fallback: simple sed-based transformation
|
|
781
|
+
warn "jq not available, using sed for MCP transformation (may be less accurate)"
|
|
782
|
+
sed -e "s|C:\\\\\\\\Users\\\\\\\\[^\\\\\\\\]*\\\\\\\\Dropbox\\\\\\\\mikpc\\\\\\\\dev|/Users/${mac_user}/Dropbox/dev|g" \
|
|
783
|
+
-e "s|C:\\\\\\\\Users\\\\\\\\[^\\\\\\\\]*\\\\\\\\Dropbox\\\\\\\\mikpc\\\\\\\\dev\\\\\\\\configs|${mac_config_dir}|g" \
|
|
784
|
+
-e "s|\\.ps1|.sh|g" \
|
|
785
|
+
-e "s|\"command\": \"powershell\",|\"command\":|g" \
|
|
786
|
+
-e "s|\"args\": \[\"-ExecutionPolicy\", \"Bypass\", \"-File\", \"\([^\"]*\)\"\]|\"args\": []|g" \
|
|
787
|
+
-e "s|\"command\": \"\([^\"]*\)\"|&|g" \
|
|
788
|
+
"$input_file" > "$output_file"
|
|
789
|
+
fi
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
# Get all configuration files (filtering Mac-only on Windows)
|
|
794
|
+
get_all_configs() {
|
|
795
|
+
# Use zsh array for better performance and maintainability
|
|
796
|
+
local configs=(
|
|
797
|
+
"shell/zsh/.zshrc"
|
|
798
|
+
"git/config/base.gitconfig"
|
|
799
|
+
"git/config/chiefmikey.gitconfig"
|
|
800
|
+
"git/config/$(get_git_config)"
|
|
801
|
+
"ide/vscode/settings.json"
|
|
802
|
+
"ide/vscode/keybindings.json"
|
|
803
|
+
"ide/vscode/insiders/settings.json"
|
|
804
|
+
"ide/vscode/insiders/keybindings.json"
|
|
805
|
+
"ide/cursor/settings.json"
|
|
806
|
+
"ide/cursor/keybindings.json"
|
|
807
|
+
"gh/config.yml"
|
|
808
|
+
"gh/hosts.yml"
|
|
809
|
+
"ssh/$(get_ssh_config)"
|
|
810
|
+
"ssh/personal_signing.pub"
|
|
811
|
+
"ide/vscode/vscode/extensions.txt"
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
# MCP config (transformed on Windows)
|
|
815
|
+
configs+=("ide/cursor/mcp.json")
|
|
816
|
+
|
|
817
|
+
# MCP launcher (installed to stable per-user path)
|
|
818
|
+
configs+=("scripts/mcp/mcp-launcher.sh")
|
|
819
|
+
configs+=("scripts/mcp/mcp-launcher.ps1")
|
|
820
|
+
|
|
821
|
+
# Docker configs (cross-platform)
|
|
822
|
+
configs+=(
|
|
823
|
+
"docker/mcp/registry.yaml"
|
|
824
|
+
"docker/mcp/catalog.json"
|
|
825
|
+
"docker/mcp/config.yaml"
|
|
826
|
+
"docker/mcp/tools.yaml"
|
|
827
|
+
"docker/mcp/catalogs/docker-mcp.yaml"
|
|
828
|
+
"docker/cli/daemon.json"
|
|
829
|
+
"docker/cli/features.json"
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
# Mac-only configs
|
|
833
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
834
|
+
configs+=(
|
|
835
|
+
"iterm/com.googlecode.iterm2.plist"
|
|
836
|
+
"apps/RectangleConfig.plist"
|
|
837
|
+
"apps/iStat Menus Settings.ismp7"
|
|
838
|
+
)
|
|
839
|
+
fi
|
|
840
|
+
|
|
841
|
+
# WezTerm (cross-platform terminal)
|
|
842
|
+
configs+=("wezterm/wezterm.lua")
|
|
843
|
+
|
|
844
|
+
# Ghostty (Mac/Linux terminal)
|
|
845
|
+
configs+=("ghostty/config")
|
|
846
|
+
|
|
847
|
+
# PowerShell profile (Windows only — debug helpers)
|
|
848
|
+
configs+=("shell/powershell/Microsoft.PowerShell_profile.ps1")
|
|
849
|
+
|
|
850
|
+
# Synergy works on both platforms
|
|
851
|
+
configs+=("apps/Synergy")
|
|
852
|
+
|
|
853
|
+
# Claude Code configs (cross-platform)
|
|
854
|
+
configs+=(
|
|
855
|
+
"claude/settings.json"
|
|
856
|
+
"claude/settings.local.json"
|
|
857
|
+
"claude/hooks.json"
|
|
858
|
+
"claude/statusline-command.sh"
|
|
859
|
+
"claude/CLAUDE.md"
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
# Claude Desktop (Mac only)
|
|
863
|
+
if [[ "$IS_MAC" == "true" ]]; then
|
|
864
|
+
configs+=("claude/desktop/claude_desktop_config.json")
|
|
865
|
+
fi
|
|
866
|
+
|
|
867
|
+
# WSL configs (Windows only)
|
|
868
|
+
if [[ "$IS_WINDOWS" == "true" ]]; then
|
|
869
|
+
configs+=(
|
|
870
|
+
"wsl/.wslconfig"
|
|
871
|
+
"wsl/wsl.conf"
|
|
872
|
+
)
|
|
873
|
+
fi
|
|
874
|
+
|
|
875
|
+
# Print array elements (zsh automatically handles this)
|
|
876
|
+
print -l $configs
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
# Get destination path for a config file
|
|
880
|
+
get_dest_path() {
|
|
881
|
+
get_config_dest "$1"
|
|
882
|
+
}
|