diff --git a/bin/dot b/bin/dot
index 7d717ed1..8b2e7c6c 100755
--- a/bin/dot
+++ b/bin/dot
@@ -88,47 +88,36 @@ get_system_dir() {
echo "$DOT_HOME/$installed_system"
}
-# Resolve module name (handles numeric prefixes like 00-, 01-)
-resolve_module_path() {
+# Resolve script name (handles numeric prefixes like 01-50-mise)
+resolve_script_name() {
local module="$1"
+ local command="$2"
local system_dir="$(get_system_dir)"
-
- # First, try the module as-is
- if [[ -d "$system_dir/$module" ]]; then
+ local command_dir="$system_dir/$command"
+
+ # First, try the module name as-is
+ if [[ -f "$command_dir/$module" && -x "$command_dir/$module" ]]; then
echo "$module"
return 0
fi
-
- # If module contains /, handle each part
- if [[ "$module" == */* ]]; then
- local parent="${module%/*}"
- local child="${module##*/}"
-
- # Try with numeric prefix on child
- for prefix in {00..99}; do
- if [[ -d "$system_dir/$parent/${prefix}-${child}" ]]; then
- echo "$parent/${prefix}-${child}"
- return 0
- fi
- done
-
- # Try with numeric prefix on parent
- for prefix in {00..99}; do
- if [[ -d "$system_dir/${prefix}-${parent}/$child" ]]; then
- echo "${prefix}-${parent}/$child"
- return 0
- fi
- done
- else
- # Try with numeric prefixes for top-level modules
- for prefix in {00..99}; do
- if [[ -d "$system_dir/${prefix}-${module}" ]]; then
- echo "${prefix}-${module}"
- return 0
- fi
- done
+
+ # Try to find a script that ends with the module name (e.g., "mise" -> "01-50-mise")
+ local found_script=""
+ for script in "$command_dir"/*; do
+ [[ ! -f "$script" || ! -x "$script" ]] && continue
+ local script_name=$(basename "$script")
+ # Check if script ends with -$module
+ if [[ "$script_name" == *-"$module" ]]; then
+ found_script="$script_name"
+ break
+ fi
+ done
+
+ if [[ -n "$found_script" ]]; then
+ echo "$found_script"
+ return 0
fi
-
+
# Return original if no match found
echo "$module"
return 1
@@ -151,7 +140,7 @@ check_system_for_list() {
get_available_modules() {
local command="$1"
local modules=()
-
+
# Get the installed system type
local installed_system
if [[ -n "${INSTALLATION:-}" ]]; then
@@ -160,28 +149,30 @@ get_available_modules() {
installed_system=$(head -n1 "$INSTALLATION_MARKER")
fi
local system_dir="$DOT_HOME/$installed_system"
-
- # Check if system directory exists
- if [[ ! -d "$system_dir" ]]; then
+ local command_dir="$system_dir/$command"
+
+ # Check if command directory exists
+ if [[ ! -d "$command_dir" ]]; then
return
fi
-
- # Find all modules with the given command script
+
+ # Find all executable files in the command directory
+ # Use -perm for BSD find compatibility (macOS)
while IFS= read -r -d '' script; do
- local module_path=$(dirname "$script")
- # Get the full relative path from system_dir
- local module_name="${module_path#$system_dir/}"
-
- modules+=("$module_name")
- done < <(find "$system_dir" -name "$command" -type f -executable -print0)
-
- printf '%s\n' "${modules[@]}" | sort
+ local script_name=$(basename "$script")
+ modules+=("$script_name")
+ done < <(find "$command_dir" -maxdepth 1 -type f -perm +111 -print0 2>/dev/null)
+
+ # Handle empty array to avoid unbound variable error
+ if [[ ${#modules[@]} -gt 0 ]]; then
+ printf '%s\n' "${modules[@]}" | sort
+ fi
}
# Run install command
cmd_install() {
local module="${1:-all}"
-
+
# Handle --list option
if [[ "$module" == "--list" ]]; then
check_system_for_list
@@ -189,41 +180,65 @@ cmd_install() {
get_available_modules "install"
return 0
fi
-
+
check_system_setup
init_logging "install"
-
+
log_header "Installing: $module"
-
+
+ # Export common variables for scripts
+ export DOT_HOME="$DOT_HOME"
+ export SYSTEM_DIR="$(get_system_dir)"
+ export SHARED_DIR="$DOT_HOME/shared"
+
if [[ "$module" == "all" ]]; then
log_info "Installing all modules"
-
+
# Get all available install scripts
- local available_modules
- mapfile -t available_modules < <(get_available_modules "install")
-
- for mod in "${available_modules[@]}"; do
- log_step "Running install for module: $mod"
- "$(get_system_dir)/$mod/install"
+ local available_modules=()
+ while IFS= read -r line; do
+ [[ -n "$line" ]] && available_modules+=("$line")
+ done < <(get_available_modules "install")
+
+ for script_name in "${available_modules[@]}"; do
+ # Extract module name from script filename (e.g., "01-50-mise" -> "mise")
+ export MODULE_NAME="$(basename "$script_name" | sed 's/^[0-9]*-[0-9]*-//')"
+
+ init_module_logging "$MODULE_NAME"
+ if "$SYSTEM_DIR/install/$script_name"; then
+ finalize_module_logging "success"
+ else
+ finalize_module_logging "failed"
+ exit 1
+ fi
done
else
# Install specific module
- local resolved_module=$(resolve_module_path "$module")
- if [[ -x "$(get_system_dir)/$resolved_module/install" ]]; then
- "$(get_system_dir)/$resolved_module/install"
+ local script_name=$(resolve_script_name "$module" "install")
+ if [[ -x "$SYSTEM_DIR/install/$script_name" ]]; then
+ # Extract module name from script filename
+ export MODULE_NAME="$(basename "$script_name" | sed 's/^[0-9]*-[0-9]*-//')"
+
+ init_module_logging "$MODULE_NAME"
+ if "$SYSTEM_DIR/install/$script_name"; then
+ finalize_module_logging "success"
+ else
+ finalize_module_logging "failed"
+ exit 1
+ fi
else
log_error "Module $module does not have an install script"
exit 1
fi
fi
-
+
finalize_logging "success"
}
# Run build command
cmd_build() {
local module="${1:-all}"
-
+
# Handle --list option
if [[ "$module" == "--list" ]]; then
check_system_for_list
@@ -231,41 +246,65 @@ cmd_build() {
get_available_modules "build"
return 0
fi
-
+
check_system_setup
init_logging "build"
-
+
log_header "Building: $module"
-
+
+ # Export common variables for scripts
+ export DOT_HOME="$DOT_HOME"
+ export SYSTEM_DIR="$(get_system_dir)"
+ export SHARED_DIR="$DOT_HOME/shared"
+
if [[ "$module" == "all" ]]; then
log_info "Building all modules"
-
+
# Get all available build scripts
- local available_modules
- mapfile -t available_modules < <(get_available_modules "build")
-
- for mod in "${available_modules[@]}"; do
- log_step "Running build for module: $mod"
- "$(get_system_dir)/$mod/build"
+ local available_modules=()
+ while IFS= read -r line; do
+ [[ -n "$line" ]] && available_modules+=("$line")
+ done < <(get_available_modules "build")
+
+ for script_name in "${available_modules[@]}"; do
+ # Extract module name from script filename (e.g., "03-00-git" -> "git")
+ export MODULE_NAME="$(basename "$script_name" | sed 's/^[0-9]*-[0-9]*-//')"
+
+ init_module_logging "$MODULE_NAME"
+ if "$SYSTEM_DIR/build/$script_name"; then
+ finalize_module_logging "success"
+ else
+ finalize_module_logging "failed"
+ exit 1
+ fi
done
else
# Build specific module
- local resolved_module=$(resolve_module_path "$module")
- if [[ -x "$(get_system_dir)/$resolved_module/build" ]]; then
- "$(get_system_dir)/$resolved_module/build"
+ local script_name=$(resolve_script_name "$module" "build")
+ if [[ -x "$SYSTEM_DIR/build/$script_name" ]]; then
+ # Extract module name from script filename
+ export MODULE_NAME="$(basename "$script_name" | sed 's/^[0-9]*-[0-9]*-//')"
+
+ init_module_logging "$MODULE_NAME"
+ if "$SYSTEM_DIR/build/$script_name"; then
+ finalize_module_logging "success"
+ else
+ finalize_module_logging "failed"
+ exit 1
+ fi
else
log_error "Module $module does not have a build script"
exit 1
fi
fi
-
+
finalize_logging "success"
}
# Run upgrade command
cmd_upgrade() {
local module="${1:-all}"
-
+
# Handle --list option
if [[ "$module" == "--list" ]]; then
check_system_for_list
@@ -273,40 +312,64 @@ cmd_upgrade() {
get_available_modules "upgrade"
return 0
fi
-
+
check_system_setup
init_logging "upgrade"
-
+
log_header "Upgrading: $module"
-
+
+ # Export common variables for scripts
+ export DOT_HOME="$DOT_HOME"
+ export SYSTEM_DIR="$(get_system_dir)"
+ export SHARED_DIR="$DOT_HOME/shared"
+
if [[ "$module" == "all" ]]; then
log_info "Upgrading all modules"
-
+
# Get all available upgrade scripts
- local available_modules
- mapfile -t available_modules < <(get_available_modules "upgrade")
-
+ local available_modules=()
+ while IFS= read -r line; do
+ [[ -n "$line" ]] && available_modules+=("$line")
+ done < <(get_available_modules "upgrade")
+
if [[ ${#available_modules[@]} -eq 0 ]]; then
log_info "No modules have upgrade scripts"
finalize_logging "success"
return
fi
-
- for mod in "${available_modules[@]}"; do
- log_step "Running upgrade for module: $mod"
- "$(get_system_dir)/$mod/upgrade"
+
+ for script_name in "${available_modules[@]}"; do
+ # Extract module name from script filename
+ export MODULE_NAME="$(basename "$script_name" | sed 's/^[0-9]*-[0-9]*-//')"
+
+ init_module_logging "$MODULE_NAME"
+ if "$SYSTEM_DIR/upgrade/$script_name"; then
+ finalize_module_logging "success"
+ else
+ finalize_module_logging "failed"
+ exit 1
+ fi
done
else
# Upgrade specific module
- local resolved_module=$(resolve_module_path "$module")
- if [[ -x "$(get_system_dir)/$resolved_module/upgrade" ]]; then
- "$(get_system_dir)/$resolved_module/upgrade"
+ local script_name=$(resolve_script_name "$module" "upgrade")
+ if [[ -x "$SYSTEM_DIR/upgrade/$script_name" ]]; then
+ # Extract module name from script filename
+ export MODULE_NAME="$(basename "$script_name" | sed 's/^[0-9]*-[0-9]*-//')"
+
+ init_module_logging "$MODULE_NAME"
+ if "$SYSTEM_DIR/upgrade/$script_name"; then
+ finalize_module_logging "success"
+ else
+ finalize_module_logging "failed"
+ exit 1
+ fi
else
log_error "Module $module does not have an upgrade script"
exit 1
fi
fi
-
+
finalize_logging "success"
}
diff --git a/bin/install-macos b/bin/install-macos
new file mode 100755
index 00000000..4ff11f90
--- /dev/null
+++ b/bin/install-macos
@@ -0,0 +1,102 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# Check if running with --local flag (already cloned and running locally)
+if [[ "${1:-}" != "--local" ]]; then
+ # Remote execution - clone repo and re-exec locally
+ echo "[INFO] Cloning dotfiles and running local setup..."
+
+ DOTFILES_DIR="$HOME/Repositories/dotfiles"
+
+ if [[ ! -d "$DOTFILES_DIR" ]]; then
+ echo "[INFO] Creating Repositories directory"
+ mkdir -p "$HOME/Repositories"
+
+ echo "[INFO] Cloning dotfiles repository"
+ git clone https://github.com/vikdotdev/dotfiles.git "$DOTFILES_DIR"
+ else
+ echo "[INFO] Dotfiles repository already exists, pulling latest changes"
+ cd "$DOTFILES_DIR"
+ git pull origin master
+ fi
+
+ # Re-exec the local script with --local flag
+ echo "[INFO] Running local install script..."
+ chmod +x "$DOTFILES_DIR/bin/install-macos"
+ exec "$DOTFILES_DIR/bin/install-macos" --local
+fi
+
+# Local execution from here on
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOT_HOME="$(dirname "$SCRIPT_DIR")"
+
+# Source local utilities
+source "$DOT_HOME/lib/macos/include.sh"
+
+# Initialize
+init_logging "install-macos"
+
+# Don't run as root
+if is_root; then
+ log_error "Don't run install-macos as root/sudo. It will ask for sudo when needed."
+ exit 1
+fi
+
+log_header "macOS Installation Setup"
+
+# Check if we're on macOS
+if ! is_macos; then
+ log_error "This script is designed for macOS systems"
+ exit 1
+fi
+
+# Basic system setup
+log_step "Setting up basic system requirements"
+
+# Install Homebrew if not present
+if ! command_exists brew; then
+ log_info "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 [[ -f "/opt/homebrew/bin/brew" ]]; then
+ eval "$(/opt/homebrew/bin/brew shellenv)"
+ fi
+else
+ log_info "Homebrew already installed"
+fi
+
+# Update Homebrew
+log_info "Updating Homebrew"
+brew update
+
+# Install essential tools
+log_info "Installing minimal essential packages"
+ESSENTIAL_FORMULAS=(
+ "curl"
+ "wget"
+ "git"
+ "gnupg"
+)
+
+brew install "${ESSENTIAL_FORMULAS[@]}"
+
+# Mark system as installed
+mark_system_installed "macos"
+log_success "System marked as macOS installation"
+
+# Run dot install to install all packages and applications
+log_step "Running dot install to set up all packages and applications"
+"$DOT_HOME/bin/dot" install
+
+# Run dot build to configure all dotfiles
+log_step "Running dot build to configure all dotfiles"
+"$DOT_HOME/bin/dot" build
+
+log_success "macOS setup complete!"
+echo
+echo "All packages installed and dotfiles configured!"
+echo "You may need to restart your shell for some changes to take effect."
+
+finalize_logging "success"
diff --git a/debian/kmonad/build b/debian/kmonad/build
index 2ea898ec..d62366c6 100755
--- a/debian/kmonad/build
+++ b/debian/kmonad/build
@@ -29,10 +29,11 @@ log_step "Deploying KMonad configuration"
CONFIG_DIR="$HOME/.config/kmonad"
mkdir -p "$CONFIG_DIR"
-# Deploy keyboard configuration
-if [[ -f "$SCRIPT_DIR/config/laptop.kbd" ]]; then
+# Deploy keyboard configuration from shared
+SHARED_CONFIG="$DOT_HOME/shared/kmonad/config/laptop.kbd"
+if [[ -f "$SHARED_CONFIG" ]]; then
log_info "Deploying keyboard configuration"
- cp "$SCRIPT_DIR/config/laptop.kbd" "$CONFIG_DIR/config.kbd"
+ cp "$SHARED_CONFIG" "$CONFIG_DIR/config.kbd"
else
log_warning "No keyboard configuration found"
fi
diff --git a/debian/kmonad/config/laptop.kbd b/debian/kmonad/config/laptop.kbd
deleted file mode 100644
index db472b47..00000000
--- a/debian/kmonad/config/laptop.kbd
+++ /dev/null
@@ -1,57 +0,0 @@
-(defcfg
- input (device-file "/dev/input/by-path/platform-i8042-serio-0-event-kbd")
- output (uinput-sink "KMonad output" "/usr/bin/sleep 1")
- cmp-seq ralt
- cmp-seq-delay 5
- fallthrough true
- allow-cmd true
-)
-
-(defalias
- mta (tap-hold-next-release 150 tab lmet)
- rta (tap-hold-next-release 150 \ rmet)
- qta (tap-hold-next-release 150 ' lalt)
- ces (tap-hold-next-release 150 esc lctl)
- laa (tap-hold-next-release 300 a lalt)
- lac (tap-hold-next-release 300 ; rctl)
- lsb (tap-hold-next-release 170 lbrc lsft)
- cst (tap-macro (around-next lsft) (around-next lctl))
- rsb (tap-hold-next-release 170 rbrc rsft)
- sid (tap-hold-next-release 300 spc (layer-toggle side))
- ;; browser history navigation
- hfo (around lalt right)
- hba (around lalt left)
- ;; browser tab navigation
- tba (around lctl (around lsft tab))
- tfo (around lctl tab)
-)
-
-(defsrc
- esc mute vold volu end ins del
- grv 1 2 3 4 5 6 7 8 9 0 - = bspc
- tab q w e r t y u i o p [ ] \
- caps a s d f g h j k l ; ' ret
- lsft z x c v b n m , . / rsft
- lctl lmet lalt spc ralt prnt rctl pgup up pgdn
- left down rght
-)
-
-(deflayer main
- esc mute vold volu end ins del
- = 1 2 3 4 5 6 7 8 9 0 - grv XX
- @mta q w e r t y u i o p @rta XX XX
- @ces @laa s d f g h j k l @lac @qta ret
- @lsb z x c v b n m , . / @rsb
- caps _ lalt @sid ralt prnt rctl pgup up pgdn
- left down rght
-)
-
-(deflayer side
- _ _ _ _ _ _ _
- _ _ _ _ _ _ _ _ _ _ _ _ _ _
- _ @hba @hfo @tba @tfo _ _ _ _ _ up _ _ _
- _ _ _ bspc rght ret _ _ _ _ _ _ _
- _ _ _ _ _ left down @cst _ _ _ _
- _ _ _ _ _ _ _ _ _ _
- _ _ _
-)
diff --git a/debian/shell/build b/debian/shell/build
index 7b8f86c3..625ca7fe 100755
--- a/debian/shell/build
+++ b/debian/shell/build
@@ -5,68 +5,5 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOT_HOME="$(dirname "$(dirname "$SCRIPT_DIR")")"
-source "$DOT_HOME/lib/debian/include.sh"
-
-MODULE="shell"
-init_module_logging "$MODULE"
-
-log_header "Building Shell Configuration"
-
-# Deploy shell configuration files from existing plans
-log_step "Deploying shell configuration"
-
-# Bash configuration
-log_step "Installing bash configuration"
-
-# Copy bash configuration files
-if [[ -f "$SCRIPT_DIR/config/.bashrc" ]]; then
- cp "$SCRIPT_DIR/config/.bashrc" "$HOME/.bashrc"
- log_info "Deployed .bashrc"
-fi
-
-if [[ -f "$SCRIPT_DIR/config/.profile" ]]; then
- cp "$SCRIPT_DIR/config/.profile" "$HOME/.profile"
- log_info "Deployed .profile"
-fi
-
-if [[ -f "$SCRIPT_DIR/config/.bash_profile" ]]; then
- cp "$SCRIPT_DIR/config/.bash_profile" "$HOME/.bash_profile"
- log_info "Deployed .bash_profile"
-fi
-
-# Pry configuration
-if [[ -f "$SCRIPT_DIR/config/.pryrc" ]]; then
- cp "$SCRIPT_DIR/config/.pryrc" "$HOME/.pryrc"
- log_info "Deployed .pryrc"
-fi
-
-# IRB configuration
-if [[ -f "$SCRIPT_DIR/config/.irbrc" ]]; then
- cp "$SCRIPT_DIR/config/.irbrc" "$HOME/.irbrc"
- log_info "Deployed .irbrc"
-fi
-
-# Readline configuration
-if [[ -f "$SCRIPT_DIR/config/.inputrc" ]]; then
- cp "$SCRIPT_DIR/config/.inputrc" "$HOME/.inputrc"
- log_info "Deployed .inputrc"
-fi
-
-# Deploy .profile.local example if it doesn't exist
-if [[ ! -f "$HOME/.profile.local" ]] && [[ -f "$SCRIPT_DIR/config/.profile.local.example" ]]; then
- cp "$SCRIPT_DIR/config/.profile.local.example" "$HOME/.profile.local"
- log_info "Deployed .profile.local from example (customize as needed)"
-fi
-
-# Bash completions
-log_step "Installing bash completions"
-mkdir -p "$HOME/.bashrc.d"
-
-# Source dot completions from top-level
-if [[ -f "$DOT_HOME/completions/dot.bash" ]]; then
- cp "$DOT_HOME/completions/dot.bash" "$HOME/.bashrc.d/dot-completion.bash"
- log_info "Deployed dot command completions"
-fi
-
-log_success "Shell configuration completed"
-finalize_module_logging "success"
+# Call shared build script
+"$DOT_HOME/shared/shell/build"
diff --git a/debian/tools/00-mise/build b/debian/tools/00-mise/build
index 4e6f0e90..003100fa 100755
--- a/debian/tools/00-mise/build
+++ b/debian/tools/00-mise/build
@@ -15,7 +15,7 @@ log_header "Building Mise Configuration"
if command_exists mise; then
log_step "Creating mise.toml"
mkdir -p "$HOME/.config/mise/"
- cp "$SCRIPT_DIR/config/mise.toml" "$HOME/.config/mise/config.toml"
+ cp "$DOT_HOME/shared/tools/mise/config/mise.toml" "$HOME/.config/mise/config.toml"
log_success "Deployed mise config to ~/.config/mise/config.toml"
log_step "Installing tools"
mise install
diff --git a/debian/tools/01-ghostty/build b/debian/tools/01-ghostty/build
index 9175d879..4bbbbe40 100755
--- a/debian/tools/01-ghostty/build
+++ b/debian/tools/01-ghostty/build
@@ -16,13 +16,14 @@ log_header "Building Ghostty Configuration"
log_step "Creating Ghostty config directory"
mkdir -p "$HOME/.config/ghostty"
-# Deploy Ghostty configuration
+# Deploy Ghostty configuration from shared
log_step "Deploying Ghostty configuration"
-if [[ -f "$SCRIPT_DIR/config/config" ]]; then
- cp "$SCRIPT_DIR/config/config" "$HOME/.config/ghostty/config"
+SHARED_CONFIG="$DOT_HOME/shared/ghostty/config/config"
+if [[ -f "$SHARED_CONFIG" ]]; then
+ cp "$SHARED_CONFIG" "$HOME/.config/ghostty/config"
log_info "Deployed Ghostty config to ~/.config/ghostty/config"
else
- log_error "Ghostty config file not found"
+ log_error "Ghostty config file not found at $SHARED_CONFIG"
finalize_module_logging "failed"
exit 1
fi
diff --git a/debian/tools/emacs/build b/debian/tools/emacs/build
index c713ec3e..0361f70d 100755
--- a/debian/tools/emacs/build
+++ b/debian/tools/emacs/build
@@ -4,6 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOT_HOME="$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")"
+SHARED_CONFIG_DIR="$DOT_HOME/shared/tools/emacs/config"
source "$DOT_HOME/lib/debian/include.sh"
@@ -24,11 +25,11 @@ cleanup_temp() {
trap cleanup_temp EXIT
# Tangle the org file to extract elisp code blocks
-if [[ -f "$SCRIPT_DIR/config/config.org" ]]; then
+if [[ -f "$SHARED_CONFIG_DIR/config.org" ]]; then
log_info "Extracting elisp code from config.org..."
emacs --batch \
--eval "(require 'org)" \
- --eval "(org-babel-tangle-file \"$SCRIPT_DIR/config/config.org\" \"$TEMP_EL\")" \
+ --eval "(org-babel-tangle-file \"$SHARED_CONFIG_DIR/config.org\" \"$TEMP_EL\")" \
2>&1 | grep -v "^Loading" || true
if [[ ! -f "$TEMP_EL" ]]; then
@@ -64,18 +65,18 @@ log_step "Deploying Emacs configuration"
EMACS_CONFIG_DIR="$HOME/.emacs.d"
mkdir -p "$EMACS_CONFIG_DIR"
-if [[ -f "$SCRIPT_DIR/config/early-init.el" ]]; then
- cp "$SCRIPT_DIR/config/early-init.el" "$EMACS_CONFIG_DIR/early-init.el"
+if [[ -f "$SHARED_CONFIG_DIR/early-init.el" ]]; then
+ cp "$SHARED_CONFIG_DIR/early-init.el" "$EMACS_CONFIG_DIR/early-init.el"
log_info "Deployed early-init.el"
fi
-if [[ -f "$SCRIPT_DIR/config/init.el" ]]; then
- cp "$SCRIPT_DIR/config/init.el" "$EMACS_CONFIG_DIR/init.el"
+if [[ -f "$SHARED_CONFIG_DIR/init.el" ]]; then
+ cp "$SHARED_CONFIG_DIR/init.el" "$EMACS_CONFIG_DIR/init.el"
log_info "Deployed init.el"
fi
-if [[ -f "$SCRIPT_DIR/config/config.org" ]]; then
- cp "$SCRIPT_DIR/config/config.org" "$EMACS_CONFIG_DIR/config.org"
+if [[ -f "$SHARED_CONFIG_DIR/config.org" ]]; then
+ cp "$SHARED_CONFIG_DIR/config.org" "$EMACS_CONFIG_DIR/config.org"
log_info "Deployed config.org"
fi
diff --git a/debian/tools/git/build b/debian/tools/git/build
index 7024cc5c..7657848f 100755
--- a/debian/tools/git/build
+++ b/debian/tools/git/build
@@ -5,48 +5,11 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOT_HOME="$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")"
-source "$DOT_HOME/lib/debian/include.sh"
+# Call shared build script
+"$DOT_HOME/shared/tools/git/build"
-MODULE="git"
-init_module_logging "$MODULE"
-
-log_header "Building Git Configuration"
-
-log_step "Installing Git configuration"
-
-# Copy .gitconfig file
-if [[ -f "$SCRIPT_DIR/config/.gitconfig" ]]; then
- cp "$SCRIPT_DIR/config/.gitconfig" "$HOME/.gitconfig"
- log_success "Git configuration installed"
-else
- log_warning "Git configuration file not found"
-fi
-
-# Copy global gitignore file
-if [[ -f "$SCRIPT_DIR/config/ignore" ]]; then
- mkdir -p "$HOME/.config/git"
- cp "$SCRIPT_DIR/config/ignore" "$HOME/.config/git/ignore"
- log_success "Global gitignore installed"
-else
- log_warning "Global gitignore file not found"
+# Copy Debian-specific git configuration
+if [[ -f "$SCRIPT_DIR/config/gitconfig.os" ]]; then
+ cp "$SCRIPT_DIR/config/gitconfig.os" "$HOME/.gitconfig.os"
+ echo "[Debian] OS-specific git configuration installed"
fi
-
-log_step "Installing git scripts"
-
-# Create scripts directory
-mkdir -p "$HOME/.local/bin"
-
-# Copy git utility scripts
-if [[ -d "$SCRIPT_DIR/scripts" ]]; then
- for script in "$SCRIPT_DIR/scripts"/*; do
- if [[ -f "$script" ]]; then
- script_name=$(basename "$script")
- cp "$script" "$HOME/.local/bin/$script_name"
- chmod +x "$HOME/.local/bin/$script_name"
- log_info "Installed script: $script_name"
- fi
- done
-fi
-
-log_success "Git configuration build completed"
-finalize_module_logging "success"
diff --git a/debian/tools/git/config/gitconfig.os b/debian/tools/git/config/gitconfig.os
new file mode 100644
index 00000000..4bf4bebd
--- /dev/null
+++ b/debian/tools/git/config/gitconfig.os
@@ -0,0 +1,5 @@
+# Debian-specific git configuration
+[credential "https://github.com"]
+ helper = !/usr/bin/gh auth git-credential
+[credential "https://gist.github.com"]
+ helper = !/usr/bin/gh auth git-credential
diff --git a/debian/tools/gpg/build b/debian/tools/gpg/build
index 4d74de1f..7b88cc8b 100755
--- a/debian/tools/gpg/build
+++ b/debian/tools/gpg/build
@@ -5,32 +5,5 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOT_HOME="$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")"
-source "$DOT_HOME/lib/debian/include.sh"
-
-MODULE="gpg"
-init_module_logging "$MODULE"
-
-log_header "Building GPG Configuration"
-
-log_step "Installing GPG agent configuration"
-
-# Create GPG directory if it doesn't exist
-mkdir -p "$HOME/.gnupg"
-
-# Set proper permissions for GPG directory
-chmod 700 "$HOME/.gnupg"
-
-# Copy GPG agent configuration
-if [[ -f "$SCRIPT_DIR/config/gpg-agent.conf" ]]; then
- cp "$SCRIPT_DIR/config/gpg-agent.conf" "$HOME/.gnupg/gpg-agent.conf"
- chmod 600 "$HOME/.gnupg/gpg-agent.conf"
-
- # Restart GPG agent to pick up new config
- if command_exists gpgconf; then
- log_info "Reloading GPG agent configuration"
- gpgconf --reload gpg-agent || log_warning "Failed to reload GPG agent"
- fi
-fi
-
-log_success "GPG configuration build completed"
-finalize_module_logging "success"
+# Call shared build script
+"$DOT_HOME/shared/tools/gpg/build"
diff --git a/lib/macos/constants.sh b/lib/macos/constants.sh
new file mode 100644
index 00000000..da776ff6
--- /dev/null
+++ b/lib/macos/constants.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Get the lib directory path
+LIB_DIR="$(dirname "$(dirname "${BASH_SOURCE[0]}")")"
+
+# Source general constants first
+source "$LIB_DIR/constants.sh"
+
+MACOS_VERSION=$(sw_vers -productVersion 2>/dev/null || echo "unknown")
+MACOS_CODENAME=$(get_macos_codename 2>/dev/null || echo "unknown")
diff --git a/lib/macos/include.sh b/lib/macos/include.sh
new file mode 100644
index 00000000..c591a9d5
--- /dev/null
+++ b/lib/macos/include.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Get the macos lib directory path
+MACOS_LIB_DIR="$(dirname "${BASH_SOURCE[0]}")"
+LIB_DIR="$(dirname "$MACOS_LIB_DIR")"
+
+source "$LIB_DIR/colors.sh"
+source "$LIB_DIR/logging.sh"
+source "$LIB_DIR/constants.sh"
+source "$LIB_DIR/utils.sh"
+
+# May override some functions
+source "$MACOS_LIB_DIR/utils.sh"
+source "$MACOS_LIB_DIR/constants.sh"
diff --git a/lib/macos/utils.sh b/lib/macos/utils.sh
new file mode 100644
index 00000000..e03d1193
--- /dev/null
+++ b/lib/macos/utils.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+
+# macOS-specific utilities - extends the general utilities
+
+ensure_homebrew() {
+ if ! command_exists brew; then
+ log_warning "Homebrew not found. 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 [[ -f "/opt/homebrew/bin/brew" ]]; then
+ eval "$(/opt/homebrew/bin/brew shellenv)"
+ fi
+ fi
+}
+
+brew_installed() {
+ local formula="$1"
+ brew list "$formula" &>/dev/null
+}
+
+cask_installed() {
+ local cask="$1"
+ brew list --cask "$cask" &>/dev/null
+}
+
+install_formula() {
+ local formulas=("$@")
+
+ ensure_homebrew
+ log_info "Installing formulas with brew: ${formulas[*]}"
+ brew install "${formulas[@]}"
+}
+
+install_cask() {
+ local casks=("$@")
+
+ ensure_homebrew
+ log_info "Installing casks with brew: ${casks[*]}"
+ brew install --cask "${casks[@]}"
+}
+
+remove_formula() {
+ local formulas=("$@")
+
+ log_info "Removing formulas with brew: ${formulas[*]}"
+ brew uninstall "${formulas[@]}"
+}
+
+update_homebrew() {
+ log_info "Updating Homebrew"
+ ensure_homebrew
+ brew update
+}
+
+upgrade_formulas() {
+ log_info "Upgrading all formulas"
+ ensure_homebrew
+ brew upgrade
+}
+
+cleanup_homebrew() {
+ log_info "Cleaning up Homebrew"
+ ensure_homebrew
+ brew cleanup
+}
+
+get_macos_version() {
+ sw_vers -productVersion
+}
+
+is_macos() {
+ [[ "$OSTYPE" == "darwin"* ]]
+}
+
+get_macos_codename() {
+ local version=$(sw_vers -productVersion)
+ local major=$(echo "$version" | cut -d. -f1)
+ local minor=$(echo "$version" | cut -d. -f2)
+
+ case "$major" in
+ 15) echo "Sequoia" ;;
+ 14) echo "Sonoma" ;;
+ 13) echo "Ventura" ;;
+ 12) echo "Monterey" ;;
+ 11) echo "Big Sur" ;;
+ 10)
+ case "$minor" in
+ 15) echo "Catalina" ;;
+ 14) echo "Mojave" ;;
+ 13) echo "High Sierra" ;;
+ *) echo "unknown" ;;
+ esac
+ ;;
+ *) echo "unknown" ;;
+ esac
+}
+
+formula_available() {
+ local formula="$1"
+ brew info "$formula" &>/dev/null
+}
diff --git a/macos/build/01-00-mise b/macos/build/01-00-mise
new file mode 100755
index 00000000..67a5b51c
--- /dev/null
+++ b/macos/build/01-00-mise
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+if command_exists mise; then
+ log_step "Creating mise.toml"
+ mkdir -p "$HOME/.config/mise/"
+ cp "$SHARED_DIR/tools/mise/config/mise.toml" "$HOME/.config/mise/config.toml"
+ log_success "Deployed mise config to ~/.config/mise/config.toml"
+ log_step "Installing tools"
+ mise install
+else
+ log_error "mise build failed - mise binary not found"
+ exit 0
+fi
diff --git a/macos/build/02-00-emacs b/macos/build/02-00-emacs
new file mode 100755
index 00000000..069abc8f
--- /dev/null
+++ b/macos/build/02-00-emacs
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Deploying Emacs configuration"
+
+EMACS_CONFIG_DIR="$HOME/.emacs.d"
+SHARED_CONFIG_DIR="$SHARED_DIR/tools/emacs/config"
+mkdir -p "$EMACS_CONFIG_DIR"
+
+if [[ -f "$SHARED_CONFIG_DIR/early-init.el" ]]; then
+ cp "$SHARED_CONFIG_DIR/early-init.el" "$EMACS_CONFIG_DIR/early-init.el"
+ log_info "Deployed early-init.el"
+fi
+
+if [[ -f "$SHARED_CONFIG_DIR/init.el" ]]; then
+ cp "$SHARED_CONFIG_DIR/init.el" "$EMACS_CONFIG_DIR/init.el"
+ log_info "Deployed init.el"
+fi
+
+if [[ -f "$SHARED_CONFIG_DIR/config.org" ]]; then
+ cp "$SHARED_CONFIG_DIR/config.org" "$EMACS_CONFIG_DIR/config.org"
+ log_info "Deployed config.org"
+fi
+
+log_success "Emacs configuration completed"
diff --git a/macos/build/03-00-git b/macos/build/03-00-git
new file mode 100755
index 00000000..95592b16
--- /dev/null
+++ b/macos/build/03-00-git
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -euo pipefail
+
+"$SHARED_DIR/tools/git/build"
+
+if [[ -f "$SYSTEM_DIR/config/git/gitconfig.os" ]]; then
+ cp "$SYSTEM_DIR/config/git/gitconfig.os" "$HOME/.gitconfig.os"
+ echo "[macOS] OS-specific git configuration installed"
+fi
diff --git a/macos/build/04-00-amethyst b/macos/build/04-00-amethyst
new file mode 100755
index 00000000..9a636483
--- /dev/null
+++ b/macos/build/04-00-amethyst
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+if ! command -v open >/dev/null || [[ ! -d "/Applications/Amethyst.app" ]]; then
+ log_warning "Amethyst not installed, skipping configuration deployment"
+ log_info "Run 'dot install amethyst' to install Amethyst first"
+ exit 0
+fi
+
+log_step "Deploying Amethyst configuration"
+SOURCE_CONFIG="$SYSTEM_DIR/config/amethyst/.amethyst.yml"
+DEST_CONFIG="$HOME/.amethyst.yml"
+
+if [[ ! -f "$SOURCE_CONFIG" ]]; then
+ log_error "Config file not found: $SOURCE_CONFIG"
+ exit 1
+fi
+
+cp "$SOURCE_CONFIG" "$DEST_CONFIG"
+log_success "Amethyst configuration deployed to $DEST_CONFIG"
+log_info "Restart Amethyst for changes to take effect"
diff --git a/macos/build/04-00-gpg b/macos/build/04-00-gpg
new file mode 100755
index 00000000..304ca0aa
--- /dev/null
+++ b/macos/build/04-00-gpg
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -euo pipefail
+
+"$SHARED_DIR/tools/gpg/build"
diff --git a/macos/build/05-00-kanata b/macos/build/05-00-kanata
new file mode 100755
index 00000000..3ce5de5f
--- /dev/null
+++ b/macos/build/05-00-kanata
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+if ! command_exists kanata; then
+ log_warning "kanata not installed, skipping configuration deployment"
+ log_info "Run 'dot install kanata' to install kanata first"
+ exit 0
+fi
+
+log_step "Validating kanata configuration"
+NEW_CONFIG="$SYSTEM_DIR/config/kanata/iso-us-international.kbd"
+
+# Basic validation: check if file exists and is not empty
+if [[ ! -f "$NEW_CONFIG" ]]; then
+ log_error "Config file not found: $NEW_CONFIG"
+ exit 1
+fi
+
+if [[ ! -s "$NEW_CONFIG" ]]; then
+ log_error "Config file is empty: $NEW_CONFIG"
+ exit 1
+fi
+
+log_info "Config file validation passed"
+
+log_step "Deploying kanata configuration"
+CONFIG_DIR="$HOME/.config/kanata"
+mkdir -p "$CONFIG_DIR"
+
+cp "$NEW_CONFIG" "$CONFIG_DIR/config.kbd"
+log_success "Configuration deployed to $CONFIG_DIR/config.kbd"
+
+KANATA_PLIST="/Library/LaunchDaemons/com.kanata.plist"
+if [[ -f "$KANATA_PLIST" ]]; then
+ log_step "Restarting kanata service"
+
+ # Bootout (unload) - will fail silently if not loaded
+ sudo launchctl bootout system "$KANATA_PLIST" 2>/dev/null || true
+ sleep 0.5
+
+ # Bootstrap (load and start)
+ if sudo launchctl bootstrap system "$KANATA_PLIST" 2>/dev/null; then
+ log_success "kanata service restarted (passwordless sudo)"
+ else
+ # Try kickstart if already loaded
+ if sudo launchctl kickstart -k system/com.kanata 2>/dev/null; then
+ log_success "kanata service restarted via kickstart"
+ else
+ log_warning "Could not restart service - may need manual intervention"
+ fi
+ fi
+else
+ log_warning "kanata LaunchDaemon not found"
+ log_info "Run 'dot install kanata' to set up the launch service"
+fi
+
+log_success "kanata build completed"
diff --git a/macos/build/06-00-shell b/macos/build/06-00-shell
new file mode 100755
index 00000000..d860b194
--- /dev/null
+++ b/macos/build/06-00-shell
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -euo pipefail
+
+"$SHARED_DIR/shell/build"
diff --git a/macos/build/07-00-ghostty b/macos/build/07-00-ghostty
new file mode 100755
index 00000000..5f5091ea
--- /dev/null
+++ b/macos/build/07-00-ghostty
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Creating Ghostty config directory"
+mkdir -p "$HOME/.config/ghostty"
+
+log_step "Deploying Ghostty configuration"
+SHARED_CONFIG="$SHARED_DIR/ghostty/config/config"
+if [[ -f "$SHARED_CONFIG" ]]; then
+ cp "$SHARED_CONFIG" "$HOME/.config/ghostty/config"
+ log_info "Deployed Ghostty config to ~/.config/ghostty/config"
+else
+ log_error "Ghostty config file not found at $SHARED_CONFIG"
+ exit 1
+fi
+
+log_success "Ghostty configuration completed"
diff --git a/macos/config/amethyst/.amethyst.yml b/macos/config/amethyst/.amethyst.yml
new file mode 100644
index 00000000..fe65afbb
--- /dev/null
+++ b/macos/config/amethyst/.amethyst.yml
@@ -0,0 +1,119 @@
+# Amethyst Configuration
+
+# Modifier keys (mod1 and mod2 can be customized)
+mod1:
+ - option
+ - control
+ - command
+
+mod2:
+ - option
+ - control
+ - command
+ - shift
+
+# Layouts
+layouts:
+ - tall
+ - fullscreen
+ - 3column-left
+ - floating
+
+# Window settings
+window-margins: true
+window-margin-size: 8
+window-resize-step: 5
+
+# Focus behavior
+mouse-follows-focus: false
+focus-follows-mouse: false
+
+# Floating applications (bundle identifiers)
+floating:
+ - com.apple.systempreferences
+ - com.apple.ActivityMonitor
+
+# Keyboard shortcuts
+cycle-layout:
+ mod: mod1
+ key: t
+
+shrink-main:
+ mod: mod1
+ key: '='
+
+expand-main:
+ mod: mod1
+ key: '-'
+
+increase-main:
+ mod: mod1
+ key: '.'
+
+decrease-main:
+ mod: mod1
+ key: ','
+
+focus-ccw:
+ mod: mod1
+ key: i
+
+focus-cw:
+ mod: mod1
+ key: o
+
+swap-cw:
+ mod: mod1
+ key: n
+
+swap-main:
+ mod: mod1
+ key: enter
+
+focus-screen-1:
+ mod: mod1
+ key: m
+
+focus-screen-2:
+ mod: mod1
+ key: l
+
+select-fullscreen-layout:
+ mod: mod1
+ key: f
+
+select-wide-layout:
+ mod: mod2
+ key: s
+
+throw-space-1:
+ mod: mod2
+ key: '1'
+
+throw-space-2:
+ mod: mod2
+ key: '2'
+
+throw-space-3:
+ mod: mod2
+ key: '3'
+
+throw-space-4:
+ mod: mod2
+ key: '4'
+
+throw-space-5:
+ mod: mod2
+ key: '5'
+
+throw-space-6:
+ mod: mod2
+ key: '6'
+
+throw-space-7:
+ mod: mod2
+ key: '7'
+
+throw-space-8:
+ mod: mod2
+ key: '8'
diff --git a/macos/config/git/gitconfig.os b/macos/config/git/gitconfig.os
new file mode 100644
index 00000000..767ac9be
--- /dev/null
+++ b/macos/config/git/gitconfig.os
@@ -0,0 +1,5 @@
+# macOS-specific git configuration
+[credential "https://github.com"]
+ helper = !/opt/homebrew/bin/gh auth git-credential
+[credential "https://gist.github.com"]
+ helper = !/opt/homebrew/bin/gh auth git-credential
diff --git a/macos/config/kanata/iso-us-international.kbd b/macos/config/kanata/iso-us-international.kbd
new file mode 100644
index 00000000..ab7fe4cb
--- /dev/null
+++ b/macos/config/kanata/iso-us-international.kbd
@@ -0,0 +1,64 @@
+;; Kanata keyboard configuration
+;; Converted from kmonad config
+
+(defcfg
+ process-unmapped-keys yes
+ ;; Allow command execution (use with caution)
+ danger-enable-cmd yes
+)
+
+(defalias
+ ;; Custom Hyper key (Cmd+Ctrl+Alt all together)
+ hyp (multi lctl lalt lmet)
+
+ ;; Tap-hold aliases
+ ;; kanata uses: (tap-hold tap-timeout hold-timeout tap-action hold-action)
+ mta (tap-hold 150 150 tab @hyp)
+ rta (tap-hold 150 150 \ rmet)
+ qta (tap-hold 150 150 ' lalt)
+ ces (tap-hold 150 150 esc lctl)
+ laa (tap-hold 300 300 a lalt)
+ lac (tap-hold 300 300 ; rctl)
+ lsb (tap-hold 170 170 [ lsft)
+ rsb (tap-hold 170 170 ] rsft)
+ sid (tap-hold 300 300 spc (layer-toggle side))
+ 1cm (tap-hold 300 300 1 lmet)
+
+ ;; Browser history navigation
+ hfo (multi lalt rght)
+ hba (multi lalt left)
+
+ ;; Browser tab navigation
+ tba (multi lctl (multi lsft tab))
+ tfo (multi lctl tab)
+)
+
+(defsrc
+ esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12
+ nubs 1 2 3 4 5 6 7 8 9 0 - = bspc
+ tab q w e r t y u i o p [ ]
+ caps a s d f g h j k l ; ' \ ret
+ lsft grv z x c v b n m , . / rsft
+ lctl lmet lalt spc ralt rctl pgup up pgdn
+ left down rght
+)
+
+(deflayer main
+ esc brdn brup mctl sls dtn dnd prev pp next mute vold volu
+ = @1cm 2 3 4 5 6 7 8 9 0 - grv bspc
+ @mta q w e r t y u i o p @hyp \
+ @ces @laa s d f g h j k l @lac @qta ret ret
+ lsft lsft z x c v b n m , . / @rsb
+ caps lalt lmet @sid ralt rctl pgup up pgdn
+ left down rght
+)
+
+(deflayer side
+ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ @hba @hfo @tba @tfo _ _ _ _ _ up _ _
+ _ _ _ bspc rght ret _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ left down _ _ _ _ _
+ _ _ _ _ _ _ _ _ _
+ _ _ _
+)
diff --git a/macos/install/01-10-mise-deps-ruby b/macos/install/01-10-mise-deps-ruby
new file mode 100755
index 00000000..006b59ee
--- /dev/null
+++ b/macos/install/01-10-mise-deps-ruby
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Installing libyaml"
+
+if brew_installed libyaml; then
+ log_info "libyaml already installed"
+else
+ install_formula libyaml
+fi
+
+log_success "Ruby dependencies installed"
+log_info "You can now install Ruby versions with: mise install ruby@3.4.5"
diff --git a/macos/install/01-50-mise b/macos/install/01-50-mise
new file mode 100755
index 00000000..2a78c4fc
--- /dev/null
+++ b/macos/install/01-50-mise
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+# Check if mise is already installed
+if command_exists mise; then
+ log_info "mise is already installed, skipping"
+else
+ log_step "Installing mise via official installer"
+
+ # Download and run the mise installer
+ log_info "Downloading and running mise installer"
+ curl https://mise.run | sh
+
+ # Verify installation
+ if [[ -f "$HOME/.local/bin/mise" ]]; then
+ log_success "mise installed successfully to $HOME/.local/bin/mise"
+ else
+ log_error "mise installation failed - binary not found"
+ exit 1
+ fi
+fi
+
+# Add mise activation to shell config if not already present
+log_step "Configuring shell integration"
+
+BASHRC="$HOME/.bashrc"
+MISE_INIT='eval "$(~/.local/bin/mise activate bash)"'
+
+if [[ -f "$BASHRC" ]]; then
+ if ! grep -q "mise activate bash" "$BASHRC"; then
+ log_info "Adding mise activation to .bashrc"
+ echo "" >> "$BASHRC"
+ echo "# mise version manager" >> "$BASHRC"
+ echo "$MISE_INIT" >> "$BASHRC"
+ else
+ log_info "mise activation already in .bashrc"
+ fi
+fi
+
+log_success "mise installation completed"
+log_info "Run 'source ~/.bashrc' or start a new shell to activate mise"
diff --git a/macos/install/02-10-emacs-deps-vterm b/macos/install/02-10-emacs-deps-vterm
new file mode 100755
index 00000000..494324dd
--- /dev/null
+++ b/macos/install/02-10-emacs-deps-vterm
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Installing Emacs dependencies (cmake, libtool)"
+
+FORMULAS=("cmake" "libtool")
+
+for formula in "${FORMULAS[@]}"; do
+ if brew_installed "$formula"; then
+ log_info "$formula already installed"
+ else
+ install_formula "$formula"
+ fi
+done
+
+log_success "Emacs dependencies installed"
diff --git a/macos/install/02-20-fonts b/macos/install/02-20-fonts
new file mode 100755
index 00000000..646585ac
--- /dev/null
+++ b/macos/install/02-20-fonts
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Installing fonts via Homebrew"
+
+# Iosevka font variants
+CASKS=(
+ "font-iosevka"
+ "font-iosevka-nerd-font"
+)
+
+for cask in "${CASKS[@]}"; do
+ if brew list --cask "$cask" &>/dev/null; then
+ log_info "$cask already installed"
+ else
+ install_cask "$cask"
+ fi
+done
+
+log_success "Fonts installation completed"
+log_info "Restart applications to use the new fonts"
diff --git a/macos/install/02-50-emacs b/macos/install/02-50-emacs
new file mode 100755
index 00000000..a8f9655b
--- /dev/null
+++ b/macos/install/02-50-emacs
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Installing emacs via Homebrew"
+install_cask "emacs"
+
+log_success "Emacs installation completed"
+log_info "Run 'dot build emacs' to configure emacs"
diff --git a/macos/install/03-10-git b/macos/install/03-10-git
new file mode 100755
index 00000000..3a3dbe14
--- /dev/null
+++ b/macos/install/03-10-git
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Installing git packages via Homebrew"
+FORMULAS=("git" "git-delta" "gh")
+install_formula "${FORMULAS[@]}"
+
+log_success "Git installation completed"
+log_info "Run 'dot build git' to configure git"
diff --git a/macos/install/04-10-amethyst b/macos/install/04-10-amethyst
new file mode 100755
index 00000000..a8fc3b6f
--- /dev/null
+++ b/macos/install/04-10-amethyst
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+log_step "Installing Amethyst via Homebrew"
+CASKS=("amethyst")
+install_cask "${CASKS[@]}"
+
+log_success "Amethyst installation completed"
+log_info "Run 'dot build amethyst' to configure Amethyst"
+log_info ""
+log_warning "IMPORTANT: Grant Accessibility permissions to Amethyst"
+log_info "Go to: System Settings > Privacy & Security > Accessibility"
+log_info "Enable Amethyst to allow window management"
diff --git a/macos/install/05-10-kanata b/macos/install/05-10-kanata
new file mode 100755
index 00000000..c59226af
--- /dev/null
+++ b/macos/install/05-10-kanata
@@ -0,0 +1,237 @@
+#!/bin/bash
+
+set -euo pipefail
+
+source "$DOT_HOME/lib/macos/include.sh"
+
+# Kanata and Karabiner versions
+KANATA_VERSION="1.10.1"
+KARABINER_VERSION="6.7.0"
+
+# Check if everything is already set up
+KARABINER_DAEMON_PLIST="/Library/LaunchDaemons/org.pqrs.karabiner.virtualhiddevice.daemon.plist"
+KANATA_PLIST="/Library/LaunchDaemons/com.kanata.plist"
+
+if command_exists kanata && [[ -f "$KARABINER_DAEMON_PLIST" ]] && [[ -f "$KANATA_PLIST" ]]; then
+ INSTALLED_VERSION=$(kanata --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
+ log_success "kanata is already fully installed and configured (version: $INSTALLED_VERSION)"
+ exit 0
+fi
+
+# Install Karabiner DriverKit VirtualHIDDevice v6.7.0
+log_step "Installing Karabiner DriverKit VirtualHIDDevice v${KARABINER_VERSION}"
+
+KARABINER_PKG="Karabiner-DriverKit-VirtualHIDDevice-${KARABINER_VERSION}.pkg"
+KARABINER_URL="https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/releases/download/v${KARABINER_VERSION}/${KARABINER_PKG}"
+TEMP_DIR=$(mktemp -d)
+
+# Check if Karabiner is already installed and what version
+if [[ -d "/Applications/.Karabiner-VirtualHIDDevice-Manager.app" ]]; then
+ log_info "Karabiner driver is installed, checking version..."
+
+ # Try to get version from the app bundle
+ CURRENT_VERSION=$(defaults read "/Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "unknown")
+
+ if [[ "$CURRENT_VERSION" == "$KARABINER_VERSION" ]]; then
+ log_info "Karabiner v${KARABINER_VERSION} already installed"
+ else
+ log_warning "Karabiner v${CURRENT_VERSION} is installed, upgrading to v${KARABINER_VERSION}"
+
+ # Uninstall old version
+ log_info "Deactivating old Karabiner driver"
+ bash '/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/scripts/uninstall/deactivate_driver.sh' || true
+
+ log_info "Removing old Karabiner files (requires sudo)"
+ ensure_sudo
+ sudo bash '/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/scripts/uninstall/remove_files.sh' || true
+
+ # Download and install new version
+ log_info "Downloading Karabiner v${KARABINER_VERSION}"
+ if download_file "$KARABINER_URL" "$TEMP_DIR/$KARABINER_PKG"; then
+ log_info "Installing Karabiner driver (requires sudo)"
+ ensure_sudo
+ sudo installer -pkg "$TEMP_DIR/$KARABINER_PKG" -target /
+ log_success "Karabiner v${KARABINER_VERSION} installed"
+ else
+ log_error "Failed to download Karabiner driver"
+ exit 1
+ fi
+ fi
+else
+ # Fresh install
+ log_info "Downloading Karabiner DriverKit VirtualHIDDevice v${KARABINER_VERSION}"
+ if download_file "$KARABINER_URL" "$TEMP_DIR/$KARABINER_PKG"; then
+ log_info "Installing Karabiner driver (requires sudo)"
+ ensure_sudo
+ sudo installer -pkg "$TEMP_DIR/$KARABINER_PKG" -target /
+ log_success "Karabiner driver installed"
+ else
+ log_error "Failed to download Karabiner driver"
+ exit 1
+ fi
+fi
+
+# Activate Karabiner driver (run in background with timeout workaround)
+log_step "Activating Karabiner driver"
+# macOS doesn't have timeout command, use perl-based alternative
+perl -e 'alarm 60; exec @ARGV' /Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager activate || true
+log_info "Driver activation completed (may require approval in System Settings)"
+
+# Set up LaunchDaemon for Karabiner VirtualHIDDevice daemon
+log_step "Setting up Karabiner daemon to start on boot"
+DAEMON_PLIST="/Library/LaunchDaemons/org.pqrs.karabiner.virtualhiddevice.daemon.plist"
+
+if [[ -f "$DAEMON_PLIST" ]]; then
+ log_info "LaunchDaemon already configured"
+else
+ log_info "Creating LaunchDaemon plist (requires sudo)"
+ ensure_sudo
+
+ sudo tee "$DAEMON_PLIST" > /dev/null << 'EOF'
+
+
+
+
+ Label
+ org.pqrs.karabiner.virtualhiddevice.daemon
+ ProgramArguments
+
+ /Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-VirtualHIDDevice-Daemon.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Daemon
+
+ ProcessType
+ Interactive
+ RunAtLoad
+
+ KeepAlive
+
+ StandardOutPath
+ /var/log/karabiner-daemon.log
+ StandardErrorPath
+ /var/log/karabiner-daemon.log
+
+
+EOF
+
+ # Set correct permissions
+ sudo chmod 644 "$DAEMON_PLIST"
+ sudo chown root:wheel "$DAEMON_PLIST"
+
+ # Load the daemon
+ log_info "Loading Karabiner daemon"
+ sudo launchctl load "$DAEMON_PLIST" || true
+
+ log_success "Karabiner daemon configured to start on boot"
+fi
+
+# Clean up temp directory
+rm -rf "$TEMP_DIR"
+
+# Download and install kanata binary
+log_step "Installing kanata v${KANATA_VERSION}"
+
+KANATA_ZIP="kanata-macos-binaries-arm64-v${KANATA_VERSION}.zip"
+KANATA_URL="https://github.com/jtroo/kanata/releases/download/v${KANATA_VERSION}/${KANATA_ZIP}"
+INSTALL_DIR="$HOME/.local/bin"
+KANATA_PATH="$INSTALL_DIR/kanata"
+TEMP_KANATA=$(mktemp -d)
+
+ensure_dir "$INSTALL_DIR"
+
+log_info "Downloading kanata v${KANATA_VERSION} for ARM64"
+if download_file "$KANATA_URL" "$TEMP_KANATA/$KANATA_ZIP"; then
+ log_info "Extracting kanata binary"
+ cd "$TEMP_KANATA"
+ unzip -q "$KANATA_ZIP"
+
+ # Find the binary (might be kanata_macos_arm64 or similar)
+ BINARY=$(find . -name "kanata*" -type f | head -1)
+
+ if [[ -n "$BINARY" ]]; then
+ mv "$BINARY" "$KANATA_PATH"
+ chmod +x "$KANATA_PATH"
+ log_success "kanata installed to $KANATA_PATH"
+ else
+ log_error "Could not find kanata binary in zip"
+ exit 1
+ fi
+
+ rm -rf "$TEMP_KANATA"
+else
+ log_error "Failed to download kanata"
+ exit 1
+fi
+
+# Verify installation
+if command_exists kanata; then
+ VERSION=$(kanata --version 2>&1 | head -1)
+ log_success "kanata installation completed: $VERSION"
+else
+ log_warning "kanata installed but not in PATH. Add $INSTALL_DIR to your PATH"
+fi
+
+# Set up LaunchDaemon to run kanata on boot
+log_step "Setting up kanata to start on boot"
+KANATA_PLIST="/Library/LaunchDaemons/com.kanata.plist"
+KANATA_CONFIG="$HOME/.config/kanata/config.kbd"
+
+log_info "Creating kanata LaunchDaemon (requires sudo)"
+ensure_sudo
+
+sudo tee "$KANATA_PLIST" > /dev/null << EOF
+
+
+
+
+ Label
+ com.kanata
+ ProgramArguments
+
+ $KANATA_PATH
+ --cfg
+ $KANATA_CONFIG
+
+ RunAtLoad
+
+ KeepAlive
+
+ StandardErrorPath
+ /var/log/kanata.err
+ StandardOutPath
+ /var/log/kanata.out
+
+
+EOF
+
+sudo chmod 644 "$KANATA_PLIST"
+sudo chown root:wheel "$KANATA_PLIST"
+
+log_success "kanata LaunchDaemon created"
+
+# Set up passwordless sudo for kanata service management
+log_step "Setting up passwordless sudo for kanata management"
+SUDOERS_FILE="/etc/sudoers.d/kanata"
+
+sudo tee "$SUDOERS_FILE" > /dev/null << 'SUDOEOF'
+# Allow passwordless sudo for kanata service management
+%admin ALL=(root) NOPASSWD: /bin/launchctl bootout system /Library/LaunchDaemons/com.kanata.plist
+%admin ALL=(root) NOPASSWD: /bin/launchctl bootstrap system /Library/LaunchDaemons/com.kanata.plist
+%admin ALL=(root) NOPASSWD: /bin/launchctl kickstart -k system/com.kanata
+SUDOEOF
+
+sudo chmod 440 "$SUDOERS_FILE"
+log_success "Sudoers configured for passwordless kanata management"
+log_info "kanata will start automatically after 'dot build kanata' deploys the config"
+
+echo ""
+log_warning "IMPORTANT: Kanata requires system permissions"
+log_info "1. Approve the Karabiner extension in System Settings > Privacy & Security if needed"
+log_info "2. Restart your Mac if prompted"
+log_info "3. Grant Input Monitoring permissions to: $KANATA_PATH"
+log_info " Go to: System Settings > Privacy & Security > Input Monitoring"
+log_info "4. Run 'dot build kanata' to deploy configuration and start kanata"
+log_info ""
+log_info "Manage kanata service without password:"
+log_info " sudo launchctl bootout system /Library/LaunchDaemons/com.kanata.plist"
+log_info " sudo launchctl bootstrap system /Library/LaunchDaemons/com.kanata.plist"
+echo ""
+
diff --git a/shared/ghostty/build b/shared/ghostty/build
new file mode 100755
index 00000000..aae786f0
--- /dev/null
+++ b/shared/ghostty/build
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOT_HOME="$(dirname "$(dirname "$SCRIPT_DIR")")"
+
+# Detect OS and source appropriate include file
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ source "$DOT_HOME/lib/macos/include.sh"
+else
+ source "$DOT_HOME/lib/debian/include.sh"
+fi
+
+MODULE="ghostty"
+init_module_logging "$MODULE"
+
+log_header "Building Ghostty Configuration"
+
+# Create Ghostty config directory
+log_step "Creating Ghostty config directory"
+mkdir -p "$HOME/.config/ghostty"
+
+# Deploy Ghostty configuration
+log_step "Deploying Ghostty configuration"
+if [[ -f "$SCRIPT_DIR/config/config" ]]; then
+ cp "$SCRIPT_DIR/config/config" "$HOME/.config/ghostty/config"
+ log_info "Deployed Ghostty config to ~/.config/ghostty/config"
+else
+ log_error "Ghostty config file not found"
+ finalize_module_logging "failed"
+ exit 1
+fi
+
+log_success "Ghostty configuration completed"
+finalize_module_logging "success"
\ No newline at end of file
diff --git a/debian/tools/01-ghostty/config/config b/shared/ghostty/config/config
similarity index 66%
rename from debian/tools/01-ghostty/config/config
rename to shared/ghostty/config/config
index a005ce39..7f6f4b5b 100644
--- a/debian/tools/01-ghostty/config/config
+++ b/shared/ghostty/config/config
@@ -1,5 +1,9 @@
# Ghostty terminal configuration
+# macOS: Make left Option/Alt act as Meta key for terminal shortcuts (Alt+f, Alt+b, etc.)
+# Right Option/Alt will still produce special characters (ń, ƒ, etc.)
+macos-option-as-alt = left
+
# Cursor
cursor-style = block
cursor-style-blink = false
diff --git a/shared/ghostty/install b/shared/ghostty/install
new file mode 100755
index 00000000..994211ad
--- /dev/null
+++ b/shared/ghostty/install
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOT_HOME="$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")"
+
+source "$DOT_HOME/lib/debian/include.sh"
+
+MODULE="ghostty"
+init_module_logging "$MODULE"
+
+log_header "Installing Ghostty Terminal"
+
+# Install dependencies
+log_step "Installing build dependencies"
+PACKAGES=(
+ "libgtk-4-dev"
+ "libadwaita-1-dev"
+ "git"
+ "blueprint-compiler"
+ "gettext"
+ "libxml2-utils"
+ "libgtk4-layer-shell-dev"
+)
+
+install_packages "${PACKAGES[@]}"
+
+if ! command_exists mise; then
+ log_error "mise not found. Please install mise first"
+ log_info "Run: dot install tools/00-mise"
+ exit 0
+fi
+
+# Clone or update Ghostty repository
+log_step "Setting up Ghostty repository"
+GHOSTTY_DIR="$HOME/Repositories/ghostty"
+GHOSTTY_VERSION="v1.2.2"
+GHOSTTY_ZIG_VERSION="0.14"
+
+if [[ ! -d "$GHOSTTY_DIR" ]]; then
+ log_info "Cloning Ghostty repository"
+ git clone https://github.com/ghostty-org/ghostty "$GHOSTTY_DIR"
+else
+ log_info "Updating existing Ghostty repository"
+ cd "$GHOSTTY_DIR"
+ git fetch
+fi
+
+# Checkout specific version
+log_step "Checking out Ghostty version $GHOSTTY_VERSION"
+cd "$GHOSTTY_DIR"
+git checkout "$GHOSTTY_VERSION"
+
+# Build Ghostty
+log_step "Building Ghostty"
+log_info "Running zig build with mise (this may take a while)"
+pushd "$GHOSTTY_DIR"
+mise exec zig@$GHOSTTY_ZIG_VERSION -- zig build -p "$HOME/.local" -Doptimize=ReleaseFast
+popd
+
+# Verify installation
+if [[ -f "$HOME/.local/bin/ghostty" ]]; then
+ log_success "Ghostty installed successfully to $HOME/.local/bin/ghostty"
+else
+ log_error "Ghostty installation failed - binary not found"
+ exit 1
+fi
+
+log_success "Ghostty installation completed"
+finalize_module_logging "success"
diff --git a/shared/shell/build b/shared/shell/build
new file mode 100755
index 00000000..f16126e9
--- /dev/null
+++ b/shared/shell/build
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOT_HOME="$(dirname "$(dirname "$SCRIPT_DIR")")"
+
+# Source universal libraries (OS-agnostic)
+source "$DOT_HOME/lib/colors.sh"
+source "$DOT_HOME/lib/logging.sh"
+
+MODULE="shell"
+init_module_logging "$MODULE"
+
+log_header "Building Shell Configuration"
+
+# Deploy shell configuration files from existing plans
+log_step "Deploying shell configuration"
+
+# Bash configuration
+log_step "Installing bash configuration"
+
+# Copy bash configuration files
+if [[ -f "$SCRIPT_DIR/config/.bashrc" ]]; then
+ cp "$SCRIPT_DIR/config/.bashrc" "$HOME/.bashrc"
+ log_info "Deployed .bashrc"
+fi
+
+if [[ -f "$SCRIPT_DIR/config/.profile" ]]; then
+ cp "$SCRIPT_DIR/config/.profile" "$HOME/.profile"
+ log_info "Deployed .profile"
+fi
+
+if [[ -f "$SCRIPT_DIR/config/.bash_profile" ]]; then
+ cp "$SCRIPT_DIR/config/.bash_profile" "$HOME/.bash_profile"
+ log_info "Deployed .bash_profile"
+fi
+
+# Pry configuration
+if [[ -f "$SCRIPT_DIR/config/.pryrc" ]]; then
+ cp "$SCRIPT_DIR/config/.pryrc" "$HOME/.pryrc"
+ log_info "Deployed .pryrc"
+fi
+
+# IRB configuration
+if [[ -f "$SCRIPT_DIR/config/.irbrc" ]]; then
+ cp "$SCRIPT_DIR/config/.irbrc" "$HOME/.irbrc"
+ log_info "Deployed .irbrc"
+fi
+
+# Readline configuration
+if [[ -f "$SCRIPT_DIR/config/.inputrc" ]]; then
+ cp "$SCRIPT_DIR/config/.inputrc" "$HOME/.inputrc"
+ log_info "Deployed .inputrc"
+fi
+
+# Shared aliases
+if [[ -f "$SCRIPT_DIR/config/.aliases" ]]; then
+ cp "$SCRIPT_DIR/config/.aliases" "$HOME/.aliases"
+ log_info "Deployed .aliases"
+fi
+
+# Zsh configuration
+if [[ -f "$SCRIPT_DIR/config/.zshrc" ]]; then
+ cp "$SCRIPT_DIR/config/.zshrc" "$HOME/.zshrc"
+ log_info "Deployed .zshrc"
+fi
+
+# Deploy .profile.local example if it doesn't exist
+if [[ ! -f "$HOME/.profile.local" ]] && [[ -f "$SCRIPT_DIR/config/.profile.local.example" ]]; then
+ cp "$SCRIPT_DIR/config/.profile.local.example" "$HOME/.profile.local"
+ log_info "Deployed .profile.local from example (customize as needed)"
+fi
+
+# Bash completions
+log_step "Installing bash completions"
+mkdir -p "$HOME/.bashrc.d"
+
+# Source dot completions from top-level
+if [[ -f "$DOT_HOME/completions/dot.bash" ]]; then
+ cp "$DOT_HOME/completions/dot.bash" "$HOME/.bashrc.d/dot-completion.bash"
+ log_info "Deployed dot bash completions"
+fi
+
+# Zsh completions
+# TODO: Add proper zsh completions (bash completions don't work in zsh)
+
+log_success "Shell configuration completed"
+finalize_module_logging "success"
diff --git a/shared/shell/config/.aliases b/shared/shell/config/.aliases
new file mode 100644
index 00000000..a7b680df
--- /dev/null
+++ b/shared/shell/config/.aliases
@@ -0,0 +1,4 @@
+# ~/.aliases
+# Shared aliases for bash and zsh
+
+alias ll='ls -alF'
diff --git a/debian/shell/config/.bash_profile b/shared/shell/config/.bash_profile
similarity index 100%
rename from debian/shell/config/.bash_profile
rename to shared/shell/config/.bash_profile
diff --git a/debian/shell/config/.bashrc b/shared/shell/config/.bashrc
similarity index 83%
rename from debian/shell/config/.bashrc
rename to shared/shell/config/.bashrc
index 5b932a9b..8a09a8fa 100644
--- a/debian/shell/config/.bashrc
+++ b/shared/shell/config/.bashrc
@@ -10,11 +10,6 @@ if ! [[ "$PATH" =~ "$HOME/.local/bin:" ]]; then
PATH="$HOME/.local/bin:$PATH"
fi
-# Add dotfiles bin to PATH
-if ! [[ "$PATH" =~ "$HOME/Repositories/dotfiles/bin:" ]]; then
- PATH="$HOME/Repositories/dotfiles/bin:$PATH"
-fi
-
export PATH
# User specific aliases and functions
@@ -26,20 +21,10 @@ if [ -d ~/.bashrc.d ]; then
done
fi
-# Aliases
-alias ll='ls -alF'
-alias la='ls -A'
-alias l='ls -CF'
-alias grep='grep --color=auto'
-alias fgrep='fgrep --color=auto'
-alias egrep='egrep --color=auto'
-
-# Git aliases
-alias gs='git status'
-alias ga='git add'
-alias gc='git commit'
-alias gp='git push'
-alias gl='git pull'
+# Source shared aliases
+if [ -f "$HOME/.aliases" ]; then
+ . "$HOME/.aliases"
+fi
# History settings
HISTSIZE=10000
diff --git a/debian/shell/config/.inputrc b/shared/shell/config/.inputrc
similarity index 100%
rename from debian/shell/config/.inputrc
rename to shared/shell/config/.inputrc
diff --git a/debian/shell/config/.irbrc b/shared/shell/config/.irbrc
similarity index 100%
rename from debian/shell/config/.irbrc
rename to shared/shell/config/.irbrc
diff --git a/debian/shell/config/.profile b/shared/shell/config/.profile
similarity index 64%
rename from debian/shell/config/.profile
rename to shared/shell/config/.profile
index 0dac4ac4..904f9d11 100644
--- a/debian/shell/config/.profile
+++ b/shared/shell/config/.profile
@@ -1,9 +1,16 @@
# ~/.profile
+# Add user's local bin to PATH
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi
+# Add dotfiles bin to PATH
+DOTFILES_BIN="$HOME/Repositories/dotfiles/bin"
+if [ -d "$DOTFILES_BIN" ] && ! echo "$PATH" | grep -q "$DOTFILES_BIN" ; then
+ PATH="$DOTFILES_BIN:$PATH"
+fi
+
# Set default editor
export EDITOR=emacs
export VISUAL=emacs
diff --git a/debian/shell/config/.profile.local.example b/shared/shell/config/.profile.local.example
similarity index 100%
rename from debian/shell/config/.profile.local.example
rename to shared/shell/config/.profile.local.example
diff --git a/debian/shell/config/.pryrc b/shared/shell/config/.pryrc
similarity index 100%
rename from debian/shell/config/.pryrc
rename to shared/shell/config/.pryrc
diff --git a/shared/shell/config/.zshrc b/shared/shell/config/.zshrc
new file mode 100644
index 00000000..cdff382a
--- /dev/null
+++ b/shared/shell/config/.zshrc
@@ -0,0 +1,44 @@
+# ~/.zshrc
+
+# Source common profile (PATH, EDITOR, etc.)
+if [ -f "$HOME/.profile" ]; then
+ source "$HOME/.profile"
+fi
+
+# Source shared aliases
+if [ -f "$HOME/.aliases" ]; then
+ source "$HOME/.aliases"
+fi
+
+# History settings
+HISTSIZE=10000
+SAVEHIST=20000
+HISTFILE=~/.zsh_history
+setopt SHARE_HISTORY
+setopt HIST_IGNORE_DUPS
+setopt HIST_IGNORE_SPACE
+
+# Zsh completions
+autoload -Uz compinit
+compinit
+
+# Note: Bash completions don't work in zsh
+# TODO: Add proper zsh completions later
+
+# Git prompt (using vcs_info)
+autoload -Uz vcs_info
+precmd_vcs_info() { vcs_info }
+precmd_functions+=( precmd_vcs_info )
+setopt prompt_subst
+zstyle ':vcs_info:git:*' formats '[%b%u%c] '
+zstyle ':vcs_info:*' check-for-changes true
+zstyle ':vcs_info:*' unstagedstr '!'
+zstyle ':vcs_info:*' stagedstr '+'
+
+# Colorful prompt with git info
+PROMPT='%F{yellow}%n%f%F{cyan}@%f%F{magenta}%m%f: %F{green}%~%f %F{magenta}${vcs_info_msg_0_}%f%F{cyan}%#%f '
+
+# Tool version manager
+if [ -f ~/.local/bin/mise ]; then
+ eval "$(~/.local/bin/mise activate zsh)"
+fi
diff --git a/debian/tools/emacs/config/config.org b/shared/tools/emacs/config/config.org
similarity index 98%
rename from debian/tools/emacs/config/config.org
rename to shared/tools/emacs/config/config.org
index c0a5ec88..bb5df376 100644
--- a/debian/tools/emacs/config/config.org
+++ b/shared/tools/emacs/config/config.org
@@ -138,12 +138,14 @@ Different machines get different values.
#+begin_src emacs-lisp
(defvar vik/big-font-size
(cond
+ ((eq system-type 'darwin) 180) ;; macOS needs larger due to scaling
((string= (getenv "DESKTOP") "1") 180)
((string= (getenv "LAPTOP") "2") 170)
(t 170)))
(defvar vik/default-font-size
(cond
+ ((eq system-type 'darwin) 180) ;; macOS needs larger due to scaling
((string= (getenv "DESKTOP") "1") 110)
((string= (getenv "LAPTOP") "2") 150)
(t 110)))
@@ -753,11 +755,22 @@ Use =C-h .= to look up documentation of thing at point.
(setq eldoc-echo-area-prefer-doc-buffer t)
#+end_src
** Mise
+Shared mise configuration:
#+begin_src elisp
(use-package mise
:ensure t
:hook (after-init . global-mise-mode))
#+end_src
+
+macOS-specific PATH and mise executable:
+#+begin_src elisp :tangle (if (eq system-type 'darwin) "yes" "no")
+(use-package exec-path-from-shell
+ :ensure t
+ :config
+ (exec-path-from-shell-initialize))
+
+(setq mise-executable (expand-file-name "~/.local/bin/mise"))
+#+end_src
** Eglot
Attaches eglot to every programming language buffer:
#+begin_src emacs-lisp
@@ -985,6 +998,10 @@ Attaches eglot to every programming language buffer:
(with-eval-after-load 'dired
(define-key dired-mode-map (kbd "e") 'eshell))
+;; Use GNU ls from coreutils on macOS
+(when (eq system-type 'darwin)
+ (setq insert-directory-program "/opt/homebrew/bin/gls"))
+
(setq dired-listing-switches "-lah --group-directories-first")
#+end_src
diff --git a/debian/tools/emacs/config/early-init.el b/shared/tools/emacs/config/early-init.el
similarity index 100%
rename from debian/tools/emacs/config/early-init.el
rename to shared/tools/emacs/config/early-init.el
diff --git a/debian/tools/emacs/config/init.el b/shared/tools/emacs/config/init.el
similarity index 100%
rename from debian/tools/emacs/config/init.el
rename to shared/tools/emacs/config/init.el
diff --git a/shared/tools/git/build b/shared/tools/git/build
new file mode 100755
index 00000000..e0d0ef25
--- /dev/null
+++ b/shared/tools/git/build
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOT_HOME="$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")"
+
+# Source universal libraries (OS-agnostic)
+source "$DOT_HOME/lib/colors.sh"
+source "$DOT_HOME/lib/logging.sh"
+
+MODULE="git"
+init_module_logging "$MODULE"
+
+log_header "Building Git Configuration"
+
+log_step "Installing Git configuration"
+
+# Copy .gitconfig file
+if [[ -f "$SCRIPT_DIR/config/.gitconfig" ]]; then
+ cp "$SCRIPT_DIR/config/.gitconfig" "$HOME/.gitconfig"
+ log_success "Git configuration installed"
+else
+ log_warning "Git configuration file not found"
+fi
+
+# Copy global gitignore file
+if [[ -f "$SCRIPT_DIR/config/ignore" ]]; then
+ mkdir -p "$HOME/.config/git"
+ cp "$SCRIPT_DIR/config/ignore" "$HOME/.config/git/ignore"
+ log_success "Global gitignore installed"
+else
+ log_warning "Global gitignore file not found"
+fi
+
+log_step "Installing git scripts"
+
+# Create scripts directory
+mkdir -p "$HOME/.local/bin"
+
+# Copy git utility scripts
+if [[ -d "$SCRIPT_DIR/scripts" ]]; then
+ for script in "$SCRIPT_DIR/scripts"/*; do
+ if [[ -f "$script" ]]; then
+ script_name=$(basename "$script")
+ cp "$script" "$HOME/.local/bin/$script_name"
+ chmod +x "$HOME/.local/bin/$script_name"
+ log_info "Installed script: $script_name"
+ fi
+ done
+fi
+
+log_success "Git configuration build completed"
+finalize_module_logging "success"
diff --git a/debian/tools/git/config/.gitconfig b/shared/tools/git/config/.gitconfig
similarity index 81%
rename from debian/tools/git/config/.gitconfig
rename to shared/tools/git/config/.gitconfig
index dbb0d23c..9c859cb4 100644
--- a/debian/tools/git/config/.gitconfig
+++ b/shared/tools/git/config/.gitconfig
@@ -2,10 +2,8 @@
email = vikdotdev@gmail.com
name = Viktor Habchak
signingkey = 7392D21113383CBB
-[credential "https://github.com"]
- helper = !/usr/bin/gh auth git-credential
-[credential "https://gist.github.com"]
- helper = !/usr/bin/gh auth git-credential
+[include]
+ path = ~/.gitconfig.os
[alias]
ci = commit
st = status
diff --git a/debian/tools/git/config/ignore b/shared/tools/git/config/ignore
similarity index 100%
rename from debian/tools/git/config/ignore
rename to shared/tools/git/config/ignore
diff --git a/debian/tools/git/scripts/git-branch-files-changed b/shared/tools/git/scripts/git-branch-files-changed
similarity index 100%
rename from debian/tools/git/scripts/git-branch-files-changed
rename to shared/tools/git/scripts/git-branch-files-changed
diff --git a/debian/tools/git/scripts/git-default-branch b/shared/tools/git/scripts/git-default-branch
similarity index 100%
rename from debian/tools/git/scripts/git-default-branch
rename to shared/tools/git/scripts/git-default-branch
diff --git a/debian/tools/git/scripts/git-deploy b/shared/tools/git/scripts/git-deploy
similarity index 100%
rename from debian/tools/git/scripts/git-deploy
rename to shared/tools/git/scripts/git-deploy
diff --git a/debian/tools/git/scripts/git-log-changed b/shared/tools/git/scripts/git-log-changed
similarity index 100%
rename from debian/tools/git/scripts/git-log-changed
rename to shared/tools/git/scripts/git-log-changed
diff --git a/debian/tools/git/scripts/git-nuke b/shared/tools/git/scripts/git-nuke
similarity index 100%
rename from debian/tools/git/scripts/git-nuke
rename to shared/tools/git/scripts/git-nuke
diff --git a/debian/tools/git/scripts/git-summary b/shared/tools/git/scripts/git-summary
similarity index 100%
rename from debian/tools/git/scripts/git-summary
rename to shared/tools/git/scripts/git-summary
diff --git a/shared/tools/gpg/build b/shared/tools/gpg/build
new file mode 100755
index 00000000..0f53bead
--- /dev/null
+++ b/shared/tools/gpg/build
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOT_HOME="$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")"
+
+# Source universal libraries (OS-agnostic)
+source "$DOT_HOME/lib/colors.sh"
+source "$DOT_HOME/lib/logging.sh"
+source "$DOT_HOME/lib/utils.sh"
+
+MODULE="gpg"
+init_module_logging "$MODULE"
+
+log_header "Building GPG Configuration"
+
+log_step "Installing GPG agent configuration"
+
+# Create GPG directory if it doesn't exist
+mkdir -p "$HOME/.gnupg"
+
+# Set proper permissions for GPG directory
+chmod 700 "$HOME/.gnupg"
+
+# Copy GPG agent configuration
+if [[ -f "$SCRIPT_DIR/config/gpg-agent.conf" ]]; then
+ cp "$SCRIPT_DIR/config/gpg-agent.conf" "$HOME/.gnupg/gpg-agent.conf"
+ chmod 600 "$HOME/.gnupg/gpg-agent.conf"
+
+ # Restart GPG agent to pick up new config
+ if command_exists gpgconf; then
+ log_info "Reloading GPG agent configuration"
+ gpgconf --reload gpg-agent || log_warning "Failed to reload GPG agent"
+ fi
+fi
+
+log_success "GPG configuration build completed"
+finalize_module_logging "success"
diff --git a/debian/tools/gpg/config/gpg-agent.conf b/shared/tools/gpg/config/gpg-agent.conf
similarity index 100%
rename from debian/tools/gpg/config/gpg-agent.conf
rename to shared/tools/gpg/config/gpg-agent.conf
diff --git a/debian/tools/00-mise/config/mise.toml b/shared/tools/mise/config/mise.toml
similarity index 100%
rename from debian/tools/00-mise/config/mise.toml
rename to shared/tools/mise/config/mise.toml