From a57a3a995d2a50ab1593f1de84ca5b394dc5c156 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:41:26 +0000 Subject: [PATCH 01/15] Initial plan From 1d95d4d09ae08de658739946202ebd6c4b1914d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:48:05 +0000 Subject: [PATCH 02/15] Convert all shell scripts to use Google/zx Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- README.md | 6 +- .../run_once_after_99_setup-complete.mjs | 3 + .../run_once_after_99_setup-complete.sh | 5 - .../run_once_before_00_setup-start.mjs | 3 + .../run_once_before_00_setup-start.sh | 5 - .../run_once_before_00a_install-zx.sh | 22 +++ .../run_once_before_01_install-homebrew.mjs | 25 +++ .../run_once_before_01_install-homebrew.sh | 25 --- ..._before_02_configure-touch-id-for-sudo.mjs | 24 +++ ...e_before_02_configure-touch-id-for-sudo.sh | 17 -- ...fter_00_install-brewfile-packages.mjs.tmpl | 21 ++ ...after_00_install-brewfile-packages.sh.tmpl | 17 -- home/.scripts/export-vscode-settings.mjs | 63 ++++++ home/.scripts/export-vscode-settings.sh | 64 ------ home/.scripts/import-vscode-settings.mjs | 183 ++++++++++++++++++ home/.scripts/import-vscode-settings.sh | 144 -------------- home/dot_Brewfile.tmpl | 1 + install.mjs | 34 ++++ install.sh | 33 ++-- 19 files changed, 401 insertions(+), 294 deletions(-) create mode 100755 home/.chezmoiscripts/run_once_after_99_setup-complete.mjs delete mode 100644 home/.chezmoiscripts/run_once_after_99_setup-complete.sh create mode 100755 home/.chezmoiscripts/run_once_before_00_setup-start.mjs delete mode 100644 home/.chezmoiscripts/run_once_before_00_setup-start.sh create mode 100755 home/.chezmoiscripts/run_once_before_00a_install-zx.sh create mode 100755 home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs delete mode 100644 home/.chezmoiscripts/run_once_before_01_install-homebrew.sh create mode 100755 home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.mjs delete mode 100644 home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.sh create mode 100755 home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl delete mode 100644 home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.sh.tmpl create mode 100755 home/.scripts/export-vscode-settings.mjs delete mode 100755 home/.scripts/export-vscode-settings.sh create mode 100755 home/.scripts/import-vscode-settings.mjs delete mode 100755 home/.scripts/import-vscode-settings.sh create mode 100755 install.mjs diff --git a/README.md b/README.md index 8e0c642..786a921 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ sh -c "$(curl -fsLS https://raw.githubusercontent.com/willmruzek/dotfiles/master chezmoi init --apply willmruzek ``` +**Note:** The installation scripts use [Google/zx](https://github.com/google/zx) for better shell scripting. Node.js will be installed automatically if not present. + ## What's Included - **Git configuration** (`.gitconfig`) @@ -74,13 +76,13 @@ Minimal helpers to move VS Code user settings between your Mac and this repo. Re - Export local → repo: ```bash - ./home/.scripts/export-vscode-settings.sh + ./home/.scripts/export-vscode-settings.mjs ``` - Import repo → local (preview first by default): ```bash - ./home/.scripts/import-vscode-settings.sh + ./home/.scripts/import-vscode-settings.mjs # flags: -n/--dry-run (preview only), -y/--yes (auto-approve) ``` diff --git a/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs b/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs new file mode 100755 index 0000000..1140b51 --- /dev/null +++ b/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env zx + +await $`echo "✨ Setup complete!"` diff --git a/home/.chezmoiscripts/run_once_after_99_setup-complete.sh b/home/.chezmoiscripts/run_once_after_99_setup-complete.sh deleted file mode 100644 index 42d5cae..0000000 --- a/home/.chezmoiscripts/run_once_after_99_setup-complete.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -set -e - -echo "✨ Setup complete!" diff --git a/home/.chezmoiscripts/run_once_before_00_setup-start.mjs b/home/.chezmoiscripts/run_once_before_00_setup-start.mjs new file mode 100755 index 0000000..68c1e8d --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_00_setup-start.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env zx + +await $`echo "🚀 Setting up your development environment..."` diff --git a/home/.chezmoiscripts/run_once_before_00_setup-start.sh b/home/.chezmoiscripts/run_once_before_00_setup-start.sh deleted file mode 100644 index 1760735..0000000 --- a/home/.chezmoiscripts/run_once_before_00_setup-start.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -set -e - -echo "🚀 Setting up your development environment..." diff --git a/home/.chezmoiscripts/run_once_before_00a_install-zx.sh b/home/.chezmoiscripts/run_once_before_00a_install-zx.sh new file mode 100755 index 0000000..b0d3027 --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_00a_install-zx.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +# Install zx before other scripts run +# This ensures all .mjs scripts can execute with zx + +if command -v npx &> /dev/null; then + # npx is available, zx will be available via npx zx + echo "✅ npx is available (zx will be available via npx)" +elif command -v brew &> /dev/null; then + # Install zx via Homebrew + if ! command -v zx &> /dev/null; then + echo "📦 Installing zx via Homebrew..." + brew install zx + else + echo "✅ zx is already installed" + fi +else + echo "⚠️ Neither npx nor brew available. zx scripts may not work." + echo " Please install Node.js (for npx) or Homebrew first." +fi diff --git a/home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs b/home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs new file mode 100755 index 0000000..f27f1c5 --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs @@ -0,0 +1,25 @@ +#!/usr/bin/env zx + +// Install Homebrew if not present +try { + await $`command -v brew` + console.log("✅ Homebrew is already installed") +} catch (error) { + console.log("📦 Homebrew not found.") + + const response = await question("Would you like to install Homebrew? (y/N): ") + + if (response.match(/^[Yy]$/)) { + console.log("📦 Installing Homebrew...") + await $`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` + + // Add Homebrew to PATH for Apple Silicon Macs + const arch = (await $`uname -m`).stdout.trim() + if (arch === "arm64") { + await $`echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile` + await $`eval "$(/opt/homebrew/bin/brew shellenv)"` + } + } else { + console.log("⏭️ Skipping Homebrew installation") + } +} diff --git a/home/.chezmoiscripts/run_once_before_01_install-homebrew.sh b/home/.chezmoiscripts/run_once_before_01_install-homebrew.sh deleted file mode 100644 index 7fb8605..0000000 --- a/home/.chezmoiscripts/run_once_before_01_install-homebrew.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e - -# Install Homebrew if not present -if ! command -v brew &> /dev/null; then - echo "📦 Homebrew not found." - read -p "Would you like to install Homebrew? (y/N): " -n 1 -r - echo - - if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "📦 Installing Homebrew..." - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - - # Add Homebrew to PATH for Apple Silicon Macs - if [[ $(uname -m) == "arm64" ]]; then - echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile - eval "$(/opt/homebrew/bin/brew shellenv)" - fi - else - echo "⏭️ Skipping Homebrew installation" - fi -else - echo "✅ Homebrew is already installed" -fi diff --git a/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.mjs b/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.mjs new file mode 100755 index 0000000..9948cbe --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.mjs @@ -0,0 +1,24 @@ +#!/usr/bin/env zx + +// Configure Touch ID for sudo +const sudoLocalPath = "/etc/pam.d/sudo_local" + +try { + await fs.access(sudoLocalPath) + + // File exists, check if pam_tid.so is already configured + const content = await fs.readFile(sudoLocalPath, 'utf8') + if (!content.includes("pam_tid.so")) { + console.log("🔐 Adding Touch ID authentication to existing sudo_local...") + await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` + console.log("✅ Touch ID for sudo configured") + } else { + console.log("✅ Touch ID for sudo already configured") + } +} catch (error) { + // File doesn't exist + console.log("🔐 Setting up Touch ID for sudo...") + await $`sudo cp /etc/pam.d/sudo_local.template ${sudoLocalPath}` + await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` + console.log("✅ Touch ID for sudo configured") +} diff --git a/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.sh b/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.sh deleted file mode 100644 index e4a386f..0000000 --- a/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -e - -# Configure Touch ID for sudo -if [ ! -f /etc/pam.d/sudo_local ]; then - echo "🔐 Setting up Touch ID for sudo..." - sudo cp /etc/pam.d/sudo_local.template /etc/pam.d/sudo_local - echo "auth sufficient pam_tid.so" | sudo tee -a /etc/pam.d/sudo_local - echo "✅ Touch ID for sudo configured" -elif ! grep -q "pam_tid.so" /etc/pam.d/sudo_local; then - echo "🔐 Adding Touch ID authentication to existing sudo_local..." - echo "auth sufficient pam_tid.so" | sudo tee -a /etc/pam.d/sudo_local - echo "✅ Touch ID for sudo configured" -else - echo "✅ Touch ID for sudo already configured" -fi diff --git a/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl b/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl new file mode 100755 index 0000000..a3a8e46 --- /dev/null +++ b/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl @@ -0,0 +1,21 @@ +#!/usr/bin/env zx + +// Re-run this script when the Brewfile changes +// Brewfile hash: {{ include "dot_Brewfile.tmpl" | sha256sum }} + +// Install packages from Brewfile if it exists (only if Homebrew is available) +try { + await $`command -v brew` + + try { + await fs.access(`${process.env.HOME}/.Brewfile`) + + console.log("📋 Installing packages from Brewfile...") + process.env.HOMEBREW_BUNDLE_FILE = `${process.env.HOME}/.Brewfile` + await $`brew bundle install` + } catch (error) { + console.log("⚠️ No Brewfile found, skipping package installation") + } +} catch (error) { + console.log("⚠️ Homebrew not available, skipping package installation") +} diff --git a/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.sh.tmpl deleted file mode 100644 index 719fbf9..0000000 --- a/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.sh.tmpl +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -e - -# Re-run this script when the Brewfile changes -# Brewfile hash: {{ include "dot_Brewfile.tmpl" | sha256sum }} - -# Install packages from Brewfile if it exists (only if Homebrew is available) -if command -v brew &> /dev/null && [ -f "$HOME/.Brewfile" ]; then - echo "📋 Installing packages from Brewfile..." - export HOMEBREW_BUNDLE_FILE="$HOME/.Brewfile" - brew bundle install -elif ! command -v brew &> /dev/null; then - echo "⚠️ Homebrew not available, skipping package installation" -else - echo "⚠️ No Brewfile found, skipping package installation" -fi diff --git a/home/.scripts/export-vscode-settings.mjs b/home/.scripts/export-vscode-settings.mjs new file mode 100755 index 0000000..94e7cc4 --- /dev/null +++ b/home/.scripts/export-vscode-settings.mjs @@ -0,0 +1,63 @@ +#!/usr/bin/env zx + +// export-vscode-settings.mjs - Export VS Code settings from local VS Code User directory into the repo +// +// Usage: +// ./export-vscode-settings.mjs +// +// Exports: +// - mcp.json +// - keybindings.json +// - settings.json +// - prompts/ (recursive) + +// Ensure git is available +try { + await $`command -v git` +} catch (error) { + console.error("Error: git not found. Install git.") + process.exit(1) +} + +// Ensure rsync is available +try { + await $`command -v rsync` +} catch (error) { + console.error("Error: rsync not found. Install rsync.") + process.exit(1) +} + +// Centralized include patterns for rsync +const includes = [ + '/mcp.json', + '/keybindings.json', + '/settings.json', + '/prompts/***' +] + +const includeArgs = includes.flatMap(p => ['--include', p]) + +const scriptDir = __dirname +const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout.trim() + +if (!repoRoot) { + console.error("Error: Could not determine destination path") + process.exit(1) +} + +const dest = `${repoRoot}/home/.vscode` +const src = `${process.env.HOME}/Library/Application Support/Code/User` + +// Skip gracefully if source directory does not exist +try { + await fs.access(src) +} catch (error) { + console.error(`Error: Source VS Code User directory not found: ${src}.`) + process.exit(1) +} + +await fs.mkdir(dest, { recursive: true }) + +await $`rsync -a --delete ${includeArgs} --exclude='*' ${src}/ ${dest}/` + +console.log("VS Code settings exported successfully.") diff --git a/home/.scripts/export-vscode-settings.sh b/home/.scripts/export-vscode-settings.sh deleted file mode 100755 index 7190aa1..0000000 --- a/home/.scripts/export-vscode-settings.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -# export-vscode-settings.sh - Export VS Code settings from local VS Code User directory into the repo -# -# Usage: -# ./export-vscode-settings.sh -# -# Exports: -# - mcp.json -# - keybindings.json -# - settings.json -# - prompts/ (recursive) - -# Ensure git is available -if ! command -v git >/dev/null 2>&1; then - echo "Error: git not found. Install git." >&2 - exit 1 -fi - -# Ensure rsync is available -if ! command -v rsync >/dev/null 2>&1; then - echo "Error: rsync not found. Install rsync." >&2 - exit 1 -fi - -# Centralized include patterns for rsync -INCLUDES=( - /mcp.json - /keybindings.json - /settings.json - /prompts/*** -) -INCLUDE_ARGS=() -for p in "${INCLUDES[@]}"; do - INCLUDE_ARGS+=("--include=$p") -done - -script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)" -repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null)" - -if [[ -z "${repo_root}" ]]; then - echo "Error: Could not determine destination path" >&2 - exit 1 -fi - -dest="$repo_root/home/.vscode" -src="$HOME/Library/Application Support/Code/User" - -# Skip gracefully if source directory does not exist -if [[ ! -d "$src" ]]; then - echo "Error: Source VS Code User directory not found: $src." >&2 - exit 1 -fi - -mkdir -p "$dest" - -rsync -a --delete \ - "${INCLUDE_ARGS[@]}" \ - --exclude='*' \ - "$src/" "$dest/" - -echo "VS Code settings exported successfully." diff --git a/home/.scripts/import-vscode-settings.mjs b/home/.scripts/import-vscode-settings.mjs new file mode 100755 index 0000000..38b5082 --- /dev/null +++ b/home/.scripts/import-vscode-settings.mjs @@ -0,0 +1,183 @@ +#!/usr/bin/env zx + +// import-vscode-settings.mjs - Import VS Code settings from repo into the local VS Code User directory +// +// Usage: +// ./import-vscode-settings.mjs [--dry-run|-n] [--yes|-y] +// +// Options: +// -n, --dry-run Preview diffs only; no backup or changes +// -y, --yes Auto-approve import (non-interactive) +// +// Imports: +// - mcp.json +// - keybindings.json +// - settings.json +// - prompts/ (recursive) + +// Ensure git is available +try { + await $`command -v git` +} catch (error) { + console.error("Error: git not found. Install git.") + process.exit(1) +} + +// Ensure rsync is available +try { + await $`command -v rsync` +} catch (error) { + console.error("Error: rsync not found. Install rsync.") + process.exit(1) +} + +// Parse flags +let autoYes = false +let dryRun = false + +for (const arg of process.argv.slice(3)) { + if (arg === '--yes' || arg === '-y') { + autoYes = true + } else if (arg === '--dry-run' || arg === '-n') { + dryRun = true + } +} + +// Centralized include patterns for rsync +const includes = [ + '/mcp.json', + '/keybindings.json', + '/settings.json', + '/prompts/***' +] + +const includeArgs = includes.flatMap(p => ['--include', p]) + +const scriptDir = __dirname +const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout.trim() + +// Ensure repo root is determined +if (!repoRoot) { + console.error("Error: Could not determine source path") + process.exit(1) +} + +const src = `${repoRoot}/home/.vscode` +const dest = `${process.env.HOME}/Library/Application Support/Code/User` + +// Ensure source directory exists +try { + await fs.access(src) +} catch (error) { + console.error(`Error: Source repo VS Code directory not found: ${src}.`) + process.exit(1) +} + +// Ensure VS Code User directory exists +try { + await fs.access(dest) +} catch (error) { + console.error(`Error: Destination VS Code User directory not found: ${dest}.`) + process.exit(1) +} + +// If dry-run, note it early +if (dryRun) { + console.error("Dry run: will preview changes only. No backup or import will be performed.") +} + +// Backup existing settings into $dest/.dotfiles_backup/ +if (!dryRun) { + const backupRoot = `${dest}/.dotfiles_backup` + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19).replace('T', '-') + const backupDir = `${backupRoot}/${timestamp}` + await fs.mkdir(backupDir, { recursive: true }) + + await $`rsync -a ${includeArgs} --exclude='/.dotfiles_backup/***' --exclude='*' ${dest}/ ${backupDir}/` + console.error(`Backup created at: ${backupDir}`) +} + +// Preview planned changes using git-style color diff and require approval +let changes = 0 +const jsonFiles = ['mcp', 'keybindings', 'settings'] + +console.error("Planned changes:") +console.error("") + +for (const f of jsonFiles) { + const srcFile = `${src}/${f}.json` + const destFile = `${dest}/${f}.json` + + try { + await fs.access(srcFile) + const hasFile = true + } catch (e) { + const hasFile = false + } + + try { + await fs.access(destFile) + const hasFile = true + } catch (e) { + const hasFile = false + } + + try { + await $`git --no-pager diff --no-index --quiet -- ${destFile} ${srcFile}` + } catch (error) { + changes = 1 + try { + await $`git --no-pager diff --no-index --color -- ${destFile} ${srcFile}` + } catch (e) { + // Diff returned non-zero, which is expected + } + } +} + +try { + await fs.access(`${src}/prompts`) + const hasSrcPrompts = true +} catch (e) { + const hasSrcPrompts = false +} + +try { + await fs.access(`${dest}/prompts`) + const hasDestPrompts = true +} catch (e) { + const hasDestPrompts = false +} + +try { + await $`git --no-pager diff --no-index --quiet -- ${dest}/prompts ${src}/prompts` +} catch (error) { + changes = 1 + try { + await $`git --no-pager diff --no-index --color -- ${dest}/prompts ${src}/prompts` + } catch (e) { + // Diff returned non-zero, which is expected + } +} + +if (changes === 0) { + console.error("No changes to import. Exiting.") + process.exit(0) +} + +// If dry-run, stop here after preview +if (dryRun) { + console.error("Dry run complete. Changes were not applied.") + process.exit(0) +} + +// Confirm import unless auto-approved +if (!autoYes) { + const answer = await question("Proceed with import? [y/N]: ") + if (!answer.match(/^[yY]([eE][sS])?$/)) { + console.error("Aborted.") + process.exit(1) + } +} + +// Import from repo into local VS Code User directory +await $`rsync -a ${includeArgs} --exclude='*' ${src}/ ${dest}/` diff --git a/home/.scripts/import-vscode-settings.sh b/home/.scripts/import-vscode-settings.sh deleted file mode 100755 index 484f107..0000000 --- a/home/.scripts/import-vscode-settings.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -# import-vscode-settings.sh - Import VS Code settings from repo into the local VS Code User directory -# -# Usage: -# ./import-vscode-settings.sh [--dry-run|-n] [--yes|-y] -# -# Options: -# -n, --dry-run Preview diffs only; no backup or changes -# -y, --yes Auto-approve import (non-interactive) -# -# Imports: -# - mcp.json -# - keybindings.json -# - settings.json -# - prompts/ (recursive) - -# Ensure git is available -if ! command -v git >/dev/null 2>&1; then - echo "Error: git not found. Install git." >&2 - exit 1 -fi - -# Ensure rsync is available -if ! command -v rsync >/dev/null 2>&1; then - echo "Error: rsync not found. Install rsync." >&2 - exit 1 -fi - -# Flags -AUTO_YES=0 -DRY_RUN=0 -for arg in "$@"; do - case "$arg" in - --yes|-y) AUTO_YES=1 ;; - --dry-run|-n) DRY_RUN=1 ;; - esac -done - -# Centralized include patterns for rsync -INCLUDES=( - /mcp.json - /keybindings.json - /settings.json - /prompts/*** -) -INCLUDE_ARGS=() -for p in "${INCLUDES[@]}"; do - INCLUDE_ARGS+=("--include=$p") -done - -script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)" -repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null)" - -# Ensure repo root is determined -if [[ -z "${repo_root}" ]]; then - echo "Error: Could not determine source path" >&2 - exit 1 -fi - -src="$repo_root/home/.vscode" -dest="$HOME/Library/Application Support/Code/User" - -# Ensure source directory exists -if [[ ! -d "$src" ]]; then - echo "Error: Source repo VS Code directory not found: $src." >&2 - exit 1 -fi - -# Ensure VS Code User directory exists -if [[ ! -d "$dest" ]]; then - echo "Error: Destination VS Code User directory not found: $dest." >&2 - exit 1 -fi - -# If dry-run, note it early -if [[ $DRY_RUN -eq 1 ]]; then - echo "Dry run: will preview changes only. No backup or import will be performed." >&2 -fi - -# Backup existing settings into $dest/.dotfiles_backup/ -if [[ $DRY_RUN -eq 0 ]]; then - backup_root="$dest/.dotfiles_backup" - backup_dir="$backup_root/$(date +%Y%m%d-%H%M%S)" - mkdir -p "$backup_dir" - rsync -a \ - "${INCLUDE_ARGS[@]}" \ - --exclude='/.dotfiles_backup/***' \ - --exclude='*' \ - "$dest/" "$backup_dir/" - echo "Backup created at: $backup_dir" >&2 -fi - -# Preview planned changes using git-style color diff and require approval -changes=0 -json_files=(mcp keybindings settings) - -echo "Planned changes:" >&2 -echo "" >&2 -for f in "${json_files[@]}"; do - src_f="$src/$f.json" - dest_f="$dest/$f.json" - if [[ -f "$src_f" || -f "$dest_f" ]]; then - if ! git --no-pager diff --no-index --quiet -- "$dest_f" "$src_f"; then - changes=1 - git --no-pager diff --no-index --color -- "$dest_f" "$src_f" || true - fi - fi -done - -if [[ -d "$src/prompts" || -d "$dest/prompts" ]]; then - if ! git --no-pager diff --no-index --quiet -- "$dest/prompts" "$src/prompts"; then - changes=1 - git --no-pager diff --no-index --color -- "$dest/prompts" "$src/prompts" || true - fi -fi - -if [[ $changes -eq 0 ]]; then - echo "No changes to import. Exiting." >&2 - exit 0 -fi - -# If dry-run, stop here after preview -if [[ $DRY_RUN -eq 1 ]]; then - echo "Dry run complete. Changes were not applied." >&2 - exit 0 -fi - -# Confirm import unless auto-approved -if [[ $AUTO_YES -eq 0 ]]; then - read -r -p "Proceed with import? [y/N]: " _ans &2; exit 1;; - esac -fi - -# Import from repo into local VS Code User directory -rsync -a \ - "${INCLUDE_ARGS[@]}" \ - --exclude='*' \ - "$src/" "$dest/" diff --git a/home/dot_Brewfile.tmpl b/home/dot_Brewfile.tmpl index 118ae6a..d35d9c4 100644 --- a/home/dot_Brewfile.tmpl +++ b/home/dot_Brewfile.tmpl @@ -3,6 +3,7 @@ brew "starship" brew "fzf" brew "gh" brew "tree" +brew "zx" {{- if eq .machineType "personal" }} brew "gpg" brew "mas" diff --git a/install.mjs b/install.mjs new file mode 100755 index 0000000..9296d12 --- /dev/null +++ b/install.mjs @@ -0,0 +1,34 @@ +#!/usr/bin/env zx + +// Bootstrap script to install chezmoi and apply dotfiles + +// Check if chezmoi is installed +let chezmoi +try { + await $`command -v chezmoi` + chezmoi = 'chezmoi' +} catch (error) { + const binDir = `${process.env.HOME}/.local/bin` + chezmoi = `${binDir}/chezmoi` + + try { + await $`command -v curl` + await $`sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b ${binDir}` + } catch (curlError) { + try { + await $`command -v wget` + await $`sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b ${binDir}` + } catch (wgetError) { + console.error("To install chezmoi, you must have curl or wget installed.") + process.exit(1) + } + } +} + +// Get script directory +const scriptDir = __dirname + +console.log(scriptDir) + +// Execute chezmoi init with apply +await $`${chezmoi} init --apply --source=${scriptDir}` diff --git a/install.sh b/install.sh index 48a248a..23350a6 100755 --- a/install.sh +++ b/install.sh @@ -2,24 +2,27 @@ set -e # -e: exit on error -if [ ! "$(command -v chezmoi)" ]; then - bin_dir="$HOME/.local/bin" - chezmoi="$bin_dir/chezmoi" - if [ "$(command -v curl)" ]; then - sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b "$bin_dir" - elif [ "$(command -v wget)" ]; then - sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b "$bin_dir" - else - echo "To install chezmoi, you must have curl or wget installed." >&2 - exit 1 +# Ensure Node.js/npm is available for npx zx +if ! command -v npx >/dev/null 2>&1; then + echo "📦 Node.js not found. Installing via Homebrew..." + + # Install Homebrew first if not present + if ! command -v brew >/dev/null 2>&1; then + echo "📦 Installing Homebrew..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + # Add Homebrew to PATH for Apple Silicon Macs + if [ "$(uname -m)" = "arm64" ]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + fi fi -else - chezmoi=chezmoi + + # Install Node.js + brew install node fi # POSIX way to get script's dir: https://stackoverflow.com/a/29834779/12156188 script_dir="$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)" -echo $script_dir -# exec: replace current process with chezmoi init -exec "$chezmoi" init --apply "--source=$script_dir" +# Run the zx version of the install script using npx +exec npx zx "$script_dir/install.mjs" From a864c7bcc25c85af175c926ff07fd8562be8b0f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:51:10 +0000 Subject: [PATCH 03/15] Fix zx scripts to use console.log and update chezmoi URL Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- home/.chezmoiscripts/run_once_after_99_setup-complete.mjs | 2 +- home/.chezmoiscripts/run_once_before_00_setup-start.mjs | 2 +- install.mjs | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs b/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs index 1140b51..6698477 100755 --- a/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs +++ b/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs @@ -1,3 +1,3 @@ #!/usr/bin/env zx -await $`echo "✨ Setup complete!"` +console.log("✨ Setup complete!") diff --git a/home/.chezmoiscripts/run_once_before_00_setup-start.mjs b/home/.chezmoiscripts/run_once_before_00_setup-start.mjs index 68c1e8d..8b05ff4 100755 --- a/home/.chezmoiscripts/run_once_before_00_setup-start.mjs +++ b/home/.chezmoiscripts/run_once_before_00_setup-start.mjs @@ -1,3 +1,3 @@ #!/usr/bin/env zx -await $`echo "🚀 Setting up your development environment..."` +console.log("🚀 Setting up your development environment...") diff --git a/install.mjs b/install.mjs index 9296d12..5d2ebf5 100755 --- a/install.mjs +++ b/install.mjs @@ -7,17 +7,21 @@ let chezmoi try { await $`command -v chezmoi` chezmoi = 'chezmoi' + console.log('✅ chezmoi is already installed') } catch (error) { const binDir = `${process.env.HOME}/.local/bin` chezmoi = `${binDir}/chezmoi` + console.log('📦 Installing chezmoi...') + await fs.mkdir(binDir, { recursive: true }) + try { await $`command -v curl` - await $`sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b ${binDir}` + await $`sh -c "$(curl -fsSL https://chezmoi.io/get)" -- -b ${binDir}` } catch (curlError) { try { await $`command -v wget` - await $`sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b ${binDir}` + await $`sh -c "$(wget -qO- https://chezmoi.io/get)" -- -b ${binDir}` } catch (wgetError) { console.error("To install chezmoi, you must have curl or wget installed.") process.exit(1) From f8b7862b717a4905ee46855dc514f54e5d976afd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:09:58 +0000 Subject: [PATCH 04/15] Fix: Use google/zx (lowercase) and install zx via npm only Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- README.md | 2 +- .../run_once_before_00a_install-zx.sh | 19 +++++++++---------- home/dot_Brewfile.tmpl | 1 - 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 786a921..9827253 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ sh -c "$(curl -fsLS https://raw.githubusercontent.com/willmruzek/dotfiles/master chezmoi init --apply willmruzek ``` -**Note:** The installation scripts use [Google/zx](https://github.com/google/zx) for better shell scripting. Node.js will be installed automatically if not present. +**Note:** The installation scripts use [google/zx](https://github.com/google/zx) for better shell scripting. Node.js will be installed automatically if not present. ## What's Included diff --git a/home/.chezmoiscripts/run_once_before_00a_install-zx.sh b/home/.chezmoiscripts/run_once_before_00a_install-zx.sh index b0d3027..43613f0 100755 --- a/home/.chezmoiscripts/run_once_before_00a_install-zx.sh +++ b/home/.chezmoiscripts/run_once_before_00a_install-zx.sh @@ -2,21 +2,20 @@ set -e -# Install zx before other scripts run +# Install zx before other scripts run via npm # This ensures all .mjs scripts can execute with zx if command -v npx &> /dev/null; then # npx is available, zx will be available via npx zx echo "✅ npx is available (zx will be available via npx)" -elif command -v brew &> /dev/null; then - # Install zx via Homebrew - if ! command -v zx &> /dev/null; then - echo "📦 Installing zx via Homebrew..." - brew install zx +else + echo "⚠️ npx not available. Installing zx globally via npm..." + if command -v npm &> /dev/null; then + npm install -g zx + echo "✅ zx installed via npm" else - echo "✅ zx is already installed" + echo "❌ npm not found. Node.js must be installed first." + echo " Please install Node.js before running this script." + exit 1 fi -else - echo "⚠️ Neither npx nor brew available. zx scripts may not work." - echo " Please install Node.js (for npx) or Homebrew first." fi diff --git a/home/dot_Brewfile.tmpl b/home/dot_Brewfile.tmpl index d35d9c4..118ae6a 100644 --- a/home/dot_Brewfile.tmpl +++ b/home/dot_Brewfile.tmpl @@ -3,7 +3,6 @@ brew "starship" brew "fzf" brew "gh" brew "tree" -brew "zx" {{- if eq .machineType "personal" }} brew "gpg" brew "mas" From 09c43fc81a30ef1bfa0a184ef3af528c359a1a23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:17:24 +0000 Subject: [PATCH 05/15] Remove install.mjs and keep install.sh as bash script Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- install.mjs | 38 -------------------------------------- install.sh | 24 ++++++++++++++++++++---- 2 files changed, 20 insertions(+), 42 deletions(-) delete mode 100755 install.mjs diff --git a/install.mjs b/install.mjs deleted file mode 100755 index 5d2ebf5..0000000 --- a/install.mjs +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env zx - -// Bootstrap script to install chezmoi and apply dotfiles - -// Check if chezmoi is installed -let chezmoi -try { - await $`command -v chezmoi` - chezmoi = 'chezmoi' - console.log('✅ chezmoi is already installed') -} catch (error) { - const binDir = `${process.env.HOME}/.local/bin` - chezmoi = `${binDir}/chezmoi` - - console.log('📦 Installing chezmoi...') - await fs.mkdir(binDir, { recursive: true }) - - try { - await $`command -v curl` - await $`sh -c "$(curl -fsSL https://chezmoi.io/get)" -- -b ${binDir}` - } catch (curlError) { - try { - await $`command -v wget` - await $`sh -c "$(wget -qO- https://chezmoi.io/get)" -- -b ${binDir}` - } catch (wgetError) { - console.error("To install chezmoi, you must have curl or wget installed.") - process.exit(1) - } - } -} - -// Get script directory -const scriptDir = __dirname - -console.log(scriptDir) - -// Execute chezmoi init with apply -await $`${chezmoi} init --apply --source=${scriptDir}` diff --git a/install.sh b/install.sh index 23350a6..d65a8f4 100755 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ set -e # -e: exit on error -# Ensure Node.js/npm is available for npx zx +# Ensure Node.js/npm is available for zx scripts that chezmoi will run if ! command -v npx >/dev/null 2>&1; then echo "📦 Node.js not found. Installing via Homebrew..." @@ -17,12 +17,28 @@ if ! command -v npx >/dev/null 2>&1; then fi fi - # Install Node.js + # Install Node.js LTS brew install node fi +# Install chezmoi if not present +if [ ! "$(command -v chezmoi)" ]; then + bin_dir="$HOME/.local/bin" + chezmoi="$bin_dir/chezmoi" + if [ "$(command -v curl)" ]; then + sh -c "$(curl -fsSL https://chezmoi.io/get)" -- -b "$bin_dir" + elif [ "$(command -v wget)" ]; then + sh -c "$(wget -qO- https://chezmoi.io/get)" -- -b "$bin_dir" + else + echo "To install chezmoi, you must have curl or wget installed." >&2 + exit 1 + fi +else + chezmoi=chezmoi +fi + # POSIX way to get script's dir: https://stackoverflow.com/a/29834779/12156188 script_dir="$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)" -# Run the zx version of the install script using npx -exec npx zx "$script_dir/install.mjs" +# exec: replace current process with chezmoi init +exec "$chezmoi" init --apply "--source=$script_dir" From 0fb220f514be995a440b6d921494a2377187c874 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:26:00 +0000 Subject: [PATCH 06/15] Revert chezmoi URLs to git.io/chezmoi Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index d65a8f4..92bec51 100755 --- a/install.sh +++ b/install.sh @@ -26,9 +26,9 @@ if [ ! "$(command -v chezmoi)" ]; then bin_dir="$HOME/.local/bin" chezmoi="$bin_dir/chezmoi" if [ "$(command -v curl)" ]; then - sh -c "$(curl -fsSL https://chezmoi.io/get)" -- -b "$bin_dir" + sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b "$bin_dir" elif [ "$(command -v wget)" ]; then - sh -c "$(wget -qO- https://chezmoi.io/get)" -- -b "$bin_dir" + sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b "$bin_dir" else echo "To install chezmoi, you must have curl or wget installed." >&2 exit 1 From 4e07a6402f1aaa3b47bcb65b61655e6c652fc7d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:27:00 +0000 Subject: [PATCH 07/15] Update chezmoi URLs to get.chezmoi.io Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 92bec51..e2659a8 100755 --- a/install.sh +++ b/install.sh @@ -26,9 +26,9 @@ if [ ! "$(command -v chezmoi)" ]; then bin_dir="$HOME/.local/bin" chezmoi="$bin_dir/chezmoi" if [ "$(command -v curl)" ]; then - sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b "$bin_dir" + sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$bin_dir" elif [ "$(command -v wget)" ]; then - sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b "$bin_dir" + sh -c "$(wget -qO- get.chezmoi.io)" -- -b "$bin_dir" else echo "To install chezmoi, you must have curl or wget installed." >&2 exit 1 From 69822297500fcd41c0c04703374d08469f1c7536 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:32:03 +0000 Subject: [PATCH 08/15] Move Homebrew and Node.js installation to chezmoi scripts Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- ...un_once_before_00_install-homebrew-node.sh | 35 +++++++++++++++++++ .../run_once_before_01_install-homebrew.mjs | 25 ------------- ...mjs => run_once_before_01_setup-start.mjs} | 0 install.sh | 19 ---------- 4 files changed, 35 insertions(+), 44 deletions(-) create mode 100755 home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh delete mode 100755 home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs rename home/.chezmoiscripts/{run_once_before_00_setup-start.mjs => run_once_before_01_setup-start.mjs} (100%) diff --git a/home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh b/home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh new file mode 100755 index 0000000..3aca2bb --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +# Install Homebrew if not present +if ! command -v brew &> /dev/null; then + echo "📦 Homebrew not found." + read -p "Would you like to install Homebrew? (y/N): " -n 1 -r + echo + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "📦 Installing Homebrew..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + # Add Homebrew to PATH for Apple Silicon Macs + if [[ $(uname -m) == "arm64" ]]; then + echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile + eval "$(/opt/homebrew/bin/brew shellenv)" + fi + else + echo "⏭️ Skipping Homebrew installation" + exit 0 + fi +else + echo "✅ Homebrew is already installed" +fi + +# Install Node.js LTS if not present +if ! command -v node &> /dev/null; then + echo "📦 Installing Node.js LTS..." + brew install node + echo "✅ Node.js installed" +else + echo "✅ Node.js is already installed" +fi diff --git a/home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs b/home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs deleted file mode 100755 index f27f1c5..0000000 --- a/home/.chezmoiscripts/run_once_before_01_install-homebrew.mjs +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env zx - -// Install Homebrew if not present -try { - await $`command -v brew` - console.log("✅ Homebrew is already installed") -} catch (error) { - console.log("📦 Homebrew not found.") - - const response = await question("Would you like to install Homebrew? (y/N): ") - - if (response.match(/^[Yy]$/)) { - console.log("📦 Installing Homebrew...") - await $`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` - - // Add Homebrew to PATH for Apple Silicon Macs - const arch = (await $`uname -m`).stdout.trim() - if (arch === "arm64") { - await $`echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile` - await $`eval "$(/opt/homebrew/bin/brew shellenv)"` - } - } else { - console.log("⏭️ Skipping Homebrew installation") - } -} diff --git a/home/.chezmoiscripts/run_once_before_00_setup-start.mjs b/home/.chezmoiscripts/run_once_before_01_setup-start.mjs similarity index 100% rename from home/.chezmoiscripts/run_once_before_00_setup-start.mjs rename to home/.chezmoiscripts/run_once_before_01_setup-start.mjs diff --git a/install.sh b/install.sh index e2659a8..985d226 100755 --- a/install.sh +++ b/install.sh @@ -2,25 +2,6 @@ set -e # -e: exit on error -# Ensure Node.js/npm is available for zx scripts that chezmoi will run -if ! command -v npx >/dev/null 2>&1; then - echo "📦 Node.js not found. Installing via Homebrew..." - - # Install Homebrew first if not present - if ! command -v brew >/dev/null 2>&1; then - echo "📦 Installing Homebrew..." - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - - # Add Homebrew to PATH for Apple Silicon Macs - if [ "$(uname -m)" = "arm64" ]; then - eval "$(/opt/homebrew/bin/brew shellenv)" - fi - fi - - # Install Node.js LTS - brew install node -fi - # Install chezmoi if not present if [ ! "$(command -v chezmoi)" ]; then bin_dir="$HOME/.local/bin" From 0b25e5d8eabef8d53d1520a381df91914b4986c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:46:45 +0000 Subject: [PATCH 09/15] Install Node.js via brew bundle and convert brew bundle to bash Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- ...=> run_once_before_00_install-homebrew.sh} | 10 +-------- ...fore_00a_install-brewfile-packages.sh.tmpl | 17 +++++++++++++++ ...zx.sh => run_once_before_10_install-zx.sh} | 0 ...mjs => run_once_before_11_setup-start.mjs} | 0 ...before_12_configure-touch-id-for-sudo.mjs} | 0 ...fter_00_install-brewfile-packages.mjs.tmpl | 21 ------------------- home/dot_Brewfile.tmpl | 1 + 7 files changed, 19 insertions(+), 30 deletions(-) rename home/.chezmoiscripts/{run_once_before_00_install-homebrew-node.sh => run_once_before_00_install-homebrew.sh} (77%) create mode 100755 home/.chezmoiscripts/run_once_before_00a_install-brewfile-packages.sh.tmpl rename home/.chezmoiscripts/{run_once_before_00a_install-zx.sh => run_once_before_10_install-zx.sh} (100%) rename home/.chezmoiscripts/{run_once_before_01_setup-start.mjs => run_once_before_11_setup-start.mjs} (100%) rename home/.chezmoiscripts/{run_once_before_02_configure-touch-id-for-sudo.mjs => run_once_before_12_configure-touch-id-for-sudo.mjs} (100%) delete mode 100755 home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl diff --git a/home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh b/home/.chezmoiscripts/run_once_before_00_install-homebrew.sh similarity index 77% rename from home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh rename to home/.chezmoiscripts/run_once_before_00_install-homebrew.sh index 3aca2bb..ec72abf 100755 --- a/home/.chezmoiscripts/run_once_before_00_install-homebrew-node.sh +++ b/home/.chezmoiscripts/run_once_before_00_install-homebrew.sh @@ -17,6 +17,7 @@ if ! command -v brew &> /dev/null; then echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile eval "$(/opt/homebrew/bin/brew shellenv)" fi + echo "✅ Homebrew installed" else echo "⏭️ Skipping Homebrew installation" exit 0 @@ -24,12 +25,3 @@ if ! command -v brew &> /dev/null; then else echo "✅ Homebrew is already installed" fi - -# Install Node.js LTS if not present -if ! command -v node &> /dev/null; then - echo "📦 Installing Node.js LTS..." - brew install node - echo "✅ Node.js installed" -else - echo "✅ Node.js is already installed" -fi diff --git a/home/.chezmoiscripts/run_once_before_00a_install-brewfile-packages.sh.tmpl b/home/.chezmoiscripts/run_once_before_00a_install-brewfile-packages.sh.tmpl new file mode 100755 index 0000000..719fbf9 --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_00a_install-brewfile-packages.sh.tmpl @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +# Re-run this script when the Brewfile changes +# Brewfile hash: {{ include "dot_Brewfile.tmpl" | sha256sum }} + +# Install packages from Brewfile if it exists (only if Homebrew is available) +if command -v brew &> /dev/null && [ -f "$HOME/.Brewfile" ]; then + echo "📋 Installing packages from Brewfile..." + export HOMEBREW_BUNDLE_FILE="$HOME/.Brewfile" + brew bundle install +elif ! command -v brew &> /dev/null; then + echo "⚠️ Homebrew not available, skipping package installation" +else + echo "⚠️ No Brewfile found, skipping package installation" +fi diff --git a/home/.chezmoiscripts/run_once_before_00a_install-zx.sh b/home/.chezmoiscripts/run_once_before_10_install-zx.sh similarity index 100% rename from home/.chezmoiscripts/run_once_before_00a_install-zx.sh rename to home/.chezmoiscripts/run_once_before_10_install-zx.sh diff --git a/home/.chezmoiscripts/run_once_before_01_setup-start.mjs b/home/.chezmoiscripts/run_once_before_11_setup-start.mjs similarity index 100% rename from home/.chezmoiscripts/run_once_before_01_setup-start.mjs rename to home/.chezmoiscripts/run_once_before_11_setup-start.mjs diff --git a/home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.mjs b/home/.chezmoiscripts/run_once_before_12_configure-touch-id-for-sudo.mjs similarity index 100% rename from home/.chezmoiscripts/run_once_before_02_configure-touch-id-for-sudo.mjs rename to home/.chezmoiscripts/run_once_before_12_configure-touch-id-for-sudo.mjs diff --git a/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl b/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl deleted file mode 100755 index a3a8e46..0000000 --- a/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.mjs.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env zx - -// Re-run this script when the Brewfile changes -// Brewfile hash: {{ include "dot_Brewfile.tmpl" | sha256sum }} - -// Install packages from Brewfile if it exists (only if Homebrew is available) -try { - await $`command -v brew` - - try { - await fs.access(`${process.env.HOME}/.Brewfile`) - - console.log("📋 Installing packages from Brewfile...") - process.env.HOMEBREW_BUNDLE_FILE = `${process.env.HOME}/.Brewfile` - await $`brew bundle install` - } catch (error) { - console.log("⚠️ No Brewfile found, skipping package installation") - } -} catch (error) { - console.log("⚠️ Homebrew not available, skipping package installation") -} diff --git a/home/dot_Brewfile.tmpl b/home/dot_Brewfile.tmpl index 118ae6a..faad21b 100644 --- a/home/dot_Brewfile.tmpl +++ b/home/dot_Brewfile.tmpl @@ -3,6 +3,7 @@ brew "starship" brew "fzf" brew "gh" brew "tree" +brew "node" {{- if eq .machineType "personal" }} brew "gpg" brew "mas" From affce2ca97ef5fa45bb4332650fa996f340c6874 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:22:53 +0000 Subject: [PATCH 10/15] Use Node.js v24 (LTS) instead of latest current Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- home/dot_Brewfile.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/dot_Brewfile.tmpl b/home/dot_Brewfile.tmpl index faad21b..7b66457 100644 --- a/home/dot_Brewfile.tmpl +++ b/home/dot_Brewfile.tmpl @@ -3,7 +3,7 @@ brew "starship" brew "fzf" brew "gh" brew "tree" -brew "node" +brew "node@24" {{- if eq .machineType "personal" }} brew "gpg" brew "mas" From 3a000bb2eeb16fb1a3a2620e20c985c819232809 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:35:39 +0000 Subject: [PATCH 11/15] Rename chezmoi scripts to sequential numbering without letters Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- ....tmpl => run_once_before_01_install-brewfile-packages.sh.tmpl} | 0 ...e_before_10_install-zx.sh => run_once_before_02_install-zx.sh} | 0 ...fore_11_setup-start.mjs => run_once_before_03_setup-start.mjs} | 0 ...udo.mjs => run_once_before_04_configure-touch-id-for-sudo.mjs} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename home/.chezmoiscripts/{run_once_before_00a_install-brewfile-packages.sh.tmpl => run_once_before_01_install-brewfile-packages.sh.tmpl} (100%) rename home/.chezmoiscripts/{run_once_before_10_install-zx.sh => run_once_before_02_install-zx.sh} (100%) rename home/.chezmoiscripts/{run_once_before_11_setup-start.mjs => run_once_before_03_setup-start.mjs} (100%) rename home/.chezmoiscripts/{run_once_before_12_configure-touch-id-for-sudo.mjs => run_once_before_04_configure-touch-id-for-sudo.mjs} (100%) diff --git a/home/.chezmoiscripts/run_once_before_00a_install-brewfile-packages.sh.tmpl b/home/.chezmoiscripts/run_once_before_01_install-brewfile-packages.sh.tmpl similarity index 100% rename from home/.chezmoiscripts/run_once_before_00a_install-brewfile-packages.sh.tmpl rename to home/.chezmoiscripts/run_once_before_01_install-brewfile-packages.sh.tmpl diff --git a/home/.chezmoiscripts/run_once_before_10_install-zx.sh b/home/.chezmoiscripts/run_once_before_02_install-zx.sh similarity index 100% rename from home/.chezmoiscripts/run_once_before_10_install-zx.sh rename to home/.chezmoiscripts/run_once_before_02_install-zx.sh diff --git a/home/.chezmoiscripts/run_once_before_11_setup-start.mjs b/home/.chezmoiscripts/run_once_before_03_setup-start.mjs similarity index 100% rename from home/.chezmoiscripts/run_once_before_11_setup-start.mjs rename to home/.chezmoiscripts/run_once_before_03_setup-start.mjs diff --git a/home/.chezmoiscripts/run_once_before_12_configure-touch-id-for-sudo.mjs b/home/.chezmoiscripts/run_once_before_04_configure-touch-id-for-sudo.mjs similarity index 100% rename from home/.chezmoiscripts/run_once_before_12_configure-touch-id-for-sudo.mjs rename to home/.chezmoiscripts/run_once_before_04_configure-touch-id-for-sudo.mjs From fff1bd7c780ca0dad5af8801b9da8ecf180a85f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:51:45 +0000 Subject: [PATCH 12/15] Convert setup-start to bash and move brewfile to onchange after Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- .../run_once_before_00_setup-start.sh | 5 +++++ ...w.sh => run_once_before_01_install-homebrew.sh} | 0 ...run_once_before_02_install-brewfile-packages.sh | 14 ++++++++++++++ ...tall-zx.sh => run_once_before_03_install-zx.sh} | 0 .../run_once_before_03_setup-start.mjs | 3 --- ...once_before_05_configure-touch-id-for-sudo.mjs} | 0 ...nge_after_00_install-brewfile-packages.sh.tmpl} | 0 7 files changed, 19 insertions(+), 3 deletions(-) create mode 100755 home/.chezmoiscripts/run_once_before_00_setup-start.sh rename home/.chezmoiscripts/{run_once_before_00_install-homebrew.sh => run_once_before_01_install-homebrew.sh} (100%) create mode 100755 home/.chezmoiscripts/run_once_before_02_install-brewfile-packages.sh rename home/.chezmoiscripts/{run_once_before_02_install-zx.sh => run_once_before_03_install-zx.sh} (100%) delete mode 100755 home/.chezmoiscripts/run_once_before_03_setup-start.mjs rename home/.chezmoiscripts/{run_once_before_04_configure-touch-id-for-sudo.mjs => run_once_before_05_configure-touch-id-for-sudo.mjs} (100%) rename home/.chezmoiscripts/{run_once_before_01_install-brewfile-packages.sh.tmpl => run_onchange_after_00_install-brewfile-packages.sh.tmpl} (100%) diff --git a/home/.chezmoiscripts/run_once_before_00_setup-start.sh b/home/.chezmoiscripts/run_once_before_00_setup-start.sh new file mode 100755 index 0000000..1760735 --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_00_setup-start.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +echo "🚀 Setting up your development environment..." diff --git a/home/.chezmoiscripts/run_once_before_00_install-homebrew.sh b/home/.chezmoiscripts/run_once_before_01_install-homebrew.sh similarity index 100% rename from home/.chezmoiscripts/run_once_before_00_install-homebrew.sh rename to home/.chezmoiscripts/run_once_before_01_install-homebrew.sh diff --git a/home/.chezmoiscripts/run_once_before_02_install-brewfile-packages.sh b/home/.chezmoiscripts/run_once_before_02_install-brewfile-packages.sh new file mode 100755 index 0000000..cf99291 --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_02_install-brewfile-packages.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +# Install packages from Brewfile if it exists (only if Homebrew is available) +if command -v brew &> /dev/null && [ -f "$HOME/.Brewfile" ]; then + echo "📋 Installing packages from Brewfile..." + export HOMEBREW_BUNDLE_FILE="$HOME/.Brewfile" + brew bundle install +elif ! command -v brew &> /dev/null; then + echo "⚠️ Homebrew not available, skipping package installation" +else + echo "⚠️ No Brewfile found, skipping package installation" +fi diff --git a/home/.chezmoiscripts/run_once_before_02_install-zx.sh b/home/.chezmoiscripts/run_once_before_03_install-zx.sh similarity index 100% rename from home/.chezmoiscripts/run_once_before_02_install-zx.sh rename to home/.chezmoiscripts/run_once_before_03_install-zx.sh diff --git a/home/.chezmoiscripts/run_once_before_03_setup-start.mjs b/home/.chezmoiscripts/run_once_before_03_setup-start.mjs deleted file mode 100755 index 8b05ff4..0000000 --- a/home/.chezmoiscripts/run_once_before_03_setup-start.mjs +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env zx - -console.log("🚀 Setting up your development environment...") diff --git a/home/.chezmoiscripts/run_once_before_04_configure-touch-id-for-sudo.mjs b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.mjs similarity index 100% rename from home/.chezmoiscripts/run_once_before_04_configure-touch-id-for-sudo.mjs rename to home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.mjs diff --git a/home/.chezmoiscripts/run_once_before_01_install-brewfile-packages.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.sh.tmpl similarity index 100% rename from home/.chezmoiscripts/run_once_before_01_install-brewfile-packages.sh.tmpl rename to home/.chezmoiscripts/run_onchange_after_00_install-brewfile-packages.sh.tmpl From d407b41e85554ae22fd1e79064e1bb39739055f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 18:45:04 +0000 Subject: [PATCH 13/15] Convert all .mjs scripts to TypeScript and use $.echo Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- .gitignore | 2 ++ .../run_once_after_99_setup-complete.mjs | 3 -- .../run_once_after_99_setup-complete.ts | 3 ++ ...once_before_04_install-npm-dependencies.sh | 24 ++++++++++++++++ ..._before_05_configure-touch-id-for-sudo.ts} | 10 +++---- ...settings.mjs => export-vscode-settings.ts} | 14 +++++----- ...settings.mjs => import-vscode-settings.ts} | 28 +++++++++---------- package.json | 21 ++++++++++++++ tsconfig.json | 20 +++++++++++++ 9 files changed, 96 insertions(+), 29 deletions(-) delete mode 100755 home/.chezmoiscripts/run_once_after_99_setup-complete.mjs create mode 100755 home/.chezmoiscripts/run_once_after_99_setup-complete.ts create mode 100755 home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh rename home/.chezmoiscripts/{run_once_before_05_configure-touch-id-for-sudo.mjs => run_once_before_05_configure-touch-id-for-sudo.ts} (66%) rename home/.scripts/{export-vscode-settings.mjs => export-vscode-settings.ts} (70%) rename home/.scripts/{import-vscode-settings.mjs => import-vscode-settings.ts} (81%) create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index e43b0f9..e617da4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .DS_Store +node_modules/ +package-lock.json diff --git a/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs b/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs deleted file mode 100755 index 6698477..0000000 --- a/home/.chezmoiscripts/run_once_after_99_setup-complete.mjs +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env zx - -console.log("✨ Setup complete!") diff --git a/home/.chezmoiscripts/run_once_after_99_setup-complete.ts b/home/.chezmoiscripts/run_once_after_99_setup-complete.ts new file mode 100755 index 0000000..23dba62 --- /dev/null +++ b/home/.chezmoiscripts/run_once_after_99_setup-complete.ts @@ -0,0 +1,3 @@ +#!/usr/bin/env zx + +$.echo("✨ Setup complete!") diff --git a/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh b/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh new file mode 100755 index 0000000..d368163 --- /dev/null +++ b/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +# Get the repository root (where package.json lives) +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" + +# Check if npm is available +if ! command -v npm &> /dev/null; then + echo "⚠️ npm not available, skipping dependency installation" + exit 0 +fi + +# Check if package.json exists +if [ ! -f "$REPO_ROOT/package.json" ]; then + echo "⚠️ package.json not found, skipping dependency installation" + exit 0 +fi + +echo "📦 Installing npm dependencies..." +cd "$REPO_ROOT" +npm install + +echo "✅ npm dependencies installed" diff --git a/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.mjs b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts similarity index 66% rename from home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.mjs rename to home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts index 9948cbe..b5b008b 100755 --- a/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.mjs +++ b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts @@ -9,16 +9,16 @@ try { // File exists, check if pam_tid.so is already configured const content = await fs.readFile(sudoLocalPath, 'utf8') if (!content.includes("pam_tid.so")) { - console.log("🔐 Adding Touch ID authentication to existing sudo_local...") + $.echo("🔐 Adding Touch ID authentication to existing sudo_local...") await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` - console.log("✅ Touch ID for sudo configured") + $.echo("✅ Touch ID for sudo configured") } else { - console.log("✅ Touch ID for sudo already configured") + $.echo("✅ Touch ID for sudo already configured") } } catch (error) { // File doesn't exist - console.log("🔐 Setting up Touch ID for sudo...") + $.echo("🔐 Setting up Touch ID for sudo...") await $`sudo cp /etc/pam.d/sudo_local.template ${sudoLocalPath}` await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` - console.log("✅ Touch ID for sudo configured") + $.echo("✅ Touch ID for sudo configured") } diff --git a/home/.scripts/export-vscode-settings.mjs b/home/.scripts/export-vscode-settings.ts similarity index 70% rename from home/.scripts/export-vscode-settings.mjs rename to home/.scripts/export-vscode-settings.ts index 94e7cc4..e5ceda0 100755 --- a/home/.scripts/export-vscode-settings.mjs +++ b/home/.scripts/export-vscode-settings.ts @@ -1,9 +1,9 @@ #!/usr/bin/env zx -// export-vscode-settings.mjs - Export VS Code settings from local VS Code User directory into the repo +// export-vscode-settings.ts - Export VS Code settings from local VS Code User directory into the repo // // Usage: -// ./export-vscode-settings.mjs +// ./export-vscode-settings.ts // // Exports: // - mcp.json @@ -15,7 +15,7 @@ try { await $`command -v git` } catch (error) { - console.error("Error: git not found. Install git.") + $.echo("Error: git not found. Install git.") process.exit(1) } @@ -23,7 +23,7 @@ try { try { await $`command -v rsync` } catch (error) { - console.error("Error: rsync not found. Install rsync.") + $.echo("Error: rsync not found. Install rsync.") process.exit(1) } @@ -41,7 +41,7 @@ const scriptDir = __dirname const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout.trim() if (!repoRoot) { - console.error("Error: Could not determine destination path") + $.echo("Error: Could not determine destination path") process.exit(1) } @@ -52,7 +52,7 @@ const src = `${process.env.HOME}/Library/Application Support/Code/User` try { await fs.access(src) } catch (error) { - console.error(`Error: Source VS Code User directory not found: ${src}.`) + $.echo(`Error: Source VS Code User directory not found: ${src}.`) process.exit(1) } @@ -60,4 +60,4 @@ await fs.mkdir(dest, { recursive: true }) await $`rsync -a --delete ${includeArgs} --exclude='*' ${src}/ ${dest}/` -console.log("VS Code settings exported successfully.") +$.echo("VS Code settings exported successfully.") diff --git a/home/.scripts/import-vscode-settings.mjs b/home/.scripts/import-vscode-settings.ts similarity index 81% rename from home/.scripts/import-vscode-settings.mjs rename to home/.scripts/import-vscode-settings.ts index 38b5082..a127374 100755 --- a/home/.scripts/import-vscode-settings.mjs +++ b/home/.scripts/import-vscode-settings.ts @@ -1,9 +1,9 @@ #!/usr/bin/env zx -// import-vscode-settings.mjs - Import VS Code settings from repo into the local VS Code User directory +// import-vscode-settings.ts - Import VS Code settings from repo into the local VS Code User directory // // Usage: -// ./import-vscode-settings.mjs [--dry-run|-n] [--yes|-y] +// ./import-vscode-settings.ts [--dry-run|-n] [--yes|-y] // // Options: // -n, --dry-run Preview diffs only; no backup or changes @@ -19,7 +19,7 @@ try { await $`command -v git` } catch (error) { - console.error("Error: git not found. Install git.") + $.echo("Error: git not found. Install git.") process.exit(1) } @@ -27,7 +27,7 @@ try { try { await $`command -v rsync` } catch (error) { - console.error("Error: rsync not found. Install rsync.") + $.echo("Error: rsync not found. Install rsync.") process.exit(1) } @@ -58,7 +58,7 @@ const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout // Ensure repo root is determined if (!repoRoot) { - console.error("Error: Could not determine source path") + $.echo("Error: Could not determine source path") process.exit(1) } @@ -69,7 +69,7 @@ const dest = `${process.env.HOME}/Library/Application Support/Code/User` try { await fs.access(src) } catch (error) { - console.error(`Error: Source repo VS Code directory not found: ${src}.`) + $.echo(`Error: Source repo VS Code directory not found: ${src}.`) process.exit(1) } @@ -77,13 +77,13 @@ try { try { await fs.access(dest) } catch (error) { - console.error(`Error: Destination VS Code User directory not found: ${dest}.`) + $.echo(`Error: Destination VS Code User directory not found: ${dest}.`) process.exit(1) } // If dry-run, note it early if (dryRun) { - console.error("Dry run: will preview changes only. No backup or import will be performed.") + $.echo("Dry run: will preview changes only. No backup or import will be performed.") } // Backup existing settings into $dest/.dotfiles_backup/ @@ -94,15 +94,15 @@ if (!dryRun) { await fs.mkdir(backupDir, { recursive: true }) await $`rsync -a ${includeArgs} --exclude='/.dotfiles_backup/***' --exclude='*' ${dest}/ ${backupDir}/` - console.error(`Backup created at: ${backupDir}`) + $.echo(`Backup created at: ${backupDir}`) } // Preview planned changes using git-style color diff and require approval let changes = 0 const jsonFiles = ['mcp', 'keybindings', 'settings'] -console.error("Planned changes:") -console.error("") +$.echo("Planned changes:") +$.echo("") for (const f of jsonFiles) { const srcFile = `${src}/${f}.json` @@ -160,13 +160,13 @@ try { } if (changes === 0) { - console.error("No changes to import. Exiting.") + $.echo("No changes to import. Exiting.") process.exit(0) } // If dry-run, stop here after preview if (dryRun) { - console.error("Dry run complete. Changes were not applied.") + $.echo("Dry run complete. Changes were not applied.") process.exit(0) } @@ -174,7 +174,7 @@ if (dryRun) { if (!autoYes) { const answer = await question("Proceed with import? [y/N]: ") if (!answer.match(/^[yY]([eE][sS])?$/)) { - console.error("Aborted.") + $.echo("Aborted.") process.exit(1) } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..53fff45 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "dotfiles", + "version": "1.0.0", + "description": "Personal dotfiles managed with chezmoi and zx", + "type": "module", + "scripts": { + "export-vscode": "zx home/.scripts/export-vscode-settings.ts", + "import-vscode": "zx home/.scripts/import-vscode-settings.ts" + }, + "dependencies": { + "neverthrow": "^8.1.1", + "zx": "^8.2.4" + }, + "devDependencies": { + "@types/node": "^22.10.1", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=24.0.0" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fb31268 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "noEmit": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "types": ["node"] + }, + "include": [ + "home/.scripts/**/*.ts", + "home/.chezmoiscripts/**/*.ts" + ] +} From 67a60c43499fb953d000cc146821326cca7126b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 19:47:39 +0000 Subject: [PATCH 14/15] Use echo template literal and check in package-lock.json Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- .gitignore | 1 - .../run_once_after_99_setup-complete.ts | 2 +- ...e_before_05_configure-touch-id-for-sudo.ts | 10 ++++---- home/.scripts/export-vscode-settings.ts | 10 ++++---- home/.scripts/import-vscode-settings.ts | 24 +++++++++---------- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index e617da4..646ac51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .DS_Store node_modules/ -package-lock.json diff --git a/home/.chezmoiscripts/run_once_after_99_setup-complete.ts b/home/.chezmoiscripts/run_once_after_99_setup-complete.ts index 23dba62..6f64d8d 100755 --- a/home/.chezmoiscripts/run_once_after_99_setup-complete.ts +++ b/home/.chezmoiscripts/run_once_after_99_setup-complete.ts @@ -1,3 +1,3 @@ #!/usr/bin/env zx -$.echo("✨ Setup complete!") +echo`✨ Setup complete!` diff --git a/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts index b5b008b..0710a39 100755 --- a/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts +++ b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts @@ -9,16 +9,16 @@ try { // File exists, check if pam_tid.so is already configured const content = await fs.readFile(sudoLocalPath, 'utf8') if (!content.includes("pam_tid.so")) { - $.echo("🔐 Adding Touch ID authentication to existing sudo_local...") + echo`🔐 Adding Touch ID authentication to existing sudo_local...` await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` - $.echo("✅ Touch ID for sudo configured") + echo`✅ Touch ID for sudo configured` } else { - $.echo("✅ Touch ID for sudo already configured") + echo`✅ Touch ID for sudo already configured` } } catch (error) { // File doesn't exist - $.echo("🔐 Setting up Touch ID for sudo...") + echo`🔐 Setting up Touch ID for sudo...` await $`sudo cp /etc/pam.d/sudo_local.template ${sudoLocalPath}` await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` - $.echo("✅ Touch ID for sudo configured") + echo`✅ Touch ID for sudo configured` } diff --git a/home/.scripts/export-vscode-settings.ts b/home/.scripts/export-vscode-settings.ts index e5ceda0..718cc9a 100755 --- a/home/.scripts/export-vscode-settings.ts +++ b/home/.scripts/export-vscode-settings.ts @@ -15,7 +15,7 @@ try { await $`command -v git` } catch (error) { - $.echo("Error: git not found. Install git.") + echo`Error: git not found. Install git.` process.exit(1) } @@ -23,7 +23,7 @@ try { try { await $`command -v rsync` } catch (error) { - $.echo("Error: rsync not found. Install rsync.") + echo`Error: rsync not found. Install rsync.` process.exit(1) } @@ -41,7 +41,7 @@ const scriptDir = __dirname const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout.trim() if (!repoRoot) { - $.echo("Error: Could not determine destination path") + echo`Error: Could not determine destination path` process.exit(1) } @@ -52,7 +52,7 @@ const src = `${process.env.HOME}/Library/Application Support/Code/User` try { await fs.access(src) } catch (error) { - $.echo(`Error: Source VS Code User directory not found: ${src}.`) + echo`Error: Source VS Code User directory not found: ${src}.` process.exit(1) } @@ -60,4 +60,4 @@ await fs.mkdir(dest, { recursive: true }) await $`rsync -a --delete ${includeArgs} --exclude='*' ${src}/ ${dest}/` -$.echo("VS Code settings exported successfully.") +echo`VS Code settings exported successfully.` diff --git a/home/.scripts/import-vscode-settings.ts b/home/.scripts/import-vscode-settings.ts index a127374..d9fdd05 100755 --- a/home/.scripts/import-vscode-settings.ts +++ b/home/.scripts/import-vscode-settings.ts @@ -19,7 +19,7 @@ try { await $`command -v git` } catch (error) { - $.echo("Error: git not found. Install git.") + echo`Error: git not found. Install git.` process.exit(1) } @@ -27,7 +27,7 @@ try { try { await $`command -v rsync` } catch (error) { - $.echo("Error: rsync not found. Install rsync.") + echo`Error: rsync not found. Install rsync.` process.exit(1) } @@ -58,7 +58,7 @@ const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout // Ensure repo root is determined if (!repoRoot) { - $.echo("Error: Could not determine source path") + echo`Error: Could not determine source path` process.exit(1) } @@ -69,7 +69,7 @@ const dest = `${process.env.HOME}/Library/Application Support/Code/User` try { await fs.access(src) } catch (error) { - $.echo(`Error: Source repo VS Code directory not found: ${src}.`) + echo`Error: Source repo VS Code directory not found: ${src}.` process.exit(1) } @@ -77,13 +77,13 @@ try { try { await fs.access(dest) } catch (error) { - $.echo(`Error: Destination VS Code User directory not found: ${dest}.`) + echo`Error: Destination VS Code User directory not found: ${dest}.` process.exit(1) } // If dry-run, note it early if (dryRun) { - $.echo("Dry run: will preview changes only. No backup or import will be performed.") + echo`Dry run: will preview changes only. No backup or import will be performed.` } // Backup existing settings into $dest/.dotfiles_backup/ @@ -94,15 +94,15 @@ if (!dryRun) { await fs.mkdir(backupDir, { recursive: true }) await $`rsync -a ${includeArgs} --exclude='/.dotfiles_backup/***' --exclude='*' ${dest}/ ${backupDir}/` - $.echo(`Backup created at: ${backupDir}`) + echo`Backup created at: ${backupDir}` } // Preview planned changes using git-style color diff and require approval let changes = 0 const jsonFiles = ['mcp', 'keybindings', 'settings'] -$.echo("Planned changes:") -$.echo("") +echo`Planned changes:` +echo`` for (const f of jsonFiles) { const srcFile = `${src}/${f}.json` @@ -160,13 +160,13 @@ try { } if (changes === 0) { - $.echo("No changes to import. Exiting.") + echo`No changes to import. Exiting.` process.exit(0) } // If dry-run, stop here after preview if (dryRun) { - $.echo("Dry run complete. Changes were not applied.") + echo`Dry run complete. Changes were not applied.` process.exit(0) } @@ -174,7 +174,7 @@ if (dryRun) { if (!autoYes) { const answer = await question("Proceed with import? [y/N]: ") if (!answer.match(/^[yY]([eE][sS])?$/)) { - $.echo("Aborted.") + echo`Aborted.` process.exit(1) } } From 6d71d7a957d1416c88ecd73a5adbe6a2b42dabba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:09:30 +0000 Subject: [PATCH 15/15] Update tsconfig for Node 24, improve npm install, use neverthrow in touch-id script Co-authored-by: willmruzek <108522+willmruzek@users.noreply.github.com> --- .../run_once_before_03_install-zx.sh | 24 +++--- ...once_before_04_install-npm-dependencies.sh | 22 +++-- ...e_before_05_configure-touch-id-for-sudo.ts | 86 +++++++++++++++---- home/.scripts/export-vscode-settings.ts | 2 +- tsconfig.json | 8 +- 5 files changed, 104 insertions(+), 38 deletions(-) diff --git a/home/.chezmoiscripts/run_once_before_03_install-zx.sh b/home/.chezmoiscripts/run_once_before_03_install-zx.sh index 43613f0..01d6ced 100755 --- a/home/.chezmoiscripts/run_once_before_03_install-zx.sh +++ b/home/.chezmoiscripts/run_once_before_03_install-zx.sh @@ -2,20 +2,20 @@ set -e -# Install zx before other scripts run via npm -# This ensures all .mjs scripts can execute with zx +# Verify zx is available after npm install +# zx is installed as a dependency in package.json if command -v npx &> /dev/null; then - # npx is available, zx will be available via npx zx - echo "✅ npx is available (zx will be available via npx)" -else - echo "⚠️ npx not available. Installing zx globally via npm..." - if command -v npm &> /dev/null; then - npm install -g zx - echo "✅ zx installed via npm" + echo "✅ npx is available" + + # Check if zx is available via npx + if npx zx --version &> /dev/null; then + echo "✅ zx is available via npx" else - echo "❌ npm not found. Node.js must be installed first." - echo " Please install Node.js before running this script." - exit 1 + echo "⚠️ zx not yet available via npx (will be installed by npm install)" fi +else + echo "❌ npx not found. Node.js must be installed first." + echo " Please install Node.js before running this script." + exit 1 fi diff --git a/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh b/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh index d368163..e695441 100755 --- a/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh +++ b/home/.chezmoiscripts/run_once_before_04_install-npm-dependencies.sh @@ -2,18 +2,28 @@ set -e -# Get the repository root (where package.json lives) -REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" - # Check if npm is available if ! command -v npm &> /dev/null; then echo "⚠️ npm not available, skipping dependency installation" exit 0 fi -# Check if package.json exists -if [ ! -f "$REPO_ROOT/package.json" ]; then - echo "⚠️ package.json not found, skipping dependency installation" +# Find the repository root by looking for package.json +# Start from the current directory and walk up +CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="" + +while [ "$CURRENT_DIR" != "/" ]; do + if [ -f "$CURRENT_DIR/package.json" ]; then + REPO_ROOT="$CURRENT_DIR" + break + fi + CURRENT_DIR="$(dirname "$CURRENT_DIR")" +done + +# Check if we found the repository root +if [ -z "$REPO_ROOT" ]; then + echo "⚠️ package.json not found in any parent directory, skipping dependency installation" exit 0 fi diff --git a/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts index 0710a39..2849550 100755 --- a/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts +++ b/home/.chezmoiscripts/run_once_before_05_configure-touch-id-for-sudo.ts @@ -1,24 +1,80 @@ #!/usr/bin/env zx +import { ResultAsync, err, ok } from 'neverthrow' + // Configure Touch ID for sudo const sudoLocalPath = "/etc/pam.d/sudo_local" -try { - await fs.access(sudoLocalPath) +// Helper to check if file exists +const fileExists = (path: string): ResultAsync => + ResultAsync.fromPromise( + fs.access(path).then(() => true), + () => new Error(`File does not exist: ${path}`) + ).orElse(() => ok(false)) + +// Helper to read file content +const readFile = (path: string): ResultAsync => + ResultAsync.fromPromise( + fs.readFile(path, 'utf8'), + (e) => new Error(`Failed to read file: ${e}`) + ) + +// Helper to execute shell command +const runCommand = (command: ProcessPromise): ResultAsync => + ResultAsync.fromPromise( + command.then(() => undefined), + (e) => new Error(`Command failed: ${e}`) + ) + +// Main logic +const configureTouchID = async () => { + const exists = await fileExists(sudoLocalPath) - // File exists, check if pam_tid.so is already configured - const content = await fs.readFile(sudoLocalPath, 'utf8') - if (!content.includes("pam_tid.so")) { - echo`🔐 Adding Touch ID authentication to existing sudo_local...` - await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` - echo`✅ Touch ID for sudo configured` + if (exists.isErr()) { + return exists + } + + if (exists.value) { + // File exists, check if pam_tid.so is already configured + const contentResult = await readFile(sudoLocalPath) + + if (contentResult.isErr()) { + return contentResult + } + + if (!contentResult.value.includes("pam_tid.so")) { + echo`🔐 Adding Touch ID authentication to existing sudo_local...` + const result = await runCommand($`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}`) + if (result.isErr()) { + return result + } + echo`✅ Touch ID for sudo configured` + } else { + echo`✅ Touch ID for sudo already configured` + } } else { - echo`✅ Touch ID for sudo already configured` + // File doesn't exist + echo`🔐 Setting up Touch ID for sudo...` + const copyResult = await runCommand($`sudo cp /etc/pam.d/sudo_local.template ${sudoLocalPath}`) + if (copyResult.isErr()) { + return copyResult + } + + const appendResult = await runCommand($`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}`) + if (appendResult.isErr()) { + return appendResult + } + + echo`✅ Touch ID for sudo configured` } -} catch (error) { - // File doesn't exist - echo`🔐 Setting up Touch ID for sudo...` - await $`sudo cp /etc/pam.d/sudo_local.template ${sudoLocalPath}` - await $`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}` - echo`✅ Touch ID for sudo configured` + + return ok(undefined) +} + +// Execute and handle result +const result = await configureTouchID() + +if (result.isErr()) { + echo`❌ Error: ${result.error.message}` + process.exit(1) } diff --git a/home/.scripts/export-vscode-settings.ts b/home/.scripts/export-vscode-settings.ts index 718cc9a..f7af596 100755 --- a/home/.scripts/export-vscode-settings.ts +++ b/home/.scripts/export-vscode-settings.ts @@ -38,7 +38,7 @@ const includes = [ const includeArgs = includes.flatMap(p => ['--include', p]) const scriptDir = __dirname -const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout.trim() +const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout if (!repoRoot) { echo`Error: Could not determine destination path` diff --git a/tsconfig.json b/tsconfig.json index fb31268..2c8d6fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022"], - "moduleResolution": "bundler", + "lib": ["ES2024"], + "module": "nodenext", + "target": "ES2024", + "moduleResolution": "nodenext", "allowImportingTsExtensions": true, "resolveJsonModule": true, "noEmit": true,