From f1cea04489957a712338553c8000c4866ee3ec34 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 22:50:33 +0100 Subject: [PATCH 01/10] Refactor bootstrap, split AI packages, and fix macOS compatibility issues --- Brewfile | 12 ++++++------ Brewfile.ai | 12 ++++++++++++ bootstrap.sh | 35 ++++++++++++++++++++++++++++------ brew-hooks/stats.sh | 23 ++++++++++++++++++++++ home/.bin/toggle-btop-theme | 5 +++++ home/.exports | 9 +++++++++ home/.ssh/config | 3 +++ preferences/iTerm Profile.json | 10 ---------- 8 files changed, 87 insertions(+), 22 deletions(-) create mode 100755 brew-hooks/stats.sh delete mode 100644 preferences/iTerm Profile.json diff --git a/Brewfile b/Brewfile index 327a4ba7..c12494f6 100644 --- a/Brewfile +++ b/Brewfile @@ -21,6 +21,7 @@ brew "ripgrep" # Fast grep brew "shellcheck" # Shell script linting brew "tmux" # Terminal multiplexer brew "terminal-notifier" if OS.mac? # macOS notifications +brew "mas" if OS.mac? # Mac App Store CLI brew "vim" # Text editor brew "yazi" # Terminal file manager brew "zellij" # Terminal workspace @@ -53,8 +54,6 @@ end # Python Libraries brew "numpy" # IAP tunnel perf (gcloud) -# AI/ML -brew "whisper-cpp" # Local speech-to-text # Utilities brew "ffmpeg" @@ -62,9 +61,8 @@ brew "scc" # Code line counter brew "testdisk" # Data recovery # Casks - Dev Tools -cask "codex" cask "iterm2" # Requires: post-install config -cask "lm-studio" + # Casks - Apps cask "google-chrome" @@ -79,8 +77,10 @@ cask "whatsapp" # Casks - Fonts cask "font-jetbrains-mono-nerd-font" -# Casks - Networking -cask "tailscale-app" # Mesh VPN (event bus, openclaw) # Casks - Cloud cask "gcloud-cli" # Requires: gcloud init + +# Mac App Store Apps +mas "Amphetamine", id: 937984704 if OS.mac? +mas "Infuse", id: 1136220934 if OS.mac? diff --git a/Brewfile.ai b/Brewfile.ai index f85a2c33..5a813678 100644 --- a/Brewfile.ai +++ b/Brewfile.ai @@ -4,6 +4,18 @@ # Claude Code cask "claude" +# OpenAI Terminal Agent +cask "codex" + +# Local LLM runner +cask "lm-studio" + +# Local speech-to-text +brew "whisper-cpp" + +# Tailscale Mesh VPN (event bus, openclaw) +cask "tailscale-app" + # OpenClaw Agent Tools brew "spotify_player" # Spotify CLI (OAuth device flow) brew "gogcli" # Google Suite CLI diff --git a/bootstrap.sh b/bootstrap.sh index dce6313f..88896d4f 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -15,6 +15,16 @@ cd "$(dirname "${BASH_SOURCE}")"; GATEWAY_HOST="mac-mini.tailac7b3c.ts.net" INSTALL_AI="" +# Set up Homebrew environment variables +if [[ "$(uname)" == "Darwin" ]]; then + if [[ -f "/opt/homebrew/bin/brew" ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [[ -f "/usr/local/bin/brew" ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi +fi + + # ============================================================================== # Functions # ============================================================================== @@ -47,6 +57,14 @@ set_default_shell() { return 0 fi + # chsh requires manual password entry on macOS unless run as root. + # Skip in non-interactive sessions to avoid hanging. + if [[ ! -t 0 ]]; then + echo "Warning: Cannot change default shell to zsh automatically in non-interactive mode." + echo " Please run manually: chsh -s $zsh_path" + return 0 + fi + echo "Setting default shell to zsh..." if chsh -s "$zsh_path" 2>/dev/null; then echo "Default shell set to zsh" @@ -368,9 +386,9 @@ install_tmux_plugin_manager() { mkdir -p "$tpm_dir" git clone https://github.com/tmux-plugins/tpm "$tpm_dir" - # Source tmux config if tmux is installed - if command -v tmux >/dev/null 2>&1 && [[ -e "$HOME/.tmux.conf" ]]; then - tmux source "$HOME/.tmux.conf" + # Source tmux config if tmux is installed and running + if command -v tmux >/dev/null 2>&1 && tmux info &>/dev/null && [[ -e "$HOME/.tmux.conf" ]]; then + tmux source "$HOME/.tmux.conf" || true fi } @@ -488,9 +506,9 @@ prompt_ai_install() { return 0 fi - # Non-interactive: default to install + # Non-interactive: default to skip if [[ ! -t 0 ]]; then - INSTALL_AI=true + INSTALL_AI=false return 0 fi @@ -809,6 +827,11 @@ install_packages() { 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)" + if [[ -f "/opt/homebrew/bin/brew" ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [[ -f "/usr/local/bin/brew" ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi fi install_brew_packages @@ -1072,7 +1095,7 @@ install_yazi_flavor() { # Install catppuccin-mocha flavor if not present if ! $ya_cmd pkg list 2>/dev/null | grep -q "catppuccin-mocha"; then echo "Installing yazi catppuccin-mocha flavor..." - $ya_cmd pkg add yazi-rs/flavors:catppuccin-mocha + $ya_cmd pkg add yazi-rs/flavors:catppuccin-mocha || true fi } diff --git a/brew-hooks/stats.sh b/brew-hooks/stats.sh new file mode 100755 index 00000000..76fd09a4 --- /dev/null +++ b/brew-hooks/stats.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Post-install hook for Stats app +# Imports Stats preferences from dotfiles + +set -euo pipefail + +dotfiles_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +plist_src="$dotfiles_dir/preferences/Stats.plist" + +if [[ ! -f "$plist_src" ]]; then + exit 0 +fi + +# Only run on macOS +if [[ "$(uname)" != "Darwin" ]]; then + exit 0 +fi + +echo "Importing Stats preferences..." +# Close Stats app if running to ensure it picks up the new config on restart +killall Stats 2>/dev/null || true + +defaults import eu.exelban.Stats "$plist_src" diff --git a/home/.bin/toggle-btop-theme b/home/.bin/toggle-btop-theme index aea9c5f6..2150e170 100755 --- a/home/.bin/toggle-btop-theme +++ b/home/.bin/toggle-btop-theme @@ -30,6 +30,11 @@ else fi # Update btop config (macOS sed requires '' after -i; Linux would use -i alone) +if [[ -L "$BTOP_CONF" ]]; then + TARGET="$(readlink "$BTOP_CONF")" + rm -f "$BTOP_CONF" + cp "$TARGET" "$BTOP_CONF" +fi sed -i '' "s/^color_theme = .*/color_theme = \"$THEME\"/" "$BTOP_CONF" # Signal btop to reload config (if running) diff --git a/home/.exports b/home/.exports index 7f744e5c..464f731a 100644 --- a/home/.exports +++ b/home/.exports @@ -7,6 +7,15 @@ # GEMINI_API_KEY - Required for Gemini API access # AGENT_EVENT_BUS_URL - URL for event bus MCP server (e.g., https://host/agent-event-bus/mcp) +# Set up Homebrew PATH for macOS +if [[ "$(uname)" == "Darwin" ]]; then + if [[ -f "/opt/homebrew/bin/brew" ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [[ -f "/usr/local/bin/brew" ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi +fi + # PATH configuration if [ -d "$HOME/.local/bin" ]; then export PATH="$HOME/.local/bin:$PATH" diff --git a/home/.ssh/config b/home/.ssh/config index 8515073b..a784feff 100644 --- a/home/.ssh/config +++ b/home/.ssh/config @@ -1,7 +1,10 @@ +Include ~/.ssh/config.local + Host speck-vm HostName speck-vm IdentityFile ~/.ssh/google_compute_engine + Host * IgnoreUnknown UseKeychain AddKeysToAgent yes diff --git a/preferences/iTerm Profile.json b/preferences/iTerm Profile.json deleted file mode 100644 index 6322a5b0..00000000 --- a/preferences/iTerm Profile.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Profiles" : [ - { - "Use Separate Colors for Light and Dark Mode" : false, - "Name" : "Default", - "Rewritable" : true, - "Guid" : "45085C67-FC17-45BF-8B30-93B9975B64E3" - } - ] -} \ No newline at end of file From a1b313c75bdeda92dae2f9c86f341d9d285493eb Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 22:52:51 +0100 Subject: [PATCH 02/10] Fix npm update command failure during bootstrap --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 88896d4f..47cddb81 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -892,7 +892,7 @@ update_packages() { # Update global npm packages if command -v npm >/dev/null 2>&1; then echo "Updating global npm packages..." - npm update -g + npm update -g || true fi # Update cargo packages From ccd328a7ebded8bec0ee9b17f5752f642fc069b0 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 22:58:47 +0100 Subject: [PATCH 03/10] Configure NPM registry credentials automatically if logged in to gcloud --- bootstrap.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/bootstrap.sh b/bootstrap.sh index 47cddb81..da106def 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1166,6 +1166,32 @@ install_claude_mcp_servers() { fi } +configure_npm_registry() { + # Skip if ~/.npmrc already contains the registry auth configurations + if [[ -f "$HOME/.npmrc" ]] && grep -q "ah-3p-staging-npm" "$HOME/.npmrc" 2>/dev/null; then + return 0 + fi + + # Require gcloud and npm to be installed + if ! command -v gcloud >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then + return 0 + fi + + # Check if user has an active gcloud account + local active_account + active_account=$(gcloud config get-value account 2>/dev/null) || return 0 + if [[ -z "$active_account" ]]; then + return 0 + fi + + echo "Configuring NPM registry credentials via gcloud..." + if gcloud artifacts print-settings npm --project=artifact-foundry-prod --repository=ah-3p-staging-npm --location=us >> "$HOME/.npmrc" 2>/dev/null; then + echo "NPM registry credentials configured in ~/.npmrc" + else + echo " Warning: Failed to configure NPM credentials automatically" + fi +} + sync_dotfiles() { # Set default shell to zsh set_default_shell @@ -1224,6 +1250,9 @@ sync_dotfiles() { install_launch_agents cleanup_legacy_cron + # Configure NPM registry auth if authenticated with gcloud + configure_npm_registry + # Reload zsh configuration if [[ -n "${ZSH_VERSION:-}" ]]; then source ~/.zshrc 2>/dev/null || echo "Restart your terminal or run: source ~/.zshrc" From 0cab53756b3f3f89bc027cb9709a49cab4c633c6 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 23:01:03 +0100 Subject: [PATCH 04/10] Optimize package update flow (run brew update before bundle, remove global upgrade) --- bootstrap.sh | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index da106def..45c54e2e 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -832,6 +832,9 @@ install_packages() { elif [[ -f "/usr/local/bin/brew" ]]; then eval "$(/usr/local/bin/brew shellenv)" fi + else + echo "Updating Homebrew..." + brew update fi install_brew_packages @@ -853,15 +856,6 @@ install_packages() { update_packages() { if [[ "$(uname)" == "Darwin" ]]; then - if command -v brew >/dev/null 2>&1; then - echo "Updating Homebrew and upgrading packages..." - brew update - brew upgrade - brew cleanup - else - echo "Homebrew not installed, skipping" - fi - if command -v softwareupdate >/dev/null 2>&1; then echo "Checking for macOS software updates..." softwareupdate --list From 584055f609ade74147bed9812207ed7361a5b861 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 23:07:09 +0100 Subject: [PATCH 05/10] Updating prefs. --- preferences/iTerm.json | 630 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 preferences/iTerm.json diff --git a/preferences/iTerm.json b/preferences/iTerm.json new file mode 100644 index 00000000..e34ec751 --- /dev/null +++ b/preferences/iTerm.json @@ -0,0 +1,630 @@ +{ + "Ansi 7 Color (Light)" : { + "Red Component" : 0.65098039215686276, + "Color Space" : "sRGB", + "Blue Component" : 0.78431372549019607, + "Alpha Component" : 1, + "Green Component" : 0.67843137254901964 + }, + "Ansi 15 Color (Light)" : { + "Red Component" : 0.72941176470588232, + "Color Space" : "sRGB", + "Blue Component" : 0.87058823529411766, + "Alpha Component" : 1, + "Green Component" : 0.76078431372549016 + }, + "Ansi 2 Color (Light)" : { + "Red Component" : 0.65098039215686276, + "Color Space" : "sRGB", + "Blue Component" : 0.63137254901960782, + "Alpha Component" : 1, + "Green Component" : 0.8901960784313725 + }, + "Bold Color" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Ansi 1 Color (Dark)" : { + "Red Component" : 0.95294117647058818, + "Color Space" : "sRGB", + "Blue Component" : 0.6588235294117647, + "Alpha Component" : 1, + "Green Component" : 0.54509803921568623 + }, + "Use Bright Bold" : true, + "Ansi 9 Color (Light)" : { + "Red Component" : 0.95294117647058818, + "Color Space" : "sRGB", + "Blue Component" : 0.59999999999999998, + "Alpha Component" : 1, + "Green Component" : 0.46666666666666667 + }, + "Ansi 8 Color (Dark)" : { + "Red Component" : 0.34509803921568627, + "Color Space" : "sRGB", + "Blue Component" : 0.4392156862745098, + "Alpha Component" : 1, + "Green Component" : 0.35686274509803922 + }, + "Background Color" : { + "Red Component" : 0.11764705882352941, + "Color Space" : "sRGB", + "Blue Component" : 0.1803921568627451, + "Alpha Component" : 1, + "Green Component" : 0.11764705882352941 + }, + "Columns" : 80, + "Ansi 8 Color" : { + "Red Component" : 0.34509803921568627, + "Color Space" : "sRGB", + "Blue Component" : 0.4392156862745098, + "Alpha Component" : 1, + "Green Component" : 0.35686274509803922 + }, + "Right Option Key Sends" : 0, + "Ansi 4 Color (Light)" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.98039215686274506, + "Alpha Component" : 1, + "Green Component" : 0.70588235294117652 + }, + "Blinking Cursor" : false, + "Selected Text Color (Light)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Selected Text Color (Dark)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Ansi 3 Color (Dark)" : { + "Red Component" : 0.97647058823529409, + "Color Space" : "sRGB", + "Blue Component" : 0.68627450980392157, + "Alpha Component" : 1, + "Green Component" : 0.88627450980392153 + }, + "Keyboard Map" : { + + }, + "Visual Bell" : true, + "Cursor Text Color" : { + "Red Component" : 0.11764705882352941, + "Color Space" : "sRGB", + "Blue Component" : 0.1803921568627451, + "Alpha Component" : 1, + "Green Component" : 0.11764705882352941 + }, + "Scrollback Lines" : 1000, + "Selection Color (Light)" : { + "Red Component" : 0.34509803921568627, + "Color Space" : "sRGB", + "Blue Component" : 0.4392156862745098, + "Alpha Component" : 1, + "Green Component" : 0.35686274509803922 + }, + "Ansi 0 Color" : { + "Red Component" : 0.27058823529411763, + "Color Space" : "sRGB", + "Blue Component" : 0.35294117647058826, + "Alpha Component" : 1, + "Green Component" : 0.27843137254901962 + }, + "Match Background Color (Dark)" : { + "Red Component" : 0.99697142839431763, + "Color Space" : "P3", + "Blue Component" : 0.32116127014160156, + "Alpha Component" : 1, + "Green Component" : 0.98600882291793823 + }, + "Ansi 11 Color (Light)" : { + "Red Component" : 0.92156862745098034, + "Color Space" : "sRGB", + "Blue Component" : 0.56862745098039214, + "Alpha Component" : 1, + "Green Component" : 0.82745098039215681 + }, + "Ansi 5 Color (Dark)" : { + "Red Component" : 0.96078431372549022, + "Color Space" : "sRGB", + "Blue Component" : 0.90588235294117647, + "Alpha Component" : 1, + "Green Component" : 0.76078431372549016 + }, + "Cursor Text Color (Light)" : { + "Red Component" : 0.11764705882352941, + "Color Space" : "sRGB", + "Blue Component" : 0.1803921568627451, + "Alpha Component" : 1, + "Green Component" : 0.11764705882352941 + }, + "Silence Bell" : false, + "Rows" : 25, + "Draw Powerline Glyphs" : true, + "Guid" : "F28ADFE5-D4C0-442B-BFD2-429D0C29B31D", + "Ansi 14 Color (Dark)" : { + "Red Component" : 0.41960784313725491, + "Color Space" : "sRGB", + "Blue Component" : 0.792156862745098, + "Alpha Component" : 1, + "Green Component" : 0.84313725490196079 + }, + "Ansi 15 Color (Dark)" : { + "Red Component" : 0.72941176470588232, + "Color Space" : "sRGB", + "Blue Component" : 0.87058823529411766, + "Alpha Component" : 1, + "Green Component" : 0.76078431372549016 + }, + "Ansi 0 Color (Dark)" : { + "Red Component" : 0.27058823529411763, + "Color Space" : "sRGB", + "Blue Component" : 0.35294117647058826, + "Alpha Component" : 1, + "Green Component" : 0.27843137254901962 + }, + "Ambiguous Double Width" : false, + "Option Key Sends" : 2, + "Ansi 3 Color" : { + "Red Component" : 0.97647058823529409, + "Color Space" : "sRGB", + "Blue Component" : 0.68627450980392157, + "Alpha Component" : 1, + "Green Component" : 0.88627450980392153 + }, + "Window Type" : 0, + "BM Growl" : true, + "Prompt Before Closing 2" : false, + "Command" : "", + "Left Option Key Changeable" : false, + "Selected Text Color" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Ansi 14 Color (Light)" : { + "Red Component" : 0.41960784313725491, + "Color Space" : "sRGB", + "Blue Component" : 0.792156862745098, + "Alpha Component" : 1, + "Green Component" : 0.84313725490196079 + }, + "Cursor Guide Color (Dark)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 0.070000000000000007, + "Green Component" : 0.83921568627450982 + }, + "Send Code When Idle" : false, + "Ansi 6 Color" : { + "Red Component" : 0.58039215686274515, + "Color Space" : "sRGB", + "Blue Component" : 0.83529411764705885, + "Alpha Component" : 1, + "Green Component" : 0.88627450980392153 + }, + "Jobs to Ignore" : [ + "rlogin", + "ssh", + "slogin", + "telnet" + ], + "Badge Color (Dark)" : { + "Red Component" : 0.92929404973983765, + "Color Space" : "P3", + "Blue Component" : 0.13960540294647217, + "Alpha Component" : 0.5, + "Green Component" : 0.25479039549827576 + }, + "Cursor Color" : { + "Red Component" : 0.96078431372549022, + "Color Space" : "sRGB", + "Blue Component" : 0.86274509803921573, + "Alpha Component" : 1, + "Green Component" : 0.8784313725490196 + }, + "Vertical Spacing" : 1, + "Disable Window Resizing" : true, + "Close Sessions On End" : true, + "Selection Color (Dark)" : { + "Red Component" : 0.34509803921568627, + "Color Space" : "sRGB", + "Blue Component" : 0.4392156862745098, + "Alpha Component" : 1, + "Green Component" : 0.35686274509803922 + }, + "Default Bookmark" : "No", + "Foreground Color (Light)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Custom Command" : "No", + "Background Color (Dark)" : { + "Red Component" : 0.11764705882352941, + "Color Space" : "sRGB", + "Blue Component" : 0.1803921568627451, + "Alpha Component" : 1, + "Green Component" : 0.11764705882352941 + }, + "Ansi 9 Color" : { + "Red Component" : 0.95294117647058818, + "Color Space" : "sRGB", + "Blue Component" : 0.59999999999999998, + "Alpha Component" : 1, + "Green Component" : 0.46666666666666667 + }, + "Ansi 14 Color" : { + "Red Component" : 0.41960784313725491, + "Color Space" : "sRGB", + "Blue Component" : 0.792156862745098, + "Alpha Component" : 1, + "Green Component" : 0.84313725490196079 + }, + "Flashing Bell" : false, + "Use Italic Font" : true, + "Ansi 13 Color (Dark)" : { + "Red Component" : 0.94901960784313721, + "Color Space" : "sRGB", + "Blue Component" : 0.87058823529411766, + "Alpha Component" : 1, + "Green Component" : 0.68235294117647061 + }, + "Cursor Guide Color (Light)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 0.070000000000000007, + "Green Component" : 0.83921568627450982 + }, + "Ansi 12 Color" : { + "Red Component" : 0.45490196078431372, + "Color Space" : "sRGB", + "Blue Component" : 0.9882352941176471, + "Alpha Component" : 1, + "Green Component" : 0.6588235294117647 + }, + "Ansi 10 Color (Light)" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.54509803921568623, + "Alpha Component" : 1, + "Green Component" : 0.84705882352941175 + }, + "Non-ASCII Anti Aliased" : true, + "Ansi 10 Color" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.54509803921568623, + "Alpha Component" : 1, + "Green Component" : 0.84705882352941175 + }, + "Foreground Color" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Link Color (Light)" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.92156862745098034, + "Alpha Component" : 1, + "Green Component" : 0.86274509803921573 + }, + "Description" : "Default", + "Match Background Color" : { + "Red Component" : 0.99697142839431763, + "Color Space" : "P3", + "Blue Component" : 0.32116127014160156, + "Alpha Component" : 1, + "Green Component" : 0.98600882291793823 + }, + "Ansi 7 Color (Dark)" : { + "Red Component" : 0.65098039215686276, + "Color Space" : "sRGB", + "Blue Component" : 0.78431372549019607, + "Alpha Component" : 1, + "Green Component" : 0.67843137254901964 + }, + "Sync Title" : false, + "Ansi 1 Color" : { + "Red Component" : 0.95294117647058818, + "Color Space" : "sRGB", + "Blue Component" : 0.6588235294117647, + "Alpha Component" : 1, + "Green Component" : 0.54509803921568623 + }, + "Name" : "Evan", + "Blink Allowed" : true, + "Transparency" : 0, + "Horizontal Spacing" : 1, + "Cursor Color (Dark)" : { + "Red Component" : 0.96078431372549022, + "Color Space" : "sRGB", + "Blue Component" : 0.86274509803921573, + "Alpha Component" : 1, + "Green Component" : 0.8784313725490196 + }, + "Ansi 2 Color (Dark)" : { + "Red Component" : 0.65098039215686276, + "Color Space" : "sRGB", + "Blue Component" : 0.63137254901960782, + "Alpha Component" : 1, + "Green Component" : 0.8901960784313725 + }, + "Ansi 9 Color (Dark)" : { + "Red Component" : 0.95294117647058818, + "Color Space" : "sRGB", + "Blue Component" : 0.59999999999999998, + "Alpha Component" : 1, + "Green Component" : 0.46666666666666667 + }, + "Badge Color" : { + "Red Component" : 0.92929404973983765, + "Color Space" : "P3", + "Blue Component" : 0.13960540294647217, + "Alpha Component" : 0.5, + "Green Component" : 0.25479039549827576 + }, + "Ansi 13 Color (Light)" : { + "Red Component" : 0.94901960784313721, + "Color Space" : "sRGB", + "Blue Component" : 0.87058823529411766, + "Alpha Component" : 1, + "Green Component" : 0.68235294117647061 + }, + "Idle Code" : 0, + "Ansi 4 Color" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.98039215686274506, + "Alpha Component" : 1, + "Green Component" : 0.70588235294117652 + }, + "Bold Color (Dark)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Screen" : -1, + "Ansi 4 Color (Dark)" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.98039215686274506, + "Alpha Component" : 1, + "Green Component" : 0.70588235294117652 + }, + "Cursor Text Color (Dark)" : { + "Red Component" : 0.11764705882352941, + "Color Space" : "sRGB", + "Blue Component" : 0.1803921568627451, + "Alpha Component" : 1, + "Green Component" : 0.11764705882352941 + }, + "Selection Color" : { + "Red Component" : 0.34509803921568627, + "Color Space" : "sRGB", + "Blue Component" : 0.4392156862745098, + "Alpha Component" : 1, + "Green Component" : 0.35686274509803922 + }, + "Use Non-ASCII Font" : false, + "Badge Color (Light)" : { + "Red Component" : 0.92929404973983765, + "Color Space" : "P3", + "Blue Component" : 0.13960540294647217, + "Alpha Component" : 0.5, + "Green Component" : 0.25479039549827576 + }, + "Character Encoding" : 4, + "Ansi 11 Color (Dark)" : { + "Red Component" : 0.92156862745098034, + "Color Space" : "sRGB", + "Blue Component" : 0.56862745098039214, + "Alpha Component" : 1, + "Green Component" : 0.82745098039215681 + }, + "Bold Color (Light)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Ansi 12 Color (Dark)" : { + "Red Component" : 0.45490196078431372, + "Color Space" : "sRGB", + "Blue Component" : 0.9882352941176471, + "Alpha Component" : 1, + "Green Component" : 0.6588235294117647 + }, + "Ansi 7 Color" : { + "Red Component" : 0.65098039215686276, + "Color Space" : "sRGB", + "Blue Component" : 0.78431372549019607, + "Alpha Component" : 1, + "Green Component" : 0.67843137254901964 + }, + "Non Ascii Font" : "Monaco 12", + "Ansi 6 Color (Dark)" : { + "Red Component" : 0.58039215686274515, + "Color Space" : "sRGB", + "Blue Component" : 0.83529411764705885, + "Alpha Component" : 1, + "Green Component" : 0.88627450980392153 + }, + "Cursor Guide Color" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 0.070000000000000007, + "Green Component" : 0.83921568627450982 + }, + "Custom Directory" : "No", + "Working Directory" : "\/Users\/evansenter", + "ASCII Anti Aliased" : true, + "Shortcut" : "", + "Mouse Reporting" : true, + "Tags" : [ + + ], + "Ansi 6 Color (Light)" : { + "Red Component" : 0.58039215686274515, + "Color Space" : "sRGB", + "Blue Component" : 0.83529411764705885, + "Alpha Component" : 1, + "Green Component" : 0.88627450980392153 + }, + "Match Background Color (Light)" : { + "Red Component" : 0.99697142839431763, + "Color Space" : "P3", + "Blue Component" : 0.32116127014160156, + "Alpha Component" : 1, + "Green Component" : 0.98600882291793823 + }, + "Background Image Location" : "", + "Ansi 1 Color (Light)" : { + "Red Component" : 0.95294117647058818, + "Color Space" : "sRGB", + "Blue Component" : 0.6588235294117647, + "Alpha Component" : 1, + "Green Component" : 0.54509803921568623 + }, + "Use Bold Font" : true, + "Ansi 8 Color (Light)" : { + "Red Component" : 0.34509803921568627, + "Color Space" : "sRGB", + "Blue Component" : 0.4392156862745098, + "Alpha Component" : 1, + "Green Component" : 0.35686274509803922 + }, + "Ansi 2 Color" : { + "Red Component" : 0.65098039215686276, + "Color Space" : "sRGB", + "Blue Component" : 0.63137254901960782, + "Alpha Component" : 1, + "Green Component" : 0.8901960784313725 + }, + "Normal Font" : "JetBrainsMonoNLNFM-Regular 12", + "Unlimited Scrollback" : true, + "Ansi 12 Color (Light)" : { + "Red Component" : 0.45490196078431372, + "Color Space" : "sRGB", + "Blue Component" : 0.9882352941176471, + "Alpha Component" : 1, + "Green Component" : 0.6588235294117647 + }, + "Ansi 10 Color (Dark)" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.54509803921568623, + "Alpha Component" : 1, + "Green Component" : 0.84705882352941175 + }, + "Ansi 3 Color (Light)" : { + "Red Component" : 0.97647058823529409, + "Color Space" : "sRGB", + "Blue Component" : 0.68627450980392157, + "Alpha Component" : 1, + "Green Component" : 0.88627450980392153 + }, + "Cursor Color (Light)" : { + "Red Component" : 0.96078431372549022, + "Color Space" : "sRGB", + "Blue Component" : 0.86274509803921573, + "Alpha Component" : 1, + "Green Component" : 0.8784313725490196 + }, + "Ansi 15 Color" : { + "Red Component" : 0.72941176470588232, + "Color Space" : "sRGB", + "Blue Component" : 0.87058823529411766, + "Alpha Component" : 1, + "Green Component" : 0.76078431372549016 + }, + "Blur" : false, + "Use Separate Colors for Light and Dark Mode" : false, + "Background Color (Light)" : { + "Red Component" : 0.11764705882352941, + "Color Space" : "sRGB", + "Blue Component" : 0.1803921568627451, + "Alpha Component" : 1, + "Green Component" : 0.11764705882352941 + }, + "Terminal Type" : "xterm-256color", + "Ansi 13 Color" : { + "Red Component" : 0.94901960784313721, + "Color Space" : "sRGB", + "Blue Component" : 0.87058823529411766, + "Alpha Component" : 1, + "Green Component" : 0.68235294117647061 + }, + "Ansi 5 Color (Light)" : { + "Red Component" : 0.96078431372549022, + "Color Space" : "sRGB", + "Blue Component" : 0.90588235294117647, + "Alpha Component" : 1, + "Green Component" : 0.76078431372549016 + }, + "Foreground Color (Dark)" : { + "Red Component" : 0.80392156862745101, + "Color Space" : "sRGB", + "Blue Component" : 0.95686274509803926, + "Alpha Component" : 1, + "Green Component" : 0.83921568627450982 + }, + "Link Color" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.92156862745098034, + "Alpha Component" : 1, + "Green Component" : 0.86274509803921573 + }, + "Link Color (Dark)" : { + "Red Component" : 0.53725490196078429, + "Color Space" : "sRGB", + "Blue Component" : 0.92156862745098034, + "Alpha Component" : 1, + "Green Component" : 0.86274509803921573 + }, + "Ansi 11 Color" : { + "Red Component" : 0.92156862745098034, + "Color Space" : "sRGB", + "Blue Component" : 0.56862745098039214, + "Alpha Component" : 1, + "Green Component" : 0.82745098039215681 + }, + "Ansi 5 Color" : { + "Red Component" : 0.96078431372549022, + "Color Space" : "sRGB", + "Blue Component" : 0.90588235294117647, + "Alpha Component" : 1, + "Green Component" : 0.76078431372549016 + }, + "Ansi 0 Color (Light)" : { + "Red Component" : 0.27058823529411763, + "Color Space" : "sRGB", + "Blue Component" : 0.35294117647058826, + "Alpha Component" : 1, + "Green Component" : 0.27843137254901962 + } +} \ No newline at end of file From 7ea0943f95499baa34eb32b8df054b1a80205365 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 23:08:57 +0100 Subject: [PATCH 06/10] Skip pip updates when INSTALL_AI is false to avoid gcloud keyring errors --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 45c54e2e..b3460a21 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -898,7 +898,7 @@ update_packages() { fi # Update pip packages (no safe "update all" — list managed packages explicitly) - if command -v pip3 >/dev/null 2>&1; then + if [[ "$INSTALL_AI" == true ]] && command -v pip3 >/dev/null 2>&1; then echo "Updating pip packages..." pip3 install --upgrade piper-tts 2>/dev/null || pip3 install --upgrade --break-system-packages piper-tts 2>/dev/null || true fi From 57c2dab1a4d9c85c34d8403e8da1f144b277ec01 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 23:18:03 +0100 Subject: [PATCH 07/10] Filter out deprecated always-auth parameter from auto-generated NPM registry configs --- bootstrap.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index b3460a21..13a62215 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1179,7 +1179,9 @@ configure_npm_registry() { fi echo "Configuring NPM registry credentials via gcloud..." - if gcloud artifacts print-settings npm --project=artifact-foundry-prod --repository=ah-3p-staging-npm --location=us >> "$HOME/.npmrc" 2>/dev/null; then + local settings + if settings=$(gcloud artifacts print-settings npm --project=artifact-foundry-prod --repository=ah-3p-staging-npm --location=us 2>/dev/null); then + echo "$settings" | grep -v "always-auth" >> "$HOME/.npmrc" echo "NPM registry credentials configured in ~/.npmrc" else echo " Warning: Failed to configure NPM credentials automatically" From f9e966585b7a3b322f01527b34807be2a417a308 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Fri, 5 Jun 2026 23:19:12 +0100 Subject: [PATCH 08/10] Automate temporary Pip mirror override during Homebrew package installations --- bootstrap.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bootstrap.sh b/bootstrap.sh index 13a62215..9b238bfb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1002,6 +1002,17 @@ install_brew_packages() { return 0 fi + local temp_pip_conf=false + # Check if we need to temporarily override pip.conf to bypass corporate mirror issues for gcloud-cli + if [[ "$(uname)" == "Darwin" ]] && [[ -f "/Library/Application Support/pip/pip.conf" ]]; then + if [[ ! -f "$HOME/.config/pip/pip.conf" ]]; then + echo "Temporarily configuring PyPI mirror for Homebrew Python dependencies..." + mkdir -p "$HOME/.config/pip" + echo -e "[global]\nindex-url = https://pypi.org/simple" > "$HOME/.config/pip/pip.conf" + temp_pip_conf=true + fi + fi + echo "Installing Homebrew packages..." # Use --adopt to take ownership of existing apps instead of erroring HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$brewfile" @@ -1015,6 +1026,12 @@ install_brew_packages() { HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$ai_brewfile" fi fi + + # Restore/cleanup temporary pip.conf + if [[ "$temp_pip_conf" == true ]]; then + rm -f "$HOME/.config/pip/pip.conf" + echo "Restored corporate Pip configuration." + fi } run_brew_hooks() { From fffaf0a75c4f436d02cab2d5113fc0aebc4cebea Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 22:34:38 +0000 Subject: [PATCH 09/10] Address PR review feedback on bootstrap improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pip.conf: guarantee cleanup via EXIT trap so a failed brew bundle under set -euo pipefail can't leave the temp file masking the corporate pip.conf on later runs; clear the trap on the success path. Reword the cleanup message (nothing is "restored"). - npm registry: guard `grep -v always-auth` with `|| true` so a fully filtered output (grep exit 1) can't abort bootstrap under pipefail; note the embedded OAuth token can go stale. - Brewfile: move tailscale-app back out of Brewfile.ai into the base Brewfile — it underpins infra/gateway networking independent of AI tooling. - Remove obsolete brew-hooks/iterm2.sh: preferences/iTerm.json is now a flat color-preset export (manual import), not a DynamicProfile, and the hook still pointed at the deleted iTerm Profile.json. --- Brewfile | 2 ++ Brewfile.ai | 3 --- bootstrap.sh | 19 ++++++++++++++++--- brew-hooks/iterm2.sh | 33 --------------------------------- 4 files changed, 18 insertions(+), 39 deletions(-) delete mode 100755 brew-hooks/iterm2.sh diff --git a/Brewfile b/Brewfile index c12494f6..526eb15d 100644 --- a/Brewfile +++ b/Brewfile @@ -77,6 +77,8 @@ cask "whatsapp" # Casks - Fonts cask "font-jetbrains-mono-nerd-font" +# Casks - Networking +cask "tailscale-app" # Mesh VPN (event bus, openclaw) — infra, not AI-gated # Casks - Cloud cask "gcloud-cli" # Requires: gcloud init diff --git a/Brewfile.ai b/Brewfile.ai index 5a813678..8669e8e6 100644 --- a/Brewfile.ai +++ b/Brewfile.ai @@ -13,9 +13,6 @@ cask "lm-studio" # Local speech-to-text brew "whisper-cpp" -# Tailscale Mesh VPN (event bus, openclaw) -cask "tailscale-app" - # OpenClaw Agent Tools brew "spotify_player" # Spotify CLI (OAuth device flow) brew "gogcli" # Google Suite CLI diff --git a/bootstrap.sh b/bootstrap.sh index 9b238bfb..b0c1feb8 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1010,6 +1010,11 @@ install_brew_packages() { mkdir -p "$HOME/.config/pip" echo -e "[global]\nindex-url = https://pypi.org/simple" > "$HOME/.config/pip/pip.conf" temp_pip_conf=true + # Guarantee cleanup even if a brew bundle below aborts under set -euo + # pipefail. A leftover temp file would otherwise mask the corporate + # pip.conf on every subsequent run (the guard above skips re-creating + # it once $HOME/.config/pip/pip.conf exists). + trap 'rm -f "$HOME/.config/pip/pip.conf"' EXIT fi fi @@ -1027,10 +1032,12 @@ install_brew_packages() { fi fi - # Restore/cleanup temporary pip.conf + # Remove the temporary pip.conf on the success path and clear the + # safety-net trap (cleanup only ran via the trap on an early abort). if [[ "$temp_pip_conf" == true ]]; then rm -f "$HOME/.config/pip/pip.conf" - echo "Restored corporate Pip configuration." + trap - EXIT + echo "Removed temporary PyPI mirror override." fi } @@ -1198,7 +1205,13 @@ configure_npm_registry() { echo "Configuring NPM registry credentials via gcloud..." local settings if settings=$(gcloud artifacts print-settings npm --project=artifact-foundry-prod --repository=ah-3p-staging-npm --location=us 2>/dev/null); then - echo "$settings" | grep -v "always-auth" >> "$HOME/.npmrc" + # Strip the deprecated always-auth line. The trailing `|| true` keeps a + # fully-filtered output (grep exit 1) from aborting bootstrap under + # `set -euo pipefail`. + # Note: print-settings embeds a short-lived OAuth _authToken that is not + # refreshed here (the guard above skips re-running once ah-3p-staging-npm + # is present in ~/.npmrc). Re-run this step if npm auth starts failing. + echo "$settings" | { grep -v "always-auth" || true; } >> "$HOME/.npmrc" echo "NPM registry credentials configured in ~/.npmrc" else echo " Warning: Failed to configure NPM credentials automatically" diff --git a/brew-hooks/iterm2.sh b/brew-hooks/iterm2.sh deleted file mode 100755 index 0d61ea2b..00000000 --- a/brew-hooks/iterm2.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -# Post-install hook for iTerm2 -# Sets up Dynamic Profiles from dotfiles - -set -euo pipefail - -if [[ ! -d "/Applications/iTerm.app" ]]; then - exit 0 -fi - -dotfiles_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" -profile_src="$dotfiles_dir/preferences/iTerm Profile.json" -dynamic_profiles_dir="$HOME/Library/Application Support/iTerm2/DynamicProfiles" - -if [[ ! -f "$profile_src" ]]; then - exit 0 -fi - -# Create DynamicProfiles directory if needed -mkdir -p "$dynamic_profiles_dir" - -# Symlink profile for auto-loading -dest="$dynamic_profiles_dir/dotfiles-profile.json" -if [[ -L "$dest" && "$(readlink "$dest")" == "$profile_src" ]]; then - exit 0 -fi - -if [[ -e "$dest" || -L "$dest" ]]; then - rm -f "$dest" -fi - -ln -s "$profile_src" "$dest" -echo "Linked iTerm2 profile to DynamicProfiles" From a29ec8b88a566c23ccdf85830c785491d6828581 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 23:35:58 +0000 Subject: [PATCH 10/10] Address non-blocking review suggestions on bootstrap - brew bundle: tolerate a non-zero exit (e.g. a `mas` App Store entry on a machine that isn't signed in) with a visible warning, so Phase 1 no longer aborts before sync_dotfiles runs. brew bundle still reports which packages failed, so failures stay visible. - npm registry: prepend a newline before appending gcloud settings to ~/.npmrc so the first line can't concatenate onto an existing entry that lacks a trailing newline. - CLAUDE.md: document that `-p` refreshes metadata + installs missing packages but no longer runs `brew upgrade`/`cleanup` on macOS. Left stats.sh prefs import as-is: the committed plist is intentionally the source of truth, matching the repo's tracked-config pattern. --- CLAUDE.md | 2 +- bootstrap.sh | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f737b3f8..a9ae2236 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,7 +46,7 @@ CI runs: Lint, Test, Hooks, Bootstrap, claude-review. **Phase 1 — Pull & packages** (only with `--pull`/`-p`): 1. Pulls latest from git 2. Installs packages: Homebrew (macOS), apt (Debian/Ubuntu), or binary downloads to `~/.local/bin` (SteamOS) -3. Updates existing packages +3. Refreshes package metadata (`brew update`) and installs any missing packages via `brew bundle`. Note: on macOS it does **not** run `brew upgrade`/`brew cleanup`, so already-installed formulae are not bumped to newer versions (kept out for speed and to avoid surprise breakage — upgrade manually with `brew upgrade`). **Phase 2 — Sync dotfiles** (always runs): 1. Sets default shell to zsh diff --git a/bootstrap.sh b/bootstrap.sh index b0c1feb8..45dde3fa 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1019,8 +1019,12 @@ install_brew_packages() { fi echo "Installing Homebrew packages..." - # Use --adopt to take ownership of existing apps instead of erroring - HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$brewfile" + # Use --adopt to take ownership of existing apps instead of erroring. + # Tolerate a non-zero exit (e.g. a `mas` entry failing because the machine + # isn't signed into the App Store) so Phase 1 doesn't abort before + # sync_dotfiles runs — brew bundle already reports which packages failed. + HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$brewfile" \ + || echo " Warning: some Homebrew packages failed to install (continuing)." # Conditionally install AI assistant packages (Claude, OpenClaw tools) prompt_ai_install @@ -1028,7 +1032,8 @@ install_brew_packages() { local ai_brewfile="$dotfiles_dir/Brewfile.ai" if [[ -f "$ai_brewfile" ]]; then echo "Installing AI assistant packages..." - HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$ai_brewfile" + HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$ai_brewfile" \ + || echo " Warning: some AI Homebrew packages failed to install (continuing)." fi fi @@ -1211,7 +1216,9 @@ configure_npm_registry() { # Note: print-settings embeds a short-lived OAuth _authToken that is not # refreshed here (the guard above skips re-running once ah-3p-staging-npm # is present in ~/.npmrc). Re-run this step if npm auth starts failing. - echo "$settings" | { grep -v "always-auth" || true; } >> "$HOME/.npmrc" + # Prepend a newline so the first appended line can't concatenate onto an + # existing ~/.npmrc entry that lacks a trailing newline. + { printf '\n'; echo "$settings" | { grep -v "always-auth" || true; }; } >> "$HOME/.npmrc" echo "NPM registry credentials configured in ~/.npmrc" else echo " Warning: Failed to configure NPM credentials automatically"