Skip to content

Instantly share code, notes, and snippets.

@digitaldrummerj
Last active February 12, 2026 14:17
Show Gist options
  • Select an option

  • Save digitaldrummerj/770986c87a7888db92d30f48ca07e6cd to your computer and use it in GitHub Desktop.

Select an option

Save digitaldrummerj/770986c87a7888db92d30f48ca07e6cd to your computer and use it in GitHub Desktop.
Powershell Public Profile

Justin's (digitaldrummerj) PowerShell Setup

This profile is setup for Mac and has not been tested on Windows

🚀 Quick Install (End Users - From Gist)

For a brand new machine, use the automated gist installer:

  1. Download the gist and run the install.ps1

The installer will:

  • ✅ Create the proper directory structure (~/.config/powershell/)
  • ✅ Download all files from gist
  • ✅ Set proper permissions on scripts
  • ✅ Check for required dependencies
  • ✅ Provide next steps for environment setup

After installation:

  1. Close and reopen your terminal
  2. Set required environment variables (see Configuration section)
  3. Type help to see all available commands

👨‍💻 Developer Setup (From Git Repository)

For development work, clone the full repository:

git clone --recurse-submodules https://github.com/digitaldrummerj/dotfiles.git
cd dotfiles/powershell

This gives you:

  • Full Git history for version control
  • Access to private modules (submodule)
  • Ability to make changes and submit PRs

Prerequisites

  1. PowerShell 7+ - Install from Microsoft's website or via Homebrew:

    brew install --cask powershell
  2. Homebrew - Required for dependency management (if not already installed):

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Installation Steps

1. Set Up GitHub Gist Token (Required for Sync)

The profile uses GitHub Gist for syncing across machines. You'll need a personal access token:

  1. Go to GitHub Settings → Tokens

  2. Generate a new token with gist scope

  3. Store it securely in macOS Keychain:

    # From PowerShell, run:
    SetEnv 'GITHUB_GIST_TOKEN' 'your-token-here'

2. Update Gist ID (For Your Own Gist)

Edit ~/.config/powershell/modules/config.ps1 and update the gist ID to your own:

$script:GistId = "your-gist-id-here"

Set Up GitHub Private Gist Id (Required for Sync) to hold your Powershell modules that you want to keep private

  1. Create a gist that will hold your private modules

  2. Grab the Gist Id

  3. Set the Environment Variable

    # From PowerShell, run:
    SetEnv 'POWERSHELL_PRIVATE_GIST_ID' 'your-gist-id-here'

3. Install Dependencies

The profile will automatically check for missing dependencies on first run and show install commands:

  • PSReadLine - Enhanced command-line editing
  • Terminal-Icons - Pretty icons for files/folders
  • Oh-My-Posh - Prompt theming engine
  • Monaspace Nerd Font - Required font for icons

When you first load the profile, it will show something like:

⚠️  Missing dependencies:
   • PSReadLine - Install with: Install-Module PSReadLine
   • Terminal-Icons - Install with: Install-Module Terminal-Icons
   • Oh-My-Posh - Install with: brew install oh-my-posh
   • Monaspace Nerd Font - Install with: brew install --cask font-monaspice-nerd-font
   
   💡 After installing, set terminal font to: MonaspiceKr Nerd Font
      (Terminal → Settings → Text → Change Font)

Just copy and paste the install commands shown.

5. Configure Your Terminal Font

After installing the Nerd Font, configure your terminal:

  1. Open Terminal preferences
  2. Go to your profile → Text tab
  3. Click "Change" button next to Font
  4. Select "MonaspiceKr Nerd Font" (or your preferred Monaspace variant)
  5. Restart Terminal

6. Sync to Your Own Gist (Optional)

Once everything is set up, you can push your profile to your own gist:

profile ps    # Push to your gist

On other machines, you can pull it down with:

profile pl    # Pull from your gist

Enjoy! 🎉

Type help in your terminal to see all available shortcuts and features.


Justin's (digitaldrummerj) PowerShell Setup

This profile is setup for Mac and has not been tested on Windows

Prerequisites

  1. PowerShell 7+ - Install from Microsoft's website or via Homebrew:

    brew install --cask powershell
    1. Open Terminal
    2. Go to Settings
    3. Click on Profiles
    4. Click the + to add a new Profile
    5. Name the profile Powershell
    6. Go to the Shell tab for the Powershell profile
      • check the 'Run command' and put in /usr/local/microsoft/powershell/7/pwsh
  2. Powershell Modules - for Auto Complete previous commands and terminal icons with ls

    Install-Module PSReadLine
    Install-Module Terminal-Icons
  3. Oh My Posh - Better Terminal Prompt

    brew install oh-my-posh
  4. Fonts - Monaspace Nerd Font so you get icons in the terminal

    brew install --cask font-monaspice-nerd-font

    💡 After installing, set terminal font to: MonaspiceKr Nerd Font Mono (Terminal → Settings → Text → Change Font)

Installation Steps

1. Download files

  1. Download the Gist Zip file
  2. Unzip the file
  3. Open your Powershell Terminal
  4. Navigate to the zip file directory
  5. Run ./install.ps1 to copy the file to your Powershell profile directory. It will backup your existing files in your profile directory.

1. Set Up GitHub Gist Token (Required for Sync)

The profile uses GitHub Gist for syncing across machines. You'll need a personal access token:

  1. Go to GitHub Settings → Tokens

  2. Generate a new token with gist scope

  3. Store it securely in macOS Keychain:

    # From PowerShell, run:
    SetEnv 'GITHUB_GIST_TOKEN' 'your-token-here'

2. Update Gist ID (For Your Own Gist)

  1. Go to https://gist.github.com/ and create a public gist to hold your profile files
  2. Edit ~/.config/powershell/modules/config.ps1 and update the gist ID to your own:
$script:GistId = "your-gist-id-here"

Set Up GitHub Private Gist Id (Required for Sync) to hold your Powershell modules that you want to keep private

  1. Go to https://gist.github.com/ and create a secret gist

  2. Grab the Gist Id

  3. Set the Environment Variable

    # From PowerShell, run:
    SetEnv 'POWERSHELL_PRIVATE_GIST_ID' 'your-gist-id-here'

4. Sync to Your Own Gist

Sync from Profile to Gist

profile ps    # Push to your gist

Sync from Gist to Profile:

profile pl    # Pull from your gist

Enjoy! 🎉

Type help in your terminal to see all available shortcuts and features.


{
"global": {
"dl": "~/Downloads",
"docs": "~/Documents",
"movies": "~/Movies",
"downloads": "~/Downloads",
"dev": "~/Development",
"dnerd": "~/Development/letyournerdbeheard",
"dps": "~/Development/letyournerdbeheard/dotfiles/powershell",
"dotfiles": "~/Development/letyournerdbeheard/dotfiles",
"nerd": "~/"
}
}
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Install PowerShell profile from GitHub Gist
.DESCRIPTION
This script sets up the PowerShell profile on a new machine by:
- Creating the proper directory structure
- Renaming flattened gist files (profile.ps1, modules-*, scripts-*)
- Moving files to correct locations
- Setting proper permissions
- Installing required dependencies
.EXAMPLE
./install.ps1
Run from the downloaded gist directory to install the profile
.EXAMPLE
./install.ps1 -SkipDependencies
Install profile without checking/installing dependencies
.NOTES
Gist files are flattened when downloaded:
- profile.ps1 (not Microsoft.PowerShell_profile.ps1)
- modules-*.ps1 (not in modules/ directory)
- scripts-*.ps1 (not in scripts/ directory)
This script handles the renaming and restructuring automatically.
#>
[CmdletBinding()]
param(
[switch]$SkipDependencies,
[switch]$Force
)
# ================================================================
# CONFIGURATION
# ================================================================
$script:TargetDir = Split-Path -Parent $PROFILE
$script:ModulesDir = Join-Path $script:TargetDir 'modules'
$script:ScriptsDir = Join-Path $script:TargetDir 'scripts'
$script:CurrentDir = Get-Location
# ================================================================
# HELPER FUNCTIONS
# ================================================================
function Write-Step {
param([string]$Message)
Write-Host "$Message" -ForegroundColor Cyan
}
function Write-Success {
param([string]$Message)
Write-Host "$Message" -ForegroundColor Green
}
function Write-Warning {
param([string]$Message)
Write-Host "$Message" -ForegroundColor Yellow
}
function Write-Error {
param([string]$Message)
Write-Host "$Message" -ForegroundColor Red
}
function Test-HasRequiredFiles {
# Check if we have the essential gist files (flattened format with double-dash)
# Gist downloads as: profile.ps1, modules--*.ps1, scripts--*.ps1
$profileExists = Test-Path (Join-Path $script:CurrentDir 'profile.ps1')
$hasModuleConfig = Test-Path (Join-Path $script:CurrentDir 'modules--config.ps1')
$hasModuleHelp = Test-Path (Join-Path $script:CurrentDir 'modules--help.ps1')
return ($profileExists -and $hasModuleConfig -and $hasModuleHelp)
}
# ================================================================
# MAIN INSTALLATION
# ================================================================
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " POWERSHELL PROFILE INSTALLER" -ForegroundColor Green
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
# Check if we have required files
if (-not (Test-HasRequiredFiles)) {
Write-Error "Required gist files not found!"
Write-Host ""
Write-Host "Please ensure you're running this script from the directory containing:" -ForegroundColor Yellow
Write-Host " • profile.ps1 (the main profile)" -ForegroundColor Gray
Write-Host " • modules--*.ps1 (module files with double-dash)" -ForegroundColor Gray
Write-Host " • scripts--*.ps1 (script files with double-dash)" -ForegroundColor Gray
Write-Host ""
Write-Host "These files are created when you download the gist from GitHub." -ForegroundColor Gray
Write-Host ""
exit 1
}
Write-Success "Found gist files in current directory"
Write-Host ""
# Check if profile already exists
if ((Test-Path $script:TargetDir) -and -not $Force) {
Write-Warning "Profile directory already exists: $script:TargetDir"
Write-Host ""
$response = Read-Host "Overwrite existing profile? (yes/no)"
if ($response -ne 'yes') {
Write-Host "Installation cancelled." -ForegroundColor Yellow
exit 0
}
Write-Host ""
}
# ================================================================
# STEP 1: Create Directory Structure
# ================================================================
Write-Step "Creating directory structure..."
@($script:TargetDir, $script:ModulesDir, $script:ScriptsDir) | ForEach-Object {
if (-not (Test-Path $_)) {
New-Item -ItemType Directory -Path $_ -Force | Out-Null
Write-Success "Created: $_"
} else {
Write-Success "Exists: $_"
}
}
Write-Host ""
# ================================================================
# STEP 2: Install Main Profile
# ================================================================
Write-Step "Installing main profile..."
# Gist renames Microsoft.PowerShell_profile.ps1 → profile.ps1
$profileSource = Join-Path $script:CurrentDir 'profile.ps1'
# $PROFILE is already the full path to the target location
$profileTarget = $PROFILE
if (Test-Path $profileSource) {
Copy-Item -Path $profileSource -Destination $profileTarget -Force
Write-Success "Installed: $PROFILE"
} else {
Write-Error "Profile file (profile.ps1) not found!"
}
Write-Host ""
# ================================================================
# STEP 3: Install Module Files
# ================================================================
Write-Step "Installing modules..."
# Find all modules--*.ps1 files (gist flattens directory structure with double-dash)
$moduleFiles = Get-ChildItem -Path $script:CurrentDir -Filter 'modules--*.ps1'
if ($moduleFiles.Count -eq 0) {
Write-Warning "No module files found (modules--*.ps1)"
} else {
foreach ($moduleFile in $moduleFiles) {
# Remove 'modules--' prefix to get original filename
$originalName = $moduleFile.Name -replace '^modules--', ''
$target = Join-Path $script:ModulesDir $originalName
Copy-Item -Path $moduleFile.FullName -Destination $target -Force
Write-Success "Installed: modules/$originalName"
}
Write-Success "Installed $($moduleFiles.Count) module files"
}
Write-Host ""
# ================================================================
# STEP 4: Install Script Files
# ================================================================
Write-Step "Installing scripts..."
# Find all scripts--*.ps1 files (gist flattens directory structure with double-dash)
$scriptFiles = Get-ChildItem -Path $script:CurrentDir -Filter 'scripts--*.ps1'
if ($scriptFiles.Count -eq 0) {
Write-Warning "No script files found (scripts--*.ps1)"
} else {
foreach ($scriptFile in $scriptFiles) {
# Remove 'scripts--' prefix to get original filename
$originalName = $scriptFile.Name -replace '^scripts--', ''
$target = Join-Path $script:ScriptsDir $originalName
Copy-Item -Path $scriptFile.FullName -Destination $target -Force
# Make executable on Unix-like systems
if ($IsMacOS -or $IsLinux) {
chmod +x $target
}
Write-Success "Installed: scripts/$originalName"
}
Write-Success "Installed $($scriptFiles.Count) script files"
}
Write-Host ""
# ================================================================
# STEP 5: Copy Additional Files
# ================================================================
Write-Step "Installing additional files..."
# Theme file
$themeSource = Join-Path $script:CurrentDir 'theme.omp.json'
$themeTarget = Join-Path $script:TargetDir 'theme.omp.json'
if (Test-Path $themeSource) {
Copy-Item -Path $themeSource -Destination $themeTarget -Force
Write-Success "Installed: theme.omp.json"
}
# Bookmarks file
$bookmarksSource = Join-Path $script:CurrentDir 'bookmarks.json'
$bookmarksTarget = Join-Path $script:TargetDir 'bookmarks.json'
if (Test-Path $bookmarksSource) {
Copy-Item -Path $bookmarksSource -Destination $bookmarksTarget -Force
Write-Success "Installed: bookmarks.json"
}
# README file
$readmeSource = Join-Path $script:CurrentDir '_instructions.md'
$readmeTarget = Join-Path $script:TargetDir '_instructions.md'
if (Test-Path $readmeSource) {
Copy-Item -Path $readmeSource -Destination $readmeTarget -Force
Write-Success "Installed: _instructions.md"
}
# Install script (so it's available for future pulls)
$installSource = Join-Path $script:CurrentDir 'install.ps1'
$installTarget = Join-Path $script:TargetDir 'install.ps1'
if (Test-Path $installSource) {
Copy-Item -Path $installSource -Destination $installTarget -Force
Write-Success "Installed: install.ps1"
}
# Copy any custom files (files with -- that aren't modules or scripts)
$customFiles = Get-ChildItem -Path $script:CurrentDir -File | Where-Object {
$name = $_.Name
$name -like '*--*' -and
$name -notlike 'modules--*' -and
$name -notlike 'scripts--*'
}
if ($customFiles.Count -gt 0) {
foreach ($customFile in $customFiles) {
# Reconstruct directory path (e.g., custom--config.yaml -> custom/config.yaml)
$relativePath = $customFile.Name -replace '--', '/'
$targetPath = Join-Path $script:TargetDir $relativePath
# Ensure parent directory exists
$parentDir = Split-Path -Parent $targetPath
if ($parentDir -and -not (Test-Path $parentDir)) {
New-Item -ItemType Directory -Path $parentDir -Force | Out-Null
}
Copy-Item -Path $customFile.FullName -Destination $targetPath -Force
Write-Success "Installed: $relativePath"
}
}
Write-Host ""
# ================================================================
# STEP 6: Check Dependencies
# ================================================================
if (-not $SkipDependencies) {
Write-Step "Checking dependencies..."
Write-Host ""
# Check for oh-my-posh
$hasPosh = Get-Command oh-my-posh -ErrorAction SilentlyContinue
if ($hasPosh) {
Write-Success "oh-my-posh: Installed"
} else {
Write-Warning "oh-my-posh: Not installed"
Write-Host " Install: brew install oh-my-posh" -ForegroundColor Gray
}
# Check for fnm
$hasFnm = Get-Command fnm -ErrorAction SilentlyContinue
if ($hasFnm) {
Write-Success "fnm: Installed"
} else {
Write-Warning "fnm: Not installed"
Write-Host " Install: brew install fnm" -ForegroundColor Gray
}
# Check for GitHub CLI
$hasGh = Get-Command gh -ErrorAction SilentlyContinue
if ($hasGh) {
Write-Success "GitHub CLI: Installed"
} else {
Write-Warning "GitHub CLI: Not installed"
Write-Host " Install: brew install gh" -ForegroundColor Gray
}
Write-Host ""
}
# ================================================================
# STEP 7: Setup Environment Variables
# ================================================================
Write-Step "Environment setup..."
Write-Warning "You need to set the following environment variables:"
Write-Host ""
Write-Host "After loading the profile, use:" -ForegroundColor Gray
Write-Host " SetEnv 'GITHUB_GIST_TOKEN' 'your-token'" -ForegroundColor Cyan
Write-Host " SetEnv 'POWERSHELL_PRIVATE_GIST_ID' 'your-private-gist-id'" -ForegroundColor Cyan
Write-Host ""
# ================================================================
# INSTALLATION COMPLETE
# ================================================================
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green
Write-Host " ✓ INSTALLATION COMPLETE!" -ForegroundColor Green
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host " 1. Close and reopen your terminal" -ForegroundColor White
Write-Host " 2. Set required environment variables (see above)" -ForegroundColor White
Write-Host " 3. Type 'help' to see all available commands" -ForegroundColor White
Write-Host ""
Write-Host "Profile location: $script:TargetDir" -ForegroundColor Gray
Write-Host ""
# ================================================================
# COMPANION MODULE MANAGEMENT
# ================================================================
# Unified management for Companion module projects using yarn
$script:CompanionModuleDevRoot = Join-Path $HOME 'Development/companion-module-dev'
# Define all companion modules with their directory names
$script:CompanionModules = [ordered]@{
'hdtv' = @{ Path = 'companion-module-hdtv-wolfpackgreen'; Name = 'HDTV WolfPack Green' }
'wiz' = @{ Path = 'companion-module-philips-wiz-bulbs'; Name = 'Philips Wiz Bulbs' }
'ztiles' = @{ Path = 'companion-module-zoom-tiles'; Name = 'Zoom Tiles' }
'lobs' = @{ Path = 'companion-module-lynbh-obs'; Name = 'LYNBH OBS' }
'zosc' = @{ Path = 'companion-module-zoom-osc-iso'; Name = 'Zoom OSC ISO' }
'key' = @{ Path = 'companion-module-elgato-keylight'; Name = 'Elgato Keylight' }
}
function Invoke-CompanionAction {
<#
.SYNOPSIS
Internal function to perform actions on companion modules
.PARAMETER ModuleKey
The module key (hdtv, wiz, ztiles, lobs, zosc)
.PARAMETER Action
The action to perform (deps, update, dev, build)
#>
param(
[Parameter(Mandatory)]
[string]$ModuleKey,
[Parameter(Mandatory)]
[ValidateSet('deps', 'update', 'dev', 'build')]
[string]$Action
)
$module = $script:CompanionModules[$ModuleKey]
$modulePath = Join-Path $script:CompanionModuleDevRoot $module.Path
if (-not (Test-Path $modulePath)) {
Write-Host "❗ Module path not found: $modulePath" -ForegroundColor Red
return
}
# Navigate to the module directory and stay there
Set-Location $modulePath
Write-Host "📁 In: $modulePath" -ForegroundColor DarkGray
Write-Host ""
switch ($Action) {
'deps' {
Write-Host "📦 Installing dependencies for $($module.Name)..." -ForegroundColor Cyan
yarn install
if ($LASTEXITCODE -eq 0) {
Write-Host "✅ Dependencies installed for $($module.Name)" -ForegroundColor Green
}
}
'update' {
Write-Host "🔄 Updating $($module.Name) from git..." -ForegroundColor Cyan
git pull --prune
if ($LASTEXITCODE -eq 0) {
Write-Host "$($module.Name) updated from git" -ForegroundColor Green
}
}
'dev' {
Write-Host "🚀 Starting dev mode for $($module.Name)..." -ForegroundColor Cyan
Write-Host " (This will run in the current terminal)" -ForegroundColor Gray
yarn dev
}
'build' {
Write-Host "🔨 Building $($module.Name)..." -ForegroundColor Cyan
yarn build
if ($LASTEXITCODE -eq 0) {
Write-Host "$($module.Name) built successfully" -ForegroundColor Green
}
}
}
}
function Invoke-Companion {
<#
.SYNOPSIS
Unified command for Companion module management
.DESCRIPTION
Provides centralized access to all Companion module operations including
dependency installation, git updates, and development server management.
Supports both full and short command names (nav/n, dev/d, update/u, build/b, deps/ds).
.PARAMETER Action
The action to perform: help, nav/n, update-all, deps/ds, update/u, dev/d, build/b
.PARAMETER Module
The module to perform the action on (required for deps, update, dev, build)
.EXAMPLE
comp help
Display all available Companion module commands
.EXAMPLE
cm update-all
Update all modules from git
.EXAMPLE
comp ds hdtv
Install dependencies for HDTV module (short form)
.EXAMPLE
cm d wiz
Start dev server for Wiz Bulbs module (short form)
.ALIAS
comp, cm
#>
param(
[Parameter(Position=0)]
[ValidateSet('h', 'help', 'nav', 'n', 'update-all', 'deps', 'ds', 'update', 'u', 'dev', 'd', 'build', 'b')]
[string]$Action = 'help',
[Parameter(Position=1)]
[ValidateSet('root', 'hdtv', 'wiz', 'ztiles', 'lobs', 'zosc', 'key')]
[string]$Module
)
# Normalize short aliases to full names
$normalizedAction = switch ($Action) {
'n' { 'nav' }
'ds' { 'deps' }
'u' { 'update' }
'd' { 'dev' }
'b' { 'build' }
'h' { 'help' }
default { $Action }
}
switch ($normalizedAction) {
'help' {
Write-Host ""
Write-Host "======= COMPANION MODULE MANAGEMENT =======" -ForegroundColor Green
Write-Host " Get Help: help companion OR comp help OR cm help" -ForegroundColor Gray
Write-Host ""
Write-Host " Aliases: comp, cm, companion" -ForegroundColor Gray
Write-Host ""
Write-Host "------- NAVIGATION" -ForegroundColor Yellow
Write-Host "comp nav | n -> Navigate to root directory"
Write-Host "comp nav | n <module> -> Navigate to module (e.g., comp n hdtv)"
Write-Host ""
Write-Host "------- BULK OPERATIONS" -ForegroundColor Yellow
Write-Host "comp update-all -> Update all modules from git"
Write-Host ""
Write-Host "------- INDIVIDUAL MODULE ACTIONS" -ForegroundColor Yellow
Write-Host "comp deps | ds <module> -> Install dependencies (e.g., cm ds hdtv)"
Write-Host "comp update | u <module> -> Update from git (git pull)"
Write-Host "comp dev | d <module> -> Start dev server (yarn dev)"
Write-Host "comp build | b <module> -> Build module (yarn build)"
Write-Host ""
Write-Host "------- AVAILABLE MODULES" -ForegroundColor Yellow
foreach ($key in $script:CompanionModules.Keys) {
$mod = $script:CompanionModules[$key]
Write-Host "$key -> $($mod.Path) ($($mod.Name))"
}
Write-Host ""
Write-Host "TIP: Use 'comp' or 'cm' as the command" -ForegroundColor DarkGray
Write-Host ""
}
'nav' {
if (-not $Module -or $Module -eq 'root') {
# Navigate to root
if (-not (Test-Path -LiteralPath $script:CompanionModuleDevRoot)) {
Write-Host "❗ Root path not found: $script:CompanionModuleDevRoot" -ForegroundColor Red
return
}
Set-Location -LiteralPath $script:CompanionModuleDevRoot
Write-Host "📁 Navigated to Companion Module Dev root" -ForegroundColor Cyan
}
else {
# Navigate to specific module
if (-not $script:CompanionModules.Contains($Module)) {
Write-Host "❗ Unknown module: $Module" -ForegroundColor Red
Write-Host "Available modules: hdtv, wiz, ztiles, lobs, zosc" -ForegroundColor Gray
return
}
$mod = $script:CompanionModules[$Module]
$targetPath = Join-Path $script:CompanionModuleDevRoot $mod.Path
if (-not (Test-Path -LiteralPath $targetPath)) {
Write-Host "❗ Module path not found: $targetPath" -ForegroundColor Red
return
}
Set-Location -LiteralPath $targetPath
Write-Host "📁 Navigated to $($mod.Name)" -ForegroundColor Cyan
}
}
'update-all' {
Write-Host "🔄 Updating all Companion modules from git..." -ForegroundColor Cyan
Write-Host ""
foreach ($key in $script:CompanionModules.Keys) {
Invoke-CompanionAction -ModuleKey $key -Action 'update'
Write-Host ""
}
Write-Host "✅ All modules have been updated" -ForegroundColor Green
}
{ $_ -in @('deps', 'update', 'dev', 'build') } {
if (-not $Module) {
Write-Host "❗ Module parameter is required for action '$Action'" -ForegroundColor Red
Write-Host "Usage: comp $Action <module>" -ForegroundColor Yellow
Write-Host "Available modules: hdtv, wiz, ztiles, lobs, zosc" -ForegroundColor Gray
return
}
# Validate module exists
if (-not $script:CompanionModules.Contains($Module)) {
Write-Host "❗ Unknown module: $Module" -ForegroundColor Red
Write-Host "Available modules: hdtv, wiz, ztiles, lobs, zosc" -ForegroundColor Gray
return
}
# Call the action with normalized name
Invoke-CompanionAction -ModuleKey $Module -Action $normalizedAction
}
}
}
# Aliases for convenience
Set-Alias -Name comp -Value Invoke-Companion
Set-Alias -Name cm -Value Invoke-Companion
Set-Alias -Name companion -Value Invoke-Companion
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-CompanionHelp {
# Call the built-in companion help which shows dynamic module list
companion help
}
# ================================================================
# CONFIGURATION
# ================================================================
# Global variables and paths used throughout the profile
# Public gist - contains modules/, scripts/, profile.ps1, theme, bookmarks
$script:GistId = "770986c87a7888db92d30f48ca07e6cd"
# Private gist - contains private-modules/ (optional, requires GITHUB_GIST_TOKEN)
# Set via environment variable: POWERSHELL_PRIVATE_GIST_ID
$script:PrivateGistId = $env:POWERSHELL_PRIVATE_GIST_ID
$script:ProfileDir = Split-Path -Parent $PROFILE
$script:themeFileName = "theme.omp.json"
$script:themePath = Join-Path $script:ProfileDir $script:themeFileName
$script:bookmarksFileName = "bookmarks.json"
$script:bookmarksPath = Join-Path $script:ProfileDir $script:bookmarksFileName
# Machine name detection for bookmarks (cross-platform)
$script:machineName = if ($env:MACHINE_NAME) {
# Custom override (highest priority)
$env:MACHINE_NAME
} elseif ($IsWindows) {
# Windows: Use COMPUTERNAME env var
$env:COMPUTERNAME
} else {
# Mac: Use hostname command, strip .local suffix
$hostname = Invoke-Expression -Command 'hostname'
$hostname -replace '\.local$', ''
}
# ================================================================
# COPILOT SHORTCUTS
# ================================================================
# Quick shortcuts for GitHub Copilot CLI
# ================================================================
<#
.SYNOPSIS
Shortcut wrapper for GitHub Copilot CLI
.DESCRIPTION
Provides short aliases for common Copilot CLI commands
.PARAMETER Command
Short command: h (help),c (continue), cy (continue-yolo), r (resume), ry (resume-yolo), y (yolo)
.PARAMETER Parameters
Additional parameters to pass to copilot
.EXAMPLE
c y "create a function" # Run with --yolo
c c # Continue previous session
.ALIAS
c
#>
function Invoke-Copilot {
[Alias('c')]
Param(
[Parameter(Mandatory = $false, Position = 0)]
[Alias("cmd")]
[String]
$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[Alias("params")]
[String[]]
$Parameters
)
# Normalize short aliases to full names
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default { $Command }
}
Switch ($normalizedCommand) {
# help
'help' { Show-CopilotHelp }
# continue
'c' { copilot --continue $Parameters }
# continue with yolo
'cy' { copilot --continue --yolo $Parameters }
# resume
'r' { copilot --resume $Parameters }
# resume with yolo
'ry' { copilot --resume --yolo $Parameters }
# yolo
'y' { copilot --yolo $Parameters }
# catchall
default { copilot $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-CopilotHelp {
Write-Host " COPILOT CLI SHORTCUTS (c)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help copilot OR c help OR c h" -ForegroundColor Gray
Write-Host ""
Write-Host "🤖 COPILOT COMMANDS" -ForegroundColor Yellow
Write-Host " c <cmd> GitHub Copilot CLI shortcuts:"
Write-Host " (none) = default Run copilot normally"
Write-Host " help (h) = help Show this help message"
Write-Host " y = --yolo Auto-approve all suggestions"
Write-Host " c = --continue Continue previous session"
Write-Host " cy = continue+yolo Continue with auto-approve"
Write-Host " r = --resume Resume session"
Write-Host " ry = resume+yolo Resume with auto-approve"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " c 'create a function' Normal copilot"
Write-Host " c y 'add tests' Auto-approve all"
Write-Host " c c Continue previous"
Write-Host " c cy Continue with auto-approve"
Write-Host ""
}
# ================================================================
# DEPENDENCY CHECK
# ================================================================
# Validates that required command-line tools are available
# Supports per-machine suppression via .ignored-dependencies.json
function Test-Dependencies {
<#
.SYNOPSIS
Check for required command-line tools
.DESCRIPTION
Validates that common development tools are installed and shows warnings
for any missing dependencies. Non-blocking - just informational.
Suppression: Use 'profile ignore-dep <name>' to suppress specific warnings
#>
# Load ignored dependencies from file (machine-specific)
$ignoredDepsPath = Join-Path $script:ProfileDir '.ignored-dependencies.json'
$ignoredDeps = @()
if (Test-Path $ignoredDepsPath) {
try {
$ignoredData = Get-Content $ignoredDepsPath -Raw | ConvertFrom-Json
$ignoredDeps = $ignoredData.PSObject.Properties.Name
} catch {
# Silently fail if can't read file
}
}
$dependencies = @{
'gh' = @{ Name = 'GitHub CLI'; InstallCmd = 'brew install gh'; Key = 'gh' }
'git' = @{ Name = 'Git'; InstallCmd = 'brew install git'; Key = 'git' }
'docker' = @{ Name = 'Docker'; InstallCmd = 'Install Docker Desktop'; Key = 'docker' }
'fnm' = @{ Name = 'Fast Node Manager'; InstallCmd = 'Install fnm'; Key = 'fnm' }
'copilot' = @{ Name = 'GitHub Copilot CLI'; InstallCmd = 'brew install copilot-cli'; Key = 'copilot' }
}
$missing = @()
foreach ($cmd in $dependencies.Keys) {
# Skip if ignored
if ($ignoredDeps -contains $cmd) {
continue
}
if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) {
$missing += $dependencies[$cmd]
}
}
# Check for Monaspace Nerd Font (cask installation)
$fontMissing = $false
if ($ignoredDeps -notcontains 'font') {
$monaspiceCask = brew list --cask 2>&1 | Select-String -Pattern 'monaspice-nerd-font'
$fontMissing = -not $monaspiceCask
if ($fontMissing) {
$missing += @{ Name = 'Monaspace Nerd Font'; InstallCmd = 'brew install --cask font-monaspice-nerd-font'; Key = 'font' }
}
}
if ($missing.Count -gt 0) {
Write-Host ""
Write-Host "⚠️ Missing dependencies:" -ForegroundColor Yellow
foreach ($dep in $missing) {
Write-Host "$($dep.Name) - Install with: " -NoNewline -ForegroundColor Gray
Write-Host "$($dep.InstallCmd)" -ForegroundColor Cyan
}
# Add reminder about font configuration if font is missing
if ($fontMissing) {
Write-Host ""
Write-Host " 💡 After installing, set terminal font to: " -NoNewline -ForegroundColor DarkGray
Write-Host "MonaspiceKr Nerd Font" -ForegroundColor Cyan
Write-Host " (Terminal → Settings → Text → Change Font)" -ForegroundColor DarkGray
}
# Show suppression hint
Write-Host " 💡 To suppress warnings: " -NoNewline -ForegroundColor DarkGray
Write-Host "profile ignore-dep <name>" -ForegroundColor Cyan
}
}
# Run dependency check at startup
Test-Dependencies
# ================================================================
# DOCKER COMPOSE SHORTCUTS
# ================================================================
<#
.SYNOPSIS
Docker Compose command shortcuts
.DESCRIPTION
Quick aliases for docker-compose commands. Perfect for Obvio projects.
Alias: dc
.PARAMETER Command
Short command:
b = build l = logs u = up
c = create s = start ud = up --detach
d = down x = stop rmi = images
.PARAMETER Parameters
Additional parameters passed to docker-compose command
.EXAMPLE
dc u # docker-compose up
dc ud # docker-compose up --detach (background)
dc d # docker-compose down
dc l # docker-compose logs
dc l -f myservice # docker-compose logs -f myservice
.ALIAS
dc
#>
function Invoke-DockerCompose {
[Alias('dc')]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$Parameters
)
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default
{ $Command }
}
switch ($normalizedCommand) {
'help' { Show-DockerComposeHelp }
'b' { docker-compose build $Parameters }
'c' { docker-compose create $Parameters }
'd' { docker-compose down $Parameters }
'l' { docker-compose logs $Parameters }
's' { docker-compose start $Parameters }
'u' { docker-compose up $Parameters }
'ud' { docker-compose up --detach $Parameters }
'x' { docker-compose stop $Parameters }
'rmi' { docker-compose images }
default { docker-compose $Command $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-DockerComposeHelp {
Write-Host " DOCKER COMPOSE SHORTCUTS (dc)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help docker-compose OR dc help OR dc h" -ForegroundColor Gray
Write-Host ""
Write-Host "🐳 DOCKER COMPOSE COMMANDS" -ForegroundColor Yellow
Write-Host " dc <cmd> Docker Compose shortcuts:"
Write-Host " b = build c = create d = down"
Write-Host " l = logs s = start u = up"
Write-Host " ud = up --detach x = stop rmi = images"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " dc u Start all services (foreground)"
Write-Host " dc ud Start all services (background)"
Write-Host " dc d Stop and remove containers"
Write-Host " dc l -f myservice Follow logs for one service"
Write-Host ""
}
# ================================================================
# DOCKER SHORTCUTS
# ================================================================
<#
.SYNOPSIS
Docker command shortcuts
.DESCRIPTION
Quick aliases for common docker commands using single letters.
Alias: d
.PARAMETER Command
Short command:
start = Start Docker Desktop app
b = build i = images l = logs p = push
c = container cs = start lf = logs -f v = volume
cx = stop k = kill li = login ps = ps
t = tag lo = logout r = run psf = formatted ps
.PARAMETER Parameters
Additional parameters passed to docker command
.EXAMPLE
d start # Start Docker Desktop
d ps # docker ps
d psf # docker ps formatted (names, status, ports)
d i # docker images
d l mycontainer # docker logs mycontainer
d lf mycontainer # docker logs -f mycontainer (follow)
d cs mycontainer # docker container start mycontainer
.ALIAS
d
#>
function Invoke-Docker {
[Alias('d')]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$Parameters
)
$normalizedCommand = switch ($Command) {
'h' { 'help' }
's' { 'start' }
default
{ $Command }
}
switch ($normalizedCommand) {
'help' { Show-DockerHelp }
'start' {
Write-Host "🐳 Starting Docker Desktop..." -ForegroundColor Cyan
& open -a Docker
Write-Host "✅ Docker Desktop launched" -ForegroundColor Green
}
'b' { docker build $Parameters }
'c' { docker container $Parameters }
'cs' { docker container start $Parameters }
'cx' { docker container stop $Parameters }
'i' { docker images $Parameters }
't' { docker tag $Parameters }
'k' { docker kill $Parameters }
'l' { docker logs $Parameters }
'lf' { docker logs -f $Parameters }
'li' { docker login $Parameters }
'lo' { docker logout $Parameters }
'r' { docker run $Parameters }
'p' { docker push $Parameters }
'v' { docker volume $Parameters }
'ps' { docker ps $Parameters }
'psf' { docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" }
default { docker $Command $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-DockerHelp {
Write-Host " DOCKER SHORTCUTS (d)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help docker OR d help OR d h" -ForegroundColor Gray
Write-Host ""
Write-Host "🐳 DOCKER COMMANDS" -ForegroundColor Yellow
Write-Host " d <cmd> Docker command shortcuts:"
Write-Host " start = Launch Docker Desktop"
Write-Host " b = build c = container cs = start container"
Write-Host " cx = stop container i = images k = kill"
Write-Host " l = logs lf = logs -f li = login"
Write-Host " lo = logout p = push ps = ps"
Write-Host " psf = ps (formatted) r = run t = tag"
Write-Host " v = volume"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " d start Launch Docker Desktop"
Write-Host " d ps Show running containers"
Write-Host " d psf Formatted container list"
Write-Host " d l mycontainer Show logs for container"
Write-Host " d lf mycontainer Follow logs (stream)"
Write-Host ""
}
# ================================================================
# GIT SHORTCUTS
# ================================================================
<#
.SYNOPSIS
Git command shortcuts
.DESCRIPTION
Quick aliases for common git commands using single letters.
Includes branch management (br) and stash operations (st).
Alias: g
.PARAMETER Command
Short command:
a = add pl = pull ll = pretty log
b = branch ps = push m = merge
c = checkout r = rebase rs = reset
cl = clone s = status t = tag
co = commit f = fetch l = log
cm = add + commit (with message)
cp = add + commit + push (with message)
amend = amend last commit (optional new message)
br = branch management (new, del, clean, rename)
st = stash operations (list, show, pop, drop)
i = init
.PARAMETER Parameters
Additional parameters passed to git command
.EXAMPLE
g s # git status
g cm "Add feature" # git add . && commit
g cp "Fix bug" # git add . && commit && push
g br # list branches
g br new feature/auth # create new branch
g br clean # delete merged branches
g st # stash with timestamp
g st list # list stashes
g st pop # pop latest stash
.ALIAS
g
#>
function Invoke-Git {
[Alias('g')]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[Alias("cmd")]
[String]
$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[Alias("params")]
[String[]]
$Parameters
)
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default
{ $Command }
}
Switch ($normalizedCommand) {
# help
'help' { Show-GitHelp }
# add
'a' { git add $Parameters }
# branch (simple)
'b' { git branch $Parameters }
# checkout
'c' { git checkout $Parameters }
# clone repo
'cl' { git clone $Parameters }
# commit
'co' { git commit $Parameters }
# commit with add (cm = commit message)
'cm' {
if (-not $Parameters) {
Write-Host "Usage: g cm 'commit message'" -ForegroundColor Yellow
return
}
$commitMsg = $Parameters -join ' '
git add .
git commit -m $commitMsg
}
# commit and push with add (cp = commit push)
'cp' {
if (-not $Parameters) {
Write-Host "Usage: g cp 'commit message'" -ForegroundColor Yellow
return
}
$commitMsg = $Parameters -join ' '
git add .
git commit -m $commitMsg
git push
}
# amend last commit
'amend' {
git add .
if ($Parameters) {
$commitMsg = $Parameters -join ' '
git commit --amend -m $commitMsg
} else {
git commit --amend --no-edit
}
}
# branch management (br)
'br' {
if (-not $Parameters) {
# No params - list branches
git branch -v --sort=-committerdate
return
}
$subCmd = $Parameters[0]
$subParams = $Parameters[1..($Parameters.Length - 1)]
switch ($subCmd) {
'new' {
if (-not $subParams) {
Write-Host "Usage: g br new <branch-name>" -ForegroundColor Yellow
Write-Host "Examples:" -ForegroundColor Cyan
Write-Host " g br new feature/auth-system" -ForegroundColor Gray
Write-Host " g br new fix/login-bug" -ForegroundColor Gray
Write-Host " g br new chore/update-deps" -ForegroundColor Gray
return
}
$branchName = $subParams -join ' '
git checkout -b $branchName
}
'del' {
if (-not $subParams) {
Write-Host "Usage: g br del <branch-name> (safe delete, checks if merged)" -ForegroundColor Yellow
return
}
$branchName = $subParams -join ' '
git branch -d $branchName
}
'Del' {
if (-not $subParams) {
Write-Host "Usage: g br Del <branch-name> (FORCE delete, no checks)" -ForegroundColor Yellow
return
}
$branchName = $subParams -join ' '
git branch -D $branchName
}
'clean' {
Write-Host "Cleaning merged branches..." -ForegroundColor Cyan
$defaultBranch = git symbolic-ref refs/remotes/origin/HEAD 2>&1 | Where-Object { $_ -notmatch 'fatal' } | ForEach-Object { $_ -replace '^refs/remotes/origin/', '' }
if (-not $defaultBranch) {
if (git rev-parse --verify main 2>&1 | Select-Object -First 1 | Where-Object { $_ -notmatch 'fatal' }) { $defaultBranch = "main" }
elseif (git rev-parse --verify master 2>&1 | Select-Object -First 1 | Where-Object { $_ -notmatch 'fatal' }) { $defaultBranch = "master" }
else {
Write-Host "Could not determine default branch (main/master)" -ForegroundColor Red
return
}
}
$currentBranch = git rev-parse --abbrev-ref HEAD
$mergedBranches = git branch --merged $defaultBranch |
Where-Object { $_ -notmatch '^\*' -and $_ -notmatch $defaultBranch -and $_.Trim() -ne $currentBranch } |
ForEach-Object { $_.Trim() }
if ($mergedBranches) {
Write-Host "Branches merged into $defaultBranch (excluding current):" -ForegroundColor Yellow
$mergedBranches | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
Write-Host ""
$confirm = Read-Host "Delete these branches? (y/N)"
if ($confirm -eq 'y' -or $confirm -eq 'Y') {
$mergedBranches | ForEach-Object { git branch -d $_ }
Write-Host "✅ Cleaned merged branches" -ForegroundColor Green
} else {
Write-Host "Cancelled" -ForegroundColor Yellow
}
} else {
Write-Host "No merged branches to clean" -ForegroundColor Green
}
}
'rename' {
if (-not $subParams) {
Write-Host "Usage: g br rename <new-name>" -ForegroundColor Yellow
return
}
$newName = $subParams -join ' '
git branch -m $newName
}
default {
git branch $subCmd $subParams
}
}
}
# stash operations (st)
'st' {
if (-not $Parameters) {
# No params - stash with timestamp
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
git stash push -m "Stash at $timestamp"
return
}
$subCmd = $Parameters[0]
$subParams = $Parameters[1..($Parameters.Length - 1)]
# If subCmd is not a known stash command, treat it as a message
if ($subCmd -notin @('list', 'show', 'pop', 'apply', 'drop', 'clear')) {
$message = $Parameters -join ' '
git stash push -m $message
return
}
switch ($subCmd) {
'list' {
git stash list
}
'show' {
if ($subParams) {
$index = $subParams[0]
git stash show -p "stash@{$index}"
} else {
git stash show -p
}
}
'pop' {
git stash pop
}
'apply' {
if ($subParams) {
$index = $subParams[0]
git stash apply "stash@{$index}"
} else {
git stash apply
}
}
'drop' {
if ($subParams) {
$index = $subParams[0]
git stash drop "stash@{$index}"
} else {
Write-Host "Usage: g st drop <index>" -ForegroundColor Yellow
Write-Host "Example: g st drop 0" -ForegroundColor Gray
}
}
'clear' {
Write-Host "This will delete ALL stashes. Are you sure? (y/N)" -ForegroundColor Yellow
$confirm = Read-Host
if ($confirm -eq 'y' -or $confirm -eq 'Y') {
git stash clear
Write-Host "✅ Cleared all stashes" -ForegroundColor Green
} else {
Write-Host "Cancelled" -ForegroundColor Yellow
}
}
default {
git stash $subCmd $subParams
}
}
}
# fetch
'f' { git fetch $Parameters }
# init
'i' { git init $Parameters }
# log
'l' { git log $Parameters }
# pretty log
'll' { git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit }
# merge
'm' { git merge $Parameters }
# pull
'pl' { git pull $Parameters }
'plr' { git pull --prune $Parameters }
# push
'ps' { git push $Parameters }
'psu' { git push -u origin HEAD }
# rebase
'r' { git rebase $Parameters }
'rc' { git pull --recurse-submodules && git submodule update --checkout --recursive }
'rr' { git pull --recurse-submodules && git submodule update --remote --recursive }
# reset changes
'rs' { git reset $Parameters }
# remote
'rt' { git remote $Parameters }
# status
's' { git status $Parameters }
# tag
't' { git tag $Parameters }
# catchall
default { git $Command $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-GitHelp {
Write-Host " GIT SHORTCUTS (g)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help git OR g help OR g h" -ForegroundColor Gray
Write-Host ""
Write-Host "🔧 BASIC GIT COMMANDS" -ForegroundColor Yellow
Write-Host " g <cmd> Git command shortcuts:"
Write-Host " a = add f = fetch m = merge"
Write-Host " b = branch i = init pl = pull"
Write-Host " c = checkout l = log plr = pull --prune"
Write-Host " cl = clone ll = pretty log ps = push"
Write-Host " co = commit rs = reset psu = push -u origin HEAD"
Write-Host " cm = add + commit (with message). r = rebase"
Write-Host " cp = add + commit + push (with message). s = status"
Write-Host " amend = amend last commit (optional new message)"
Write-Host " t = tag"
Write-Host ""
Write-Host "🌿 BRANCH MANAGEMENT" -ForegroundColor Yellow
Write-Host " g br <cmd> Branch operations:"
Write-Host " g br list branches with info"
Write-Host " g br new <name> create branch (supports feature/, fix/, chore/)"
Write-Host " g br del <name> safe delete (checks if merged)"
Write-Host " g br Del <name> force delete (capital D)"
Write-Host " g br clean delete all merged branches"
Write-Host " g br rename <new> rename current branch"
Write-Host ""
Write-Host "📦 STASH OPERATIONS" -ForegroundColor Yellow
Write-Host " g st <cmd> Stash operations:"
Write-Host " g st stash with timestamp"
Write-Host " g st 'msg' stash with custom message"
Write-Host " g st list list all stashes"
Write-Host " g st show [n] show stash contents"
Write-Host " g st pop apply and remove latest stash"
Write-Host " g st drop <n> drop stash by index"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " g s git status"
Write-Host " g a . git add ."
Write-Host " g cm 'Add feature' Stage all and commit"
Write-Host " g cp 'Fix bug' Stage, commit, and push"
Write-Host " g amend Amend without changing message"
Write-Host " g br List branches sorted by date"
Write-Host " g br new feature/auth Create feature/auth and switch to it"
Write-Host " g br clean Delete merged branches"
Write-Host " g st 'WIP' Stash with message"
Write-Host " g st pop Apply latest stash"
Write-Host ""
}
# ================================================================
# HELP & STARTUP BANNER
# ================================================================
<#
.SYNOPSIS
Display comprehensive help for all profile features or specific topics
.DESCRIPTION
Shows all available shortcuts, tools, and features organized by category.
Can display help for specific topics or all topics.
.PARAMETER Topic
Optional topic to show help for. Available topics:
g, git, fn, fnm, n, npm, y, yarn, d, docker, dc, ob, obvio, bk, bookmark, profile, ziso, comp, companion
.EXAMPLE
Show-ProfileHelp
help
Show all available help topics
.EXAMPLE
help g
Show only git shortcuts
.EXAMPLE
help ob
Show only obvio project management commands
.ALIAS
help
#>
function Show-ProfileHelp {
[Alias('help')]
param(
[Parameter(Position=0)]
[ValidateSet('', 'g', 'git', 'fn', 'fnm', 'n', 'npm', 'y', 'yarn', 'd', 'docker', 'dc', 'docker-compose', 'c', 'copilot', 'ob', 'obvio', 'bk', 'bookmark', 'profile', 'ziso', 'cm', 'comp', 'companion', 'util', 'utilities')]
[string]$Topic = ''
)
# Normalize aliases to canonical names
$topicMap = @{
'g' = 'git'
'fn' = 'fnm'
'n' = 'npm'
'y' = 'yarn'
'd' = 'docker'
'dc' = 'docker-compose'
'c' = 'copilot'
'ob' = 'obvio'
'bk' = 'bookmark'
'cm' = 'companion'
'comp' = 'companion'
'util' = 'utilities'
'u' = 'utilities'
}
if ($topicMap.ContainsKey($Topic)) {
$Topic = $topicMap[$Topic]
}
# If no topic specified, show topic list
if (-not $Topic) {
Write-Host ""
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " POWERSHELL PROFILE - QUICK REFERENCE" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host "📖 AVAILABLE HELP TOPICS" -ForegroundColor Yellow
Write-Host " Type 'help <topic>' for detailed information on:"
Write-Host ""
Write-Host " git (g) Git shortcuts and workflows"
Write-Host " fnm (fn) Fast Node Manager shortcuts"
Write-Host " npm (n) npm command shortcuts"
Write-Host " yarn (y) yarn command shortcuts"
Write-Host " docker (d) Docker command shortcuts"
Write-Host " docker-compose (dc) Docker Compose shortcuts"
Write-Host " copilot (c) GitHub Copilot CLI shortcuts"
Write-Host " obvio (ob) Obvio project management"
Write-Host " bookmark (bk) Directory bookmarks"
Write-Host " companion (comp) Companion module navigation"
Write-Host " profile Profile management commands"
Write-Host " ziso ZoomISO production control"
Write-Host " utilities (util) Utility functions (temp, SetEnv, JSON, etc.)"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " help git Show all git shortcuts"
Write-Host " help ob Show obvio commands"
Write-Host " help n Show npm shortcuts"
Write-Host ""
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
return
}
# Show topic-specific help
Write-Host ""
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
switch ($Topic) {
'git' {
Show-GitHelp
}
'fnm' {
Show-FnmHelp
}
'npm' {
Show-NpmHelp
}
'yarn' {
Show-YarnHelp
}
'docker' {
Show-DockerHelp
}
'docker-compose' {
Show-DockerComposeHelp
}
'copilot' {
Show-CopilotHelp
}
'obvio' {
Show-ObvioHelp
}
'bookmark' {
Show-BookmarkHelp
}
'companion' {
Show-CompanionHelp
}
'profile' {
Show-ProfileSyncHelp
}
'ziso' {
Show-ZoomISOHelp
}
'utilities' {
Show-UtilitiesHelp
}
}
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
}
# ================================================================
# FOLDER NAVIGATION & FINDER
# ================================================================
# Load bookmarks from config (supports both old flat format and new grouped format)
$script:Bookmarks = @{}
$script:BookmarksGlobal = @{}
$script:BookmarksLocal = @{}
$script:BookmarkFile = $script:bookmarksPath
if (Test-Path $script:BookmarkFile) {
try {
$bookmarkData = Get-Content $script:BookmarkFile -Raw | ConvertFrom-Json
# Check if using old flat format or new grouped format
if ($bookmarkData.PSObject.Properties['global'] -or $bookmarkData.PSObject.Properties['local']) {
# New grouped format
if ($bookmarkData.global) {
$script:BookmarksGlobal = @{}
foreach ($prop in $bookmarkData.global.PSObject.Properties) {
$script:BookmarksGlobal[$prop.Name] = $prop.Value
}
}
if ($bookmarkData.local -and $bookmarkData.local.PSObject.Properties[$script:machineName]) {
$script:BookmarksLocal = @{}
foreach ($prop in $bookmarkData.local.$($script:machineName).PSObject.Properties) {
$script:BookmarksLocal[$prop.Name] = $prop.Value
}
}
}
else {
# Old flat format - auto-migrate to new format
Write-Host "📦 Migrating bookmarks to new format (local/global)..." -ForegroundColor Yellow
# Convert old format to hashtable
$script:BookmarksGlobal = @{}
foreach ($prop in $bookmarkData.PSObject.Properties) {
$script:BookmarksGlobal[$prop.Name] = $prop.Value
}
# Create new format structure
$newFormat = @{
global = $script:BookmarksGlobal
local = @{}
}
# Save migrated format
$newFormat | ConvertTo-Json -Depth 10 | Set-Content $script:BookmarkFile
Write-Host "✅ Migration complete! All bookmarks are now global." -ForegroundColor Green
Write-Host " Use 'bk add <name> <path>' to add machine-specific bookmarks." -ForegroundColor Cyan
}
# Create merged view (local overrides global)
$script:Bookmarks = $script:BookmarksGlobal.Clone()
foreach ($key in $script:BookmarksLocal.Keys) {
$script:Bookmarks[$key] = $script:BookmarksLocal[$key]
}
}
catch {
Write-Warning "Failed to load bookmarks: $_"
$script:Bookmarks = @{}
$script:BookmarksGlobal = @{}
$script:BookmarksLocal = @{}
}
}
else {
# No bookmark file - create new format structure
$newFormat = @{
global = @{}
local = @{}
}
$newFormat | ConvertTo-Json -Depth 10 | Set-Content $script:BookmarkFile
}
function Open-InFinder {
<#
.SYNOPSIS
Opens a folder in macOS Finder
.DESCRIPTION
Opens the specified folder in Finder. Supports both paths and folder shortcuts (downloads, dev, movies, docs).
If no path is provided, opens the current directory.
.PARAMETER Path
The path or folder shortcut to open in Finder. Defaults to current directory.
.EXAMPLE
f
Opens current directory in Finder
.EXAMPLE
f downloads
Opens Downloads folder in Finder using shortcut
.EXAMPLE
f ~/Desktop
Opens Desktop folder in Finder using path
.ALIAS
finder, f
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$true)]
[string]$Path = '.'
)
process {
# Check if it's a bookmark
if ($script:Bookmarks.ContainsKey($Path)) {
$bookmarkPath = $script:Bookmarks[$Path]
# Expand ~ to home directory
$resolvedPath = $bookmarkPath -replace '^~', $HOME
}
# Resolve the path - use current location if just '.'
elseif ($Path -eq '.') {
$resolvedPath = Get-Location | Select-Object -ExpandProperty Path
}
else {
try {
$resolvedPath = Resolve-Path -Path $Path -ErrorAction Stop | Select-Object -ExpandProperty Path
}
catch {
Write-Host "❗ Path not found: $Path" -ForegroundColor Red
return
}
}
# Verify path exists
if (-not (Test-Path -LiteralPath $resolvedPath)) {
Write-Host "❗ Folder not found: $resolvedPath" -ForegroundColor Red
return
}
# Open in Finder
Write-Host "📂 Opening in Finder: $resolvedPath" -ForegroundColor Cyan
& open -a Finder $resolvedPath
}
}
function Invoke-FolderNavigation {
<#
.SYNOPSIS
Navigate to bookmarked folders or paths in the terminal
.DESCRIPTION
Quickly navigate to bookmarked folders or any valid path. Use 'bookmark list' to see all available bookmarks.
.PARAMETER Folder
The bookmark name or path to navigate to (supports .., ., relative paths, absolute paths)
.EXAMPLE
to downloads
Navigate to Downloads folder using bookmark
.EXAMPLE
goto dev
Navigate to Development folder using bookmark
.EXAMPLE
to ..
Navigate to parent directory
.EXAMPLE
to ~/Desktop
Navigate to Desktop using path
.ALIAS
to, goto
#>
param(
[Parameter(Position=0, Mandatory=$true)]
[string]$Folder
)
# Check if it's a bookmark first
if ($script:Bookmarks.ContainsKey($Folder)) {
$bookmarkPath = $script:Bookmarks[$Folder]
# Expand ~ to home directory
$targetPath = $bookmarkPath -replace '^~', $HOME
}
# Otherwise treat as a path
else {
try {
$targetPath = Resolve-Path -Path $Folder -ErrorAction Stop | Select-Object -ExpandProperty Path
}
catch {
Write-Host "❗ Path or bookmark not found: $Folder. Use 'bk list' to see available bookmarks." -ForegroundColor Red
return
}
}
if (-not (Test-Path -LiteralPath $targetPath)) {
Write-Host "❗ Folder not found: $targetPath" -ForegroundColor Red
return
}
Set-Location -LiteralPath $targetPath
Write-Host "📁 Navigated to: $targetPath" -ForegroundColor Cyan
}
<#
.SYNOPSIS
Directory bookmark system for instant navigation
.DESCRIPTION
Save directories and jump to them with short names.
Bookmarks persist across terminal sessions.
.PARAMETER Action
Action to perform: add, remove, list, or bookmark name to jump to
.PARAMETER Name
Bookmark name (required for add/remove)
.PARAMETER F
Open bookmark location in Finder instead of navigating in terminal
.EXAMPLE
# Save current location
cd ~/Development/some/deep/path
bookmark add proj
# Later, jump back instantly in terminal
bookmark proj
# Or open in Finder
bookmark proj -F
# Manage bookmarks
bookmark list # Show all bookmarks
bookmark remove proj # Delete bookmark
.NOTES
Bookmarks stored in: $bookmarksPath
Use for any frequently-visited directories
Complements fixed shortcuts like oea, oew and companion nav (cm n hdtv)
#>
function bookmark {
[Alias('bk')]
Param(
[Parameter(Position = 0)]
[string]$Action,
[Parameter(Position = 1)]
[string]$Name,
[switch]$F,
[switch]$Global,
[switch]$Local,
[switch]$All,
[Parameter(ValueFromRemainingArguments)]
[string]$Machine
)
switch ($Action) {
'h' {
Show-BookmarkHelp
}
'help' {
Show-BookmarkHelp
}
'add' {
if (-not $Name) {
Write-Error "Usage: bookmark add <name> [-Global]"
return
}
$currentPath = Get-Location | Select-Object -ExpandProperty Path
# Replace home directory with ~ for portability
if ($currentPath.StartsWith($HOME)) {
$portablePath = $currentPath -replace [regex]::Escape($HOME), '~'
} else {
$portablePath = $currentPath
}
# Load current bookmark file
$bookmarkData = Get-Content $script:BookmarkFile -Raw | ConvertFrom-Json
if ($Global) {
# Add to global section
if (-not $bookmarkData.global) {
$bookmarkData | Add-Member -MemberType NoteProperty -Name 'global' -Value (New-Object PSObject)
}
$bookmarkData.global | Add-Member -MemberType NoteProperty -Name $Name -Value $portablePath -Force
$script:BookmarksGlobal[$Name] = $portablePath
$script:Bookmarks[$Name] = $portablePath
Write-Host "✅ Global bookmark '$Name' saved: $portablePath" -ForegroundColor Green
}
else {
# Add to local section for this machine
if (-not $bookmarkData.local) {
$bookmarkData | Add-Member -MemberType NoteProperty -Name 'local' -Value (New-Object PSObject)
}
if (-not $bookmarkData.local.PSObject.Properties[$script:machineName]) {
$bookmarkData.local | Add-Member -MemberType NoteProperty -Name $script:machineName -Value (New-Object PSObject)
}
$bookmarkData.local.$($script:machineName) | Add-Member -MemberType NoteProperty -Name $Name -Value $portablePath -Force
$script:BookmarksLocal[$Name] = $portablePath
$script:Bookmarks[$Name] = $portablePath
Write-Host "✅ Local bookmark '$Name' saved: $portablePath" -ForegroundColor Green
Write-Host " Machine: $script:machineName" -ForegroundColor DarkGray
}
# Save to file
$bookmarkData | ConvertTo-Json -Depth 10 | Set-Content $script:BookmarkFile
}
'remove' {
if (-not $Name) {
Write-Error "Usage: bookmark remove <name> [-Global]"
return
}
# Load current bookmark file
$bookmarkData = Get-Content $script:BookmarkFile -Raw | ConvertFrom-Json
$removed = $false
if ($Global) {
# Remove from global only
if ($bookmarkData.global.PSObject.Properties[$Name]) {
$bookmarkData.global.PSObject.Properties.Remove($Name)
$script:BookmarksGlobal.Remove($Name)
$removed = $true
Write-Host "✅ Global bookmark '$Name' removed" -ForegroundColor Green
}
else {
Write-Host "❌ Bookmark '$Name' not found in global bookmarks" -ForegroundColor Yellow
return
}
}
else {
# Try local first, then global
if ($bookmarkData.local.PSObject.Properties[$script:machineName] -and
$bookmarkData.local.$($script:machineName).PSObject.Properties[$Name]) {
$bookmarkData.local.$($script:machineName).PSObject.Properties.Remove($Name)
$script:BookmarksLocal.Remove($Name)
$removed = $true
Write-Host "✅ Local bookmark '$Name' removed (machine: $script:machineName)" -ForegroundColor Green
}
elseif ($bookmarkData.global.PSObject.Properties[$Name]) {
$bookmarkData.global.PSObject.Properties.Remove($Name)
$script:BookmarksGlobal.Remove($Name)
$removed = $true
Write-Host "✅ Global bookmark '$Name' removed" -ForegroundColor Green
}
else {
Write-Host "❌ Bookmark '$Name' not found" -ForegroundColor Yellow
return
}
}
if ($removed) {
# Rebuild merged view
$script:Bookmarks = $script:BookmarksGlobal.Clone()
foreach ($key in $script:BookmarksLocal.Keys) {
$script:Bookmarks[$key] = $script:BookmarksLocal[$key]
}
# Save to file
$bookmarkData | ConvertTo-Json -Depth 10 | Set-Content $script:BookmarkFile
}
}
'list' {
if ($script:Bookmarks.Count -eq 0) {
Write-Host "No bookmarks saved" -ForegroundColor Yellow
return
}
if ($Local) {
# Show only local bookmarks for this machine
if ($script:BookmarksLocal.Count -eq 0) {
Write-Host "No local bookmarks for this machine" -ForegroundColor Yellow
return
}
Write-Host "`nLocal Bookmarks (machine: $script:machineName):" -ForegroundColor Green
foreach ($key in $script:BookmarksLocal.Keys | Sort-Object) {
Write-Host " $key [L] -> $($script:BookmarksLocal[$key])"
}
}
elseif ($Global) {
# Show only global bookmarks
if ($script:BookmarksGlobal.Count -eq 0) {
Write-Host "No global bookmarks" -ForegroundColor Yellow
return
}
Write-Host "`nGlobal Bookmarks:" -ForegroundColor Green
foreach ($key in $script:BookmarksGlobal.Keys | Sort-Object) {
Write-Host " $key -> $($script:BookmarksGlobal[$key])"
}
}
elseif ($All) {
# Show all machines' bookmarks (debugging)
$bookmarkData = Get-Content $script:BookmarkFile -Raw | ConvertFrom-Json
Write-Host "`nGlobal Bookmarks:" -ForegroundColor Green
if ($bookmarkData.global.PSObject.Properties.Count -gt 0) {
foreach ($prop in $bookmarkData.global.PSObject.Properties | Sort-Object Name) {
Write-Host " $($prop.Name) -> $($prop.Value)"
}
}
else {
Write-Host " (none)" -ForegroundColor DarkGray
}
Write-Host "`nLocal Bookmarks (all machines):" -ForegroundColor Green
if ($bookmarkData.local.PSObject.Properties.Count -gt 0) {
foreach ($machineProp in $bookmarkData.local.PSObject.Properties | Sort-Object Name) {
Write-Host " [$($machineProp.Name)]:" -ForegroundColor Cyan
foreach ($bookmarkProp in $machineProp.Value.PSObject.Properties | Sort-Object Name) {
Write-Host " $($bookmarkProp.Name) -> $($bookmarkProp.Value)"
}
}
}
else {
Write-Host " (none)" -ForegroundColor DarkGray
}
}
else {
# Show merged view (default)
Write-Host "`nBookmarks:" -ForegroundColor Green
foreach ($key in $script:Bookmarks.Keys | Sort-Object) {
$isLocal = $script:BookmarksLocal.ContainsKey($key)
if ($isLocal) {
Write-Host " $key [L] -> $($script:Bookmarks[$key])"
}
else {
Write-Host " $key -> $($script:Bookmarks[$key])"
}
}
Write-Host "`n [L] = Local to this machine ($script:machineName)" -ForegroundColor DarkGray
}
}
default {
if ($script:Bookmarks.ContainsKey($Action)) {
$bookmarkPath = $script:Bookmarks[$Action]
# Expand ~ to home directory
$path = $bookmarkPath -replace '^~', $HOME
if ($F) {
# Open in Finder
Write-Host "📂 Opening in Finder: $path" -ForegroundColor Cyan
& open -a Finder $path
}
else {
# Navigate in terminal
Set-Location $path
Write-Host "📁 Navigated to: $path" -ForegroundColor Cyan
}
}
else {
Write-Host "Unknown bookmark: $Action" -ForegroundColor Yellow
Write-Host "Usage: bookmark add|remove|list [name] [-Global]" -ForegroundColor Cyan
Write-Host " Or: bookmark <name> [-F] to jump to saved location" -ForegroundColor Cyan
}
}
}
}
# Register tab completers for bookmark command
Register-ArgumentCompleter -CommandName 'bookmark' -ParameterName 'Action' -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
$actions = @('add', 'remove', 'list')
$completions = $actions + $script:Bookmarks.Keys
$completions | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
Register-ArgumentCompleter -CommandName 'bookmark' -ParameterName 'Name' -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
if ($fakeBoundParameters['Action'] -eq 'remove') {
$script:Bookmarks.Keys | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
}
# Set aliases
Set-Alias -Name to -Value Invoke-FolderNavigation
Set-Alias -Name goto -Value Invoke-FolderNavigation
Set-Alias -Name cmd -Value Invoke-FolderNavigation
Set-Alias -Name finder -Value Open-InFinder
Set-Alias -Name f -Value Open-InFinder
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-BookmarkHelp {
Write-Host " BOOKMARKS (bk)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help bookmark OR bk help OR bk h" -ForegroundColor Gray
Write-Host " Aliases: bookmark, bk" -ForegroundColor Gray
Write-Host ""
Write-Host "🔖 BOOKMARK COMMANDS" -ForegroundColor Yellow
Write-Host " bookmark (bk) add <name> Save current directory (local to machine)"
Write-Host " bookmark (bk) add <name> -Global Save as global bookmark (all machines)"
Write-Host " bookmark (bk) <name> Jump to saved directory"
Write-Host " bookmark (bk) <name> -F Open bookmark in Finder"
Write-Host " bookmark (bk) list Show all bookmarks (merged view)"
Write-Host " bookmark (bk) list -Local Show only local bookmarks"
Write-Host " bookmark (bk) list -Global Show only global bookmarks"
Write-Host " bookmark (bk) list -All Show all machines' bookmarks"
Write-Host " bookmark (bk) remove <name> Delete bookmark (local, then global)"
Write-Host " bookmark (bk) remove <name> -Global Delete specifically from global"
Write-Host ""
Write-Host "💡 LOCAL VS GLOBAL" -ForegroundColor Yellow
Write-Host " Local bookmarks [L] - Machine-specific paths, stored per hostname"
Write-Host " Global bookmarks - Shared across all machines (downloads, docs, etc.)"
Write-Host " Machine name: $script:machineName" -ForegroundColor DarkGray
Write-Host " Override: SetEnv 'MACHINE_NAME' 'custom-name'" -ForegroundColor DarkGray
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " bk add myproject Save current directory (local to this machine)"
Write-Host " bk add downloads -Global Save Downloads folder (shared across machines)"
Write-Host " bk myproject Jump to myproject bookmark"
Write-Host " bk myproject -F Open myproject in Finder"
Write-Host " bk list Show all bookmarks with [L] indicator"
Write-Host " bk remove myproject Delete myproject (checks local first)"
Write-Host ""
}
# ================================================================
# FNM (Fast Node Manager) SHORTCUTS
# ================================================================
# fnm shortcuts following same pattern as git (g), copilot (c), docker (d)
# Usage:
# fn → fnm list
# fn u → fnm use --install-if-missing
# fn i 20 → fnm install 20
# ================================================================
<#
.SYNOPSIS
fnm (Fast Node Manager) command shortcuts
.DESCRIPTION
Quick aliases for fnm commands using single letters.
Alias: fn
.PARAMETER Command
Short command:
u = use --install-if-missing
i = install [version]
il = install --lts
l = list
lr = list-remote --lts
ll = list-remote --lts --latest
.PARAMETER Parameters
Additional parameters passed to fnm command
.EXAMPLE
fn # fnm list
fn u # fnm use --install-if-missing
fn i 20 # fnm install 20
fn il # fnm install --lts
fn l # fnm list
fn lr # fnm list-remote --lts
fn ll # fnm list-remote --lts --latest
.ALIAS
fn
#>
function Invoke-Fnm {
[Alias('fn')]
Param(
[Parameter(Mandatory = $false, Position = 0)]
[string]$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$Parameters
)
# If no command, show installed versions
if (-not $Command) {
fnm list
return
}
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default
{ $Command }
}
switch ($normalizedCommand) {
'help' { Show-FnmHelp }
'u' {
# Check if a version file exists in the current directory
$hasNvmrc = Test-Path ".nvmrc"
$hasNodeVersion = Test-Path ".node-version"
if (-not $hasNvmrc -and -not $hasNodeVersion) {
Write-Host "⚠️ No .nvmrc or .node-version file found in this directory." -ForegroundColor Yellow
Write-Host ""
Write-Host "Tip: Create a version file to pin Node.js version for this project:" -ForegroundColor Cyan
Write-Host " echo '20' > .nvmrc # Pin to Node 20.x" -ForegroundColor Gray
Write-Host " echo '18.16.0' > .nvmrc # Pin to exact version" -ForegroundColor Gray
Write-Host " echo 'lts/*' > .nvmrc # Use latest LTS" -ForegroundColor Gray
Write-Host ""
}
fnm use --install-if-missing $Parameters
}
'i' { fnm install $Parameters }
'il' { fnm install --lts $Parameters }
'l' { fnm list $Parameters }
'lr' { fnm list-remote --lts $Parameters }
'll' { fnm list-remote --lts --latest $Parameters }
default { fnm $Command $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-FnmHelp {
Write-Host " FNM SHORTCUTS (fn)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help fnm OR fn help OR fn h" -ForegroundColor Gray
Write-Host ""
Write-Host "📦 FNM (FAST NODE MANAGER)" -ForegroundColor Yellow
Write-Host " fn <cmd> fnm shortcuts:"
Write-Host " (none) = list List installed node versions"
Write-Host " u = use --auto Use node version (auto-install if missing)"
Write-Host " i = install [ver] Install specific version"
Write-Host " il = install --lts Install latest LTS"
Write-Host " l = list List installed versions"
Write-Host " lr = list-remote --lts Show available LTS versions"
Write-Host " ll = list-remote --lts --latest Show latest LTS only"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " fn List installed node versions"
Write-Host " fn u Use node version (auto-install if missing)"
Write-Host " fn i 20 Install node 20"
Write-Host " fn il Install latest LTS"
Write-Host " fn lr Show all available LTS versions"
Write-Host ""
}
# ================================================================
# SMART PACKAGE INSTALL
# ================================================================
# Auto-detect npm or yarn based on lock files
# Usage:
# install → auto-detect and install
# install -f → auto-detect and clean install
# ================================================================
<#
.SYNOPSIS
Smart package install - auto-detects npm or yarn
.DESCRIPTION
Automatically detects which package manager to use based on lock files:
- package-lock.json → npm install
- yarn.lock → yarn install
- Both present → error (ambiguous)
- Neither present → defaults to npm
With -Force flag, uses clean install (npm ci or yarn install --frozen-lockfile)
.PARAMETER Force
Use clean install (npm ci or yarn install --frozen-lockfile)
.EXAMPLE
install # Auto-detect and install
install -f # Auto-detect and clean install
.ALIAS
install
#>
function Install-Packages {
[Alias('install')]
Param(
[Parameter(Mandatory = $false)]
[Alias('f')]
[switch]$Force
)
$hasPackageLock = Test-Path "package-lock.json"
$hasYarnLock = Test-Path "yarn.lock"
# Error if both lock files exist
if ($hasPackageLock -and $hasYarnLock) {
Write-Error "Conflicting lock files found!"
Write-Host "Both package-lock.json and yarn.lock exist in this directory." -ForegroundColor Red
Write-Host "Please remove one of the lock files or specify the package manager:" -ForegroundColor Yellow
Write-Host " npm install (for npm)" -ForegroundColor Cyan
Write-Host " yarn install (for yarn)" -ForegroundColor Cyan
return
}
if ($hasYarnLock) {
if ($Force) {
Write-Host "Running: yarn install --frozen-lockfile" -ForegroundColor Cyan
yarn install --frozen-lockfile
} else {
Write-Host "Running: yarn install" -ForegroundColor Cyan
yarn install
}
}
elseif ($hasPackageLock) {
if ($Force) {
Write-Host "Running: npm ci" -ForegroundColor Cyan
npm ci
} else {
Write-Host "Running: npm install" -ForegroundColor Cyan
npm install
}
}
else {
Write-Warning "No lock file found (package-lock.json or yarn.lock)"
Write-Host "Defaulting to npm install..." -ForegroundColor Yellow
npm install
}
}
# ================================================================
# NPM SHORTCUTS
# ================================================================
# Quick shortcuts for npm commands
# Usage:
# n i → npm install
# n d → npm run dev
# n r build → npm run build
# ================================================================
<#
.SYNOPSIS
npm command shortcuts
.DESCRIPTION
Quick aliases for npm commands using single letters.
Alias: n
.PARAMETER Command
Short command:
i = install id = install dev o = outdated
ci = ci (clean) r = run [script] u = update
d = run dev s = start
b = run build. t = run test
.PARAMETER Parameters
Additional parameters passed to npm command
.EXAMPLE
n i # npm install
n d # npm run dev
n r build:prod # npm run build:prod
n r test --watch # npm run test --watch
.ALIAS
n
#>
function Invoke-Npm {
[Alias('n')]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$Parameters
)
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default
{ $Command }
}
switch ($normalizedCommand) {
'help' { Show-NpmHelp }
'i' { npm install $Parameters }
'id' { npm install --save-dev $Parameters }
'ci' { npm ci $Parameters }
'd' { npm run dev $Parameters }
'b' { npm run build $Parameters }
't' { npm run test $Parameters }
's' { npm start $Parameters }
'r' { npm run $Parameters }
'o' { npm outdated $Parameters }
'u' { npm update $Parameters }
default { npm $Command $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-NpmHelp {
Write-Host " NPM SHORTCUTS (n)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help npm OR n help OR n h" -ForegroundColor Gray
Write-Host ""
Write-Host "📦 NPM COMMANDS" -ForegroundColor Yellow
Write-Host " n <cmd> npm command shortcuts:"
Write-Host " i = install ci = ci (clean) d = run dev"
Write-Host " b = run build t = run test s = start"
Write-Host " r = run [script] o = outdated u = update"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " n i npm install"
Write-Host " n d npm run dev"
Write-Host " n r build:prod npm run build:prod"
Write-Host " n r test --watch npm run test --watch"
Write-Host ""
}
# ================================================================
# YARN SHORTCUTS
# ================================================================
# Quick shortcuts for yarn commands
# Usage:
# y → yarn (install)
# y d → yarn dev
# y r start → yarn run start
# ================================================================
<#
.SYNOPSIS
yarn command shortcuts
.DESCRIPTION
Quick aliases for yarn commands using single letters.
Alias: y
.PARAMETER Command
Short command:
(default) = yarn (install)
i = install r = run [script] ui = upgrade-interactive
d = run dev s = start
b = run build t = run test
.PARAMETER Parameters
Additional parameters passed to yarn command
.EXAMPLE
y # yarn (install)
y d # yarn dev
y r start:local # yarn run start:local
y ui # yarn upgrade-interactive (check outdated packages)
y r build --prod # yarn run build --prod
.ALIAS
y
#>
function Invoke-Yarn {
[Alias('y')]
Param(
[Parameter(Mandatory = $false, Position = 0)]
[string]$Command,
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$Parameters
)
# If no command provided, just run yarn (install)
if (-not $Command) {
yarn
return
}
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default
{ $Command }
}
switch ($normalizedCommand) {
'help' { Show-YarnHelp }
'i' { yarn install $Parameters }
'd' { yarn dev $Parameters }
'b' { yarn build $Parameters }
't' { yarn test $Parameters }
's' { yarn start $Parameters }
'r' { yarn run $Parameters }
'ui' { yarn upgrade-interactive $Parameters }
default { yarn $Command $Parameters }
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-YarnHelp {
Write-Host " YARN SHORTCUTS (y)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help yarn OR y help OR y h" -ForegroundColor Gray
Write-Host ""
Write-Host "📦 YARN COMMANDS" -ForegroundColor Yellow
Write-Host " y <cmd> yarn command shortcuts:"
Write-Host " (none) = install i = install d = run dev"
Write-Host " b = run build t = run test s = start"
Write-Host " r = run [script] ui = upgrade-interactive"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " y yarn (install)"
Write-Host " y d yarn dev"
Write-Host " y r start:local yarn run start:local"
Write-Host " y ui yarn upgrade-interactive"
Write-Host ""
}
# ================================================================
# PROFILE MANAGEMENT & GIST SYNC
# ================================================================
# Simplified sync system: Auto-discover all files, no state tracking
# Path to ignored dependencies file (machine-specific, not synced)
$script:IgnoredDepsPath = Join-Path $script:ProfileDir '.ignored-dependencies.json'
# Helper: Load ignored dependencies list
function Get-IgnoredDependencies {
if (-not (Test-Path $script:IgnoredDepsPath)) {
return @()
}
try {
$data = Get-Content $script:IgnoredDepsPath -Raw | ConvertFrom-Json
return $data.PSObject.Properties.Name
} catch {
Write-Warning "Failed to load ignored dependencies: $_"
return @()
}
}
# Helper: Save ignored dependencies list
function Save-IgnoredDependencies {
param([string[]]$Dependencies)
$obj = [PSCustomObject]@{}
foreach ($dep in $Dependencies) {
if ($dep) { # Skip null/empty entries
$obj | Add-Member -NotePropertyName $dep -NotePropertyValue $true
}
}
$obj | ConvertTo-Json | Set-Content $script:IgnoredDepsPath -Encoding UTF8
}
# Helper: Check if a dependency is ignored
function Test-DependencyIgnored {
param([string]$Dependency)
$ignored = Get-IgnoredDependencies
return $ignored -contains $Dependency
}
# Helper: Process and save files from a gist response
function Save-GistFiles {
param(
[Parameter(Mandatory)]
$Gist,
[string]$GistType = "public"
)
$updatedCount = 0
foreach ($gistFile in $Gist.files.PSObject.Properties) {
$flattenedName = $gistFile.Name
$content = $gistFile.Value.content
# Determine actual file path
if ($flattenedName -eq 'profile.ps1') {
$fullPath = $PROFILE
} else {
$actualPath = $flattenedName -replace '--', '/'
$fullPath = Join-Path $script:ProfileDir $actualPath
}
# Ensure parent directory exists
$parentDir = Split-Path -Parent $fullPath
if ($parentDir -and -not (Test-Path $parentDir)) {
New-Item -ItemType Directory -Path $parentDir -Force | Out-Null
}
# Save file
Set-Content -Path $fullPath -Value $content -Encoding UTF8
$updatedCount++
}
return $updatedCount
}
# Helper: Determine which gist a file belongs to based on its path
function Get-GistForFile {
param([string]$FlattenedName)
# Files in private-modules go to private gist
if ($FlattenedName -like 'private-modules--*') {
return 'private'
}
# Everything else goes to public gist
return 'public'
}
# Helper: Convert flattened gist name to user-friendly path
function ConvertTo-UserFriendlyPath {
param([string]$FlattenedName)
# Convert double-dash back to forward slash for display
return $FlattenedName -replace '--', '/'
}
# Helper: Calculate hash of file content
function Get-FileContentHash {
param([string]$Path)
if (-not (Test-Path $Path)) { return $null }
$content = Get-Content -Path $Path -Raw -Encoding UTF8
# Handle empty files - use empty string for hash calculation
if ($null -eq $content) {
$content = ''
}
$hash = [System.Security.Cryptography.SHA256]::Create()
$bytes = [System.Text.Encoding]::UTF8.GetBytes($content)
$hashBytes = $hash.ComputeHash($bytes)
return [BitConverter]::ToString($hashBytes).Replace('-', '').ToLower()
}
# Helper: Auto-discover all profile files (no state file needed)
function Get-ProfileFilesState {
$files = @{}
# Exclusion list
$excludeFiles = @(
'Microsoft.PowerShell_profile.ps1', # Syncs as profile.ps1 instead
'.ignored-dependencies.json', # Machine-specific, not synced
'.DS_Store',
'.gitignore',
'.gitignore.backup'
)
# Root files
Get-ChildItem -Path $script:ProfileDir -File | ForEach-Object {
if ($excludeFiles -notcontains $_.Name) {
$files[$_.Name] = $_.FullName
}
}
# profile.ps1 special mapping
if (Test-Path $PROFILE) {
$files['profile.ps1'] = $PROFILE
}
# modules/*.ps1
$modulesDir = Join-Path $script:ProfileDir 'modules'
if (Test-Path $modulesDir) {
Get-ChildItem -Path $modulesDir -Filter '*.ps1' -File | ForEach-Object {
$flattenedName = "modules--$($_.Name)"
$files[$flattenedName] = $_.FullName
}
}
# private-modules/**/*.ps1 (🔒 SECURITY: These go to private gist only)
# Supports nested subdirectories (e.g., private-modules/powershell/modules/obvio.ps1)
$privateModulesDir = Join-Path $script:ProfileDir 'private-modules'
if (Test-Path $privateModulesDir) {
Get-ChildItem -Path $privateModulesDir -Filter '*.ps1' -File -Recurse | ForEach-Object {
# Get relative path from private-modules directory
$relativePath = $_.FullName.Substring($privateModulesDir.Length + 1)
# Replace directory separators with double-dash
$flattenedName = "private-modules--$($relativePath -replace '[/\\]', '--')"
$files[$flattenedName] = $_.FullName
}
}
# scripts/*.ps1
$scriptsDir = Join-Path $script:ProfileDir 'scripts'
if (Test-Path $scriptsDir) {
Get-ChildItem -Path $scriptsDir -Filter '*.ps1' -File | ForEach-Object {
$flattenedName = "scripts--$($_.Name)"
$files[$flattenedName] = $_.FullName
}
}
return $files
}
# Helper: Get list of files currently in gist (names only)
function Get-GistFileList {
param(
[string]$GitHubToken,
[string]$GistId = $script:GistId
)
if (-not $GitHubToken) {
Write-Warning "Cannot fetch gist file list: GITHUB_GIST_TOKEN not set"
return @()
}
try {
$gistUrl = "https://api.github.com/gists/$GistId"
$headers = @{
'User-Agent' = 'PowerShell'
'Authorization' = "Bearer $GitHubToken"
}
$gist = Invoke-RestMethod -Uri $gistUrl -Headers $headers
return $gist.files.PSObject.Properties.Name
}
catch {
Write-Warning "Failed to fetch gist file list: $_"
return @()
}
}
function profile {
Param(
[Parameter(Mandatory = $false, Position = 0)]
[String]$Command,
[Parameter(Mandatory = $false, Position = 1)]
[String]$FilePath,
[switch]$Force
)
# Normalize short aliases to full names
$normalizedCommand = switch ($Command) {
'h' { 'help' }
'ss' { 'setup-scripts'}
'push' { 'ps' }
'pull' { 'pl' }
'w' { 'web'}
'p' { 'path'}
'd' { 'dir'}
'f' { 'folder'}
default { $Command }
}
Switch ($normalizedCommand) {
# help
'help' {
Show-ProfileSyncHelp
}
# Push all profile files to Gist (auto-discover)
'ps' {
$GitHubToken = $env:GITHUB_GIST_TOKEN
if (-not $GitHubToken) {
Write-Error "GITHUB_GIST_TOKEN environment variable is not set. Create a token at https://github.com/settings/tokens with 'gist' scope."
return
}
try {
# Prepare headers
$headers = @{
'User-Agent' = 'PowerShell'
'Authorization' = "Bearer $GitHubToken"
'Content-Type' = 'application/json'
}
# Auto-discover all files
Write-Host "🔍 Auto-discovering files..." -ForegroundColor Cyan
$allFiles = Get-ProfileFilesState
# Separate files into public and private (🔒 SECURITY CRITICAL)
$publicFiles = @{}
$privateFiles = @{}
foreach ($flattenedName in $allFiles.Keys) {
$fullPath = $allFiles[$flattenedName]
# Read content
if (Test-Path $fullPath) {
$content = Get-Content -Path $fullPath -Raw -Encoding UTF8 -ErrorAction SilentlyContinue
if ($null -eq $content) { $content = '' }
# Route to appropriate gist (🔒 SECURITY: private-modules go to private gist)
$gistType = Get-GistForFile -FlattenedName $flattenedName
if ($gistType -eq 'private') {
$privateFiles[$flattenedName] = @{ content = $content }
} else {
$publicFiles[$flattenedName] = @{ content = $content }
}
}
}
$publicCount = $publicFiles.Count
$privateCount = $privateFiles.Count
Write-Host "Found $publicCount public file(s) and $privateCount private file(s) to sync" -ForegroundColor Gray
# Sync to PUBLIC GIST
if ($publicCount -gt 0) {
Write-Host "`n📦 Syncing to PUBLIC gist..." -ForegroundColor Cyan
$publicGistUrl = "https://api.github.com/gists/$($script:GistId)"
# Deletion detection for public gist
Write-Host "Checking for files to delete from public gist..." -ForegroundColor Gray
$publicGistFiles = Get-GistFileList -GitHubToken $GitHubToken -GistId $script:GistId
$publicLocalFiles = $publicFiles.Keys
$publicFilesToDelete = $publicGistFiles | Where-Object { $_ -notin $publicLocalFiles }
if ($publicFilesToDelete.Count -gt 0) {
Write-Host "⚠️ $($publicFilesToDelete.Count) file(s) in public gist but not locally" -ForegroundColor Yellow
$shouldDelete = $false
if ($Force) {
Write-Host "🔥 -Force flag set: Deleting files automatically" -ForegroundColor Yellow
$shouldDelete = $true
} else {
$response = Read-Host "Remove $($publicFilesToDelete.Count) file(s) from public gist? [y/N]"
$shouldDelete = $response -eq 'y' -or $response -eq 'Y'
}
if ($shouldDelete) {
foreach ($file in $publicFilesToDelete) {
$publicFiles[$file] = $null
}
Write-Host "✓ Will delete $($publicFilesToDelete.Count) file(s)" -ForegroundColor Cyan
} else {
Write-Host "✓ Skipping deletion" -ForegroundColor Cyan
}
} else {
Write-Host "✓ No files to delete from public gist" -ForegroundColor Gray
}
$publicBody = @{ files = $publicFiles } | ConvertTo-Json -Depth 3
$null = Invoke-RestMethod -Uri $publicGistUrl -Method Patch -Headers $headers -Body $publicBody
Write-Host "✅ Successfully saved $publicCount file(s) to PUBLIC gist!" -ForegroundColor Green
} else {
Write-Host "`n📦 No public files to sync" -ForegroundColor Gray
}
# Sync to PRIVATE GIST
if ($privateCount -gt 0) {
if (-not $script:PrivateGistId) {
Write-Host "`n⚠️ Skipping private files: POWERSHELL_PRIVATE_GIST_ID not set" -ForegroundColor Yellow
Write-Host " Set environment variable: POWERSHELL_PRIVATE_GIST_ID=<gist-id>" -ForegroundColor Gray
} else {
Write-Host "`n🔒 Syncing to PRIVATE gist..." -ForegroundColor Cyan
$privateGistUrl = "https://api.github.com/gists/$($script:PrivateGistId)"
# Deletion detection for private gist
Write-Host "Checking for files to delete from private gist..." -ForegroundColor Gray
$privateGistFiles = Get-GistFileList -GitHubToken $GitHubToken -GistId $script:PrivateGistId
$privateLocalFiles = $privateFiles.Keys
$privateFilesToDelete = $privateGistFiles | Where-Object { $_ -notin $privateLocalFiles }
if ($privateFilesToDelete.Count -gt 0) {
Write-Host "⚠️ $($privateFilesToDelete.Count) file(s) in private gist but not locally" -ForegroundColor Yellow
$shouldDelete = $false
if ($Force) {
Write-Host "🔥 -Force flag set: Deleting files automatically" -ForegroundColor Yellow
$shouldDelete = $true
} else {
$response = Read-Host "Remove $($privateFilesToDelete.Count) file(s) from private gist? [y/N]"
$shouldDelete = $response -eq 'y' -or $response -eq 'Y'
}
if ($shouldDelete) {
foreach ($file in $privateFilesToDelete) {
$privateFiles[$file] = $null
}
Write-Host "✓ Will delete $($privateFilesToDelete.Count) file(s)" -ForegroundColor Cyan
} else {
Write-Host "✓ Skipping deletion" -ForegroundColor Cyan
}
} else {
Write-Host "✓ No files to delete from private gist" -ForegroundColor Gray
}
$privateBody = @{ files = $privateFiles } | ConvertTo-Json -Depth 3
$null = Invoke-RestMethod -Uri $privateGistUrl -Method Patch -Headers $headers -Body $privateBody
Write-Host "✅ Successfully saved $privateCount file(s) to PRIVATE gist!" -ForegroundColor Green
}
} else {
Write-Host "`n🔒 No private files to sync" -ForegroundColor Gray
}
Write-Host "`n🎉 Sync complete!" -ForegroundColor Green
}
catch {
Write-Error "Failed to save profile: $_"
}
}
# Pull all files from Gist (creates backup)
'pl' {
try {
$headers = @{ 'User-Agent' = 'PowerShell' }
# Create backup first
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$backupParent = Join-Path $script:ProfileDir "backup"
$backupDir = Join-Path $backupParent $timestamp
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
Write-Host "Creating backup in: backup/$timestamp" -ForegroundColor Yellow
# Backup current files
if (Test-Path $PROFILE) {
Copy-Item -Path $PROFILE -Destination (Join-Path $backupDir (Split-Path -Leaf $PROFILE)) -Force
Write-Host " ✓ Backed up profile" -ForegroundColor Gray
}
$modulesDir = Join-Path $script:ProfileDir 'modules'
if (Test-Path $modulesDir) {
$backupModulesDir = Join-Path $backupDir 'modules'
Copy-Item -Path $modulesDir -Destination $backupModulesDir -Recurse -Force
Write-Host " ✓ Backed up modules/" -ForegroundColor Gray
}
$privateModulesDir = Join-Path $script:ProfileDir 'private-modules'
if (Test-Path $privateModulesDir) {
$backupPrivateModulesDir = Join-Path $backupDir 'private-modules'
Copy-Item -Path $privateModulesDir -Destination $backupPrivateModulesDir -Recurse -Force
Write-Host " ✓ Backed up private-modules/" -ForegroundColor Gray
}
$scriptsDir = Join-Path $script:ProfileDir 'scripts'
if (Test-Path $scriptsDir) {
$backupScriptsDir = Join-Path $backupDir 'scripts'
Copy-Item -Path $scriptsDir -Destination $backupScriptsDir -Recurse -Force
Write-Host " ✓ Backed up scripts/" -ForegroundColor Gray
}
# Pull from PUBLIC GIST
Write-Host "`n📦 Pulling from PUBLIC gist..." -ForegroundColor Cyan
$publicGistUrl = "https://api.github.com/gists/$($script:GistId)"
$publicGist = Invoke-RestMethod -Uri $publicGistUrl -Headers $headers
if (-not $publicGist.files.'profile.ps1') {
Write-Error "No file named 'profile.ps1' found in the public gist."
return
}
$publicCount = Save-GistFiles -Gist $publicGist -GistType "public"
Write-Host "✅ Pulled $publicCount file(s) from PUBLIC gist" -ForegroundColor Green
# Pull from PRIVATE GIST (if configured)
if ($script:PrivateGistId) {
Write-Host "`n🔒 Pulling from PRIVATE gist..." -ForegroundColor Cyan
# Private gist requires token
$GitHubToken = $env:GITHUB_GIST_TOKEN
if (-not $GitHubToken) {
Write-Host "⚠️ Skipping private gist: GITHUB_GIST_TOKEN not set" -ForegroundColor Yellow
} else {
try {
$privateHeaders = @{
'User-Agent' = 'PowerShell'
'Authorization' = "Bearer $GitHubToken"
}
$privateGistUrl = "https://api.github.com/gists/$($script:PrivateGistId)"
$privateGist = Invoke-RestMethod -Uri $privateGistUrl -Headers $privateHeaders
$privateCount = Save-GistFiles -Gist $privateGist -GistType "private"
Write-Host "✅ Pulled $privateCount file(s) from PRIVATE gist" -ForegroundColor Green
}
catch {
Write-Host "⚠️ Failed to pull from private gist: $_" -ForegroundColor Yellow
}
}
}
Write-Host "`n✅ Profile successfully restored from gist!" -ForegroundColor Green
Write-Host "Restart PowerShell or run '. `$PROFILE' to reload." -ForegroundColor Cyan
}
catch {
Write-Error "Failed to update profile: $_"
}
}
# show profile path
'path' {
Write-Host "Profile location: $PROFILE" -ForegroundColor Cyan
}
# navigate to profile directory
'dir' {
Write-Host "📁 Navigated to: $script:ProfileDir" -ForegroundColor Cyan
Set-Location $script:ProfileDir
}
# open profile directory in Finder
'folder' {
Write-Host "📂 Opening in Finder: $script:ProfileDir" -ForegroundColor Cyan
& open -a Finder $script:ProfileDir
}
# open GitHub Gist in browser
'web' {
$gistWebUrl = "https://gist.github.com/$($script:GistId)"
Write-Host "🌐 Opening GitHub Gist: $gistWebUrl" -ForegroundColor Cyan
& open $gistWebUrl
}
# setup executable permissions for scripts (ZoomISO, etc.)
'setup-scripts' {
$scriptsDir = Join-Path $script:ProfileDir 'scripts'
if (-not (Test-Path $scriptsDir)) {
Write-Host "❌ Scripts directory not found: $scriptsDir" -ForegroundColor Red
Write-Host " Run 'profile pl' to pull scripts from gist" -ForegroundColor Yellow
return
}
$scriptFiles = Get-ChildItem -Path $scriptsDir -Filter '*.ps1'
if ($scriptFiles.Count -eq 0) {
Write-Host "⚠️ No script files found in: $scriptsDir" -ForegroundColor Yellow
return
}
Write-Host "Setting up executable permissions for scripts..." -ForegroundColor Cyan
foreach ($scriptFile in $scriptFiles) {
chmod +x $scriptFile.FullName
Write-Host " ✓ Made executable: $($scriptFile.Name)" -ForegroundColor Gray
}
Write-Host "`n✅ Set up $($scriptFiles.Count) script files" -ForegroundColor Green
Write-Host "`nAvailable scripts:" -ForegroundColor Cyan
foreach ($scriptFile in $scriptFiles) {
Write-Host " pwsh $($scriptFile.FullName)" -ForegroundColor Gray
}
}
# ignore dependency warnings
'ignore-dep' {
if (-not $FilePath) {
Write-Host "❌ Missing dependency name" -ForegroundColor Red
Write-Host "Usage: profile ignore-dep <name>" -ForegroundColor Yellow
Write-Host "Available: gh, git, docker, fnm, copilot, font, private-gist-id" -ForegroundColor Gray
return
}
$depName = $FilePath.ToLower()
$validDeps = @('gh', 'git', 'docker', 'fnm', 'copilot', 'font', 'private-gist-id')
if ($validDeps -notcontains $depName) {
Write-Host "❌ Invalid dependency: $depName" -ForegroundColor Red
Write-Host "Available: $($validDeps -join ', ')" -ForegroundColor Yellow
return
}
$ignored = @(Get-IgnoredDependencies)
if ($ignored -contains $depName) {
Write-Host "ℹ️ Dependency '$depName' is already ignored" -ForegroundColor Cyan
return
}
$ignored += $depName
Save-IgnoredDependencies -Dependencies $ignored
Write-Host "✅ Suppressed warnings for: $depName" -ForegroundColor Green
Write-Host " (Restart terminal to take effect)" -ForegroundColor DarkGray
}
# unignore dependency warnings
'unignore-dep' {
if (-not $FilePath) {
Write-Host "❌ Missing dependency name" -ForegroundColor Red
Write-Host "Usage: profile unignore-dep <name>" -ForegroundColor Yellow
return
}
$depName = $FilePath.ToLower()
$ignored = @(Get-IgnoredDependencies)
if ($ignored -notcontains $depName) {
Write-Host "ℹ️ Dependency '$depName' is not currently ignored" -ForegroundColor Cyan
return
}
$ignored = $ignored | Where-Object { $_ -ne $depName }
Save-IgnoredDependencies -Dependencies $ignored
Write-Host "✅ Re-enabled warnings for: $depName" -ForegroundColor Green
Write-Host " (Restart terminal to take effect)" -ForegroundColor DarkGray
}
# list ignored dependencies
'list-ignored' {
$ignored = Get-IgnoredDependencies
if ($ignored.Count -eq 0) {
Write-Host "No dependencies are currently ignored" -ForegroundColor Gray
Write-Host "Use 'profile ignore-dep <name>' to suppress warnings" -ForegroundColor Yellow
return
}
Write-Host "🔕 Ignored Dependencies:" -ForegroundColor Cyan
foreach ($dep in $ignored) {
Write-Host "$dep" -ForegroundColor Gray
}
Write-Host "`nTo re-enable: profile unignore-dep <name>" -ForegroundColor DarkGray
}
# default: open profile in VS Code
default {
code $PROFILE
}
}
}
Set-Alias -Name pf -Value profile
Set-Alias -Name pl -Value profile
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-ProfileSyncHelp {
Write-Host " PROFILE MANAGEMENT" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help profile OR profile help OR profile h" -ForegroundColor Gray
Write-Host ""
Write-Host "⚙️ PROFILE COMMANDS" -ForegroundColor Yellow
Write-Host " profile Open profile in VS Code"
Write-Host " profile path (p) Show profile file path"
Write-Host " profile dir (d) Navigate to profile directory"
Write-Host " profile folder (f) Open profile directory in Finder"
Write-Host " profile web (w) Open GitHub Gist in browser"
Write-Host " profile ps [-Force] Push all files to GitHub Gist (auto-discovers)"
Write-Host " profile pl Pull all files from GitHub Gist (creates backup)"
Write-Host " profile setup-scripts (ss) Make scripts executable (after pull)"
Write-Host " profile ignore-dep <name> Suppress dependency warnings for this machine"
Write-Host " profile unignore-dep <name>Re-enable dependency warnings"
Write-Host " profile list-ignored Show ignored dependencies"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " profile Edit profile in VS Code"
Write-Host " profile ps Push all changes to gist"
Write-Host " profile ps -Force Push without confirmation prompts"
Write-Host " profile pl Pull from gist (backup created automatically)"
Write-Host ""
}
# ================================================================
# UTILITY FUNCTIONS
# ================================================================
<#
.SYNOPSIS
Quick Fahrenheit to Celsius conversion
.DESCRIPTION
Converts Fahrenheit temperature to Celsius using simplified formula
.EXAMPLE
2C 72 # Returns approximately 21°C
#>
function 2C {
param (
[Parameter()]
[Alias('Temp')]
[int]
$Temperature
)
($Temperature - 30) / 2
}
<#
.SYNOPSIS
Quick Celsius to Fahrenheit conversion
.DESCRIPTION
Converts Celsius temperature to Fahrenheit using simplified formula
.EXAMPLE
2F 20 # Returns approximately 68°F
#>
function 2F {
param (
[Parameter()]
[Alias('Temp')]
[int]
$Temperature
)
($Temperature * 2) + 30
}
<#
.SYNOPSIS
Update Homebrew and PowerShell
.DESCRIPTION
Runs brew update and upgrades PowerShell to the latest version
.EXAMPLE
update
#>
function update {
brew update
brew upgrade powershell
}
<#
.SYNOPSIS
Set environment variable securely in macOS Keychain
.DESCRIPTION
Sets an environment variable for the current session AND stores it
securely in macOS Keychain so it persists across terminal sessions.
Secrets are stored with prefix 'pwsh-env-' in Keychain.
.PARAMETER Name
Environment variable name (e.g., 'GITHUB_GIST_TOKEN')
.PARAMETER Value
Value to set (will be stored securely)
.EXAMPLE
SetEnv 'GITHUB_GIST_TOKEN' 'ghp_xxxxxxxxxxxx'
.NOTES
Profile startup automatically loads variables from Keychain.
View/edit keychain entries in Keychain Access.app
#>
function SetEnv {
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Name,
[Parameter(Mandatory = $true, Position = 1)]
[string]$Value
)
# Set for current session
[System.Environment]::SetEnvironmentVariable($Name, $Value, [System.EnvironmentVariableTarget]::Process)
# Store in macOS Keychain for persistence across sessions
try {
# Try to update existing entry first (-U flag)
& security add-generic-password -a "$env:USER" -s "pwsh-env-$Name" -w $Value -U 2>$null
if ($LASTEXITCODE -ne 0) {
# If update fails, add new entry
& security add-generic-password -a "$env:USER" -s "pwsh-env-$Name" -w $Value
}
Write-Host "Environment variable '$Name' set and stored securely in macOS Keychain." -ForegroundColor Green
}
catch {
Write-Error "Failed to store '$Name' in Keychain: $_"
}
}
# ================================================================
# POWER USER FEATURES
# ================================================================
# Advanced shortcuts and developer tools:
# - Docker & Docker Compose shortcuts (d, dc)
# - JSON pretty-printing (jj)
# - Performance timing (perf)
# - System info (ports, disk)
# ================================================================
<#
.SYNOPSIS
Pretty-print JSON with syntax highlighting
.DESCRIPTION
Formats JSON for easy reading. Accepts piped input.
Alias: jj
.PARAMETER InputObject
JSON string to format (accepts pipeline input)
.EXAMPLE
'{"name":"test","value":123}' | jj
Get-Content data.json | jj
curl https://api.example.com/data | jj
.ALIAS
jj
#>
function Show-Json {
[Alias('jj')]
Param(
[Parameter(ValueFromPipeline = $true)]
[string]$InputObject
)
process {
if ($InputObject) {
$InputObject | ConvertFrom-Json | ConvertTo-Json -Depth 10 | Out-Host
}
}
}
# System info shortcuts
function Get-ListeningPorts {
[Alias('ports')]
Param()
Write-Host "Listening ports:" -ForegroundColor Green
lsof -iTCP -sTCP:LISTEN -n -P | Select-Object -Skip 1
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-UtilitiesHelp {
Write-Host " UTILITIES" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help utilities" -ForegroundColor Gray
Write-Host ""
Write-Host " Aliases: utilities, util" -ForegroundColor Gray
Write-Host ""
Write-Host "🛠️ UTILITY COMMANDS" -ForegroundColor Yellow
Write-Host " 2C <temp> Convert Fahrenheit to Celsius"
Write-Host " 2F <temp> Convert Celsius to Fahrenheit"
Write-Host " update Update Homebrew & PowerShell"
Write-Host " SetEnv 'NAME' 'value' Store env var in Keychain (persists)"
Write-Host " jj <json> Pretty-print JSON"
Write-Host " ports Show listening ports"
Write-Host ""
Write-Host "💡 EXAMPLES" -ForegroundColor Yellow
Write-Host " 2C 72 Convert 72°F to Celsius"
Write-Host " 2F 20 Convert 20°C to Fahrenheit"
Write-Host " SetEnv 'API_KEY' 'abc' Store API key securely"
Write-Host " jj '{\"a\":1}' Pretty-print JSON"
Write-Host ""
}
# ================================================================
# ZOOMISO PRODUCTION CONTROL
# ================================================================
# Quick commands for starting, stopping, and managing ZoomISO
# for production workflows when switching between meetings.
# ================================================================
<#
.SYNOPSIS
ZoomISO application control
.DESCRIPTION
Quick commands to start, stop, restart, and check ZoomISO status.
Useful for production workflows when switching between meetings.
Alias: ziso
.PARAMETER Command
Short command:
stop = quit ZoomISO gracefully (like GUI quit)
start = launch ZoomISO
restart = stop, wait, then start
status = check if ZoomISO is running
.EXAMPLE
ziso stop # Quit ZoomISO gracefully
ziso start # Start ZoomISO
ziso restart # Stop and start
ziso status # Check if running
.ALIAS
ziso
#>
function Invoke-Zoomiso {
[Alias('ziso')]
Param(
[Parameter(Mandatory = $false, Position = 0)]
[string]$Command = 'help'
)
$appName = "ZoomISO - v2"
$normalizedCommand = switch ($Command) {
'h' { 'help' }
default
{ $Command }
}
switch ($normalizedCommand) {
'stop' {
Write-Host "Stopping ZoomISO..." -ForegroundColor Yellow
# Check if running first
$running = pgrep -i "zoomiso" 2>$null
if (-not $running) {
Write-Host "ZoomISO is not running" -ForegroundColor Gray
return
}
# Graceful quit using osascript (same as GUI File → Quit)
osascript -e "quit app `"$appName`"" 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Failed to send quit command to ZoomISO" -ForegroundColor Red
return
}
# Wait up to 60 seconds for process to stop
$maxWaitSeconds = 60
$waited = 0
$stillRunning = $true
Write-Host "Waiting for shutdown..." -ForegroundColor Gray
while ($waited -lt $maxWaitSeconds) {
Start-Sleep -Seconds 1
$waited++
$checkProcess = pgrep -i "zoomiso" 2>$null
if (-not $checkProcess) {
$stillRunning = $false
break
}
# Show progress every second
Write-Host " Checking... ($waited seconds)" -ForegroundColor Gray
}
if (-not $stillRunning) {
Write-Host "✅ ZoomISO stopped successfully (took $waited seconds)" -ForegroundColor Green
} else {
Write-Host "❌ ZoomISO did not stop after $maxWaitSeconds seconds" -ForegroundColor Red
Write-Host " Process may be hung. Consider force quitting from Activity Monitor." -ForegroundColor Yellow
}
}
'start' {
Write-Host "Starting ZoomISO..." -ForegroundColor Yellow
# Check if already running
$running = pgrep -i "zoomiso" 2>$null
if ($running) {
Write-Host "ZoomISO is already running (PID: $running)" -ForegroundColor Gray
return
}
# Start the app
open -a "$appName" 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Failed to start ZoomISO" -ForegroundColor Red
Write-Host " Make sure '$appName' is installed in /Applications" -ForegroundColor Gray
return
}
# Wait up to 60 seconds for it to start
$maxWaitSeconds = 60
$waited = 0
$started = $false
Write-Host "Waiting for startup..." -ForegroundColor Gray
while ($waited -lt $maxWaitSeconds) {
Start-Sleep -Seconds 1
$waited++
$newProcessPid = pgrep -i "zoomiso" 2>$null
if ($newProcessPid) {
Write-Host "✅ ZoomISO started (PID: $newProcessPid, took $waited seconds)" -ForegroundColor Green
$started = $true
break
}
# Show progress every second
Write-Host " Checking... ($waited seconds)" -ForegroundColor Gray
}
if (-not $started) {
Write-Host "⚠️ ZoomISO may not have started after $maxWaitSeconds seconds" -ForegroundColor Yellow
}
}
'restart' {
Write-Host "Restarting ZoomISO..." -ForegroundColor Cyan
# Stop first
Invoke-Zoomiso -Command stop
# Wait a moment for clean shutdown
Write-Host "Waiting for clean shutdown..." -ForegroundColor Gray
Start-Sleep -Seconds 2
# Start again
Invoke-Zoomiso -Command start
}
'status' {
$processPid = pgrep -i "zoomiso" 2>$null
if ($processPid) {
Write-Host "✅ ZoomISO is running (PID: $processPid)" -ForegroundColor Green
# Show additional info
$processInfo = ps -p $processPid -o pid,vsz,rss,%cpu,%mem,etime,command 2>$null | Select-Object -Skip 1
if ($processInfo) {
Write-Host "Process info:" -ForegroundColor Cyan
Write-Host $processInfo -ForegroundColor Gray
}
} else {
Write-Host "❌ ZoomISO is not running" -ForegroundColor Red
}
}
'help' {
Write-Host " ZOOMISO SHORTCUTS (ziso)" -ForegroundColor Green
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Get Help: help zoomiso OR ziso help OR ziso h" -ForegroundColor Gray
Write-Host ""
Write-Host "ziso <command>:" -ForegroundColor Yellow
Write-Host " stop Quit ZoomISO gracefully" -ForegroundColor Gray
Write-Host " start Launch ZoomISO" -ForegroundColor Gray
Write-Host " restart Stop and start ZoomISO" -ForegroundColor Gray
Write-Host " status Check if ZoomISO is running" -ForegroundColor Gray
Write-Host ""
Write-Host "Examples:" -ForegroundColor Yellow
Write-Host " ziso stop" -ForegroundColor Gray
Write-Host " ziso start" -ForegroundColor Gray
Write-Host " ziso restart" -ForegroundColor Gray
}
default {
Write-Host "Usage: ziso <command>" -ForegroundColor Yellow
Write-Host ""
Write-Host "Commands:" -ForegroundColor Cyan
Write-Host " stop Quit ZoomISO gracefully" -ForegroundColor Gray
Write-Host " start Launch ZoomISO" -ForegroundColor Gray
Write-Host " restart Stop and start ZoomISO" -ForegroundColor Gray
Write-Host " status Check if ZoomISO is running" -ForegroundColor Gray
Write-Host ""
Write-Host "Examples:" -ForegroundColor Cyan
Write-Host " ziso stop" -ForegroundColor Gray
Write-Host " ziso start" -ForegroundColor Gray
Write-Host " ziso restart" -ForegroundColor Gray
}
}
}
# ================================================================
# HELP FUNCTION
# ================================================================
function Show-ZoomISOHelp {
# Call the built-in ziso help
ziso help
}
# ================================================================
# POWERSHELL PROFILE - MODULAR CONFIGURATION
# ================================================================
# This profile provides shortcuts and tools for:
# - Obvio project management (oea, oew, oma, etc.)
# - Git, Docker, and Copilot CLI shortcuts
# - Directory navigation and bookmarks
# - System utilities and developer tools
# ================================================================
# ================================================================
# ENVIRONMENT SETUP
# ================================================================
# Initialize Homebrew environment variables
$(/opt/homebrew/bin/brew shellenv) | Invoke-Expression
# Load PowerShell modules
Import-Module Terminal-Icons # Pretty icons for files/folders
Import-Module PSReadLine # Enhanced command-line editing
# Load sensitive environment variables from macOS Keychain
# This keeps secrets out of plain text files and syncs across profile updates
$keychainEnvVars = @('GITHUB_GIST_TOKEN', 'POWERSHELL_PRIVATE_GIST_ID')
foreach ($varName in $keychainEnvVars) {
if (-not (Get-Item -Path "Env:$varName" -ErrorAction SilentlyContinue)) {
try {
# Retrieve from keychain using security command
$value = & security find-generic-password -a "$env:USER" -s "pwsh-env-$varName" -w 2>$null
if ($LASTEXITCODE -eq 0 -and $value) {
[System.Environment]::SetEnvironmentVariable($varName, $value, [System.EnvironmentVariableTarget]::Process)
}
}
catch {
# Silently ignore if not found in keychain
}
}
}
# Validate required environment variables are set
$missingEnvVars = @()
if (-not $env:GITHUB_GIST_TOKEN) { $missingEnvVars += "GITHUB_GIST_TOKEN" }
if (-not $env:POWERSHELL_PRIVATE_GIST_ID) { $missingEnvVars += "POWERSHELL_PRIVATE_GIST_ID" }
if ($missingEnvVars.Count -gt 0) {
Write-Host "Missing environment variables: $($missingEnvVars -join ', ')" -ForegroundColor Yellow
Write-Host "Set them using: SetEnv 'VARIABLE_NAME' 'your-value-here'" -ForegroundColor Cyan
}
# Configure PSReadLine for better command-line experience
Set-PSReadLineOption -PredictionSource History # Suggest commands from history
Set-PSReadLineOption -PredictionViewStyle ListView # Show suggestions in a list
Set-PSReadLineOption -EditMode Windows # Use Windows-style editing keys
# Initialize oh-my-posh prompt theme
# Load config first to get theme path
. "$PSScriptRoot/modules/config.ps1"
oh-my-posh init pwsh --config $script:themePath | Invoke-Expression
# Initialize Fast Node Manager (fnm) for Node.js version management
fnm env --use-on-cd | Out-String | Invoke-Expression
# ================================================================
# LOAD MODULES
# ================================================================
# Load modules in order (some modules depend on previous ones)
. "$PSScriptRoot/modules/dependencies.ps1" # Check required tools
# Load private modules (from git submodule) if they exist
$privateModulesPath = Join-Path $PSScriptRoot "private-modules/powershell/modules"
if (Test-Path $privateModulesPath) {
Get-ChildItem -Path $privateModulesPath -Filter "*.ps1" -File | ForEach-Object {
. $_.FullName
}
}
. "$PSScriptRoot/modules/companion.ps1" # Companion module management
. "$PSScriptRoot/modules/navigation.ps1" # Folder navigation & bookmarks
. "$PSScriptRoot/modules/package-npm.ps1" # npm shortcuts
. "$PSScriptRoot/modules/package-yarn.ps1" # yarn shortcuts
. "$PSScriptRoot/modules/package-fnm.ps1" # fnm (Fast Node Manager) shortcuts
. "$PSScriptRoot/modules/package-install.ps1" # Smart package install (auto-detect npm/yarn)
. "$PSScriptRoot/modules/git-shortcuts.ps1" # Git shortcuts
. "$PSScriptRoot/modules/copilot-shortcuts.ps1" # Copilot CLI shortcuts
. "$PSScriptRoot/modules/docker-shortcuts.ps1" # Docker shortcuts
. "$PSScriptRoot/modules/docker-compose-shortcuts.ps1" # Docker Compose shortcuts
. "$PSScriptRoot/modules/zoomiso.ps1" # ZoomISO production control
. "$PSScriptRoot/modules/profile-sync.ps1" # Profile sync with GitHub Gist
. "$PSScriptRoot/modules/utilities.ps1" # Utility functions & developer tools
. "$PSScriptRoot/modules/help.ps1" # Help system & startup banner
# Display ASCII art banner
Write-Host " " -NoNewline
Write-Host "_____" -ForegroundColor Red
Write-Host " /" -NoNewline -ForegroundColor Red
Write-Host " " -NoNewline -ForegroundColor Gray
Write-Host "`\ " -NoNewline -ForegroundColor Red
Write-Host "LET YOUR NERD" -ForegroundColor Red
Write-Host " " -NoNewline
Write-Host "| " -NoNewline -ForegroundColor Red
Write-Host "[" -NoNewline -ForegroundColor Red
Write-Host "O" -NoNewline -ForegroundColor White
Write-Host "_" -NoNewline -ForegroundColor Red
Write-Host "O" -NoNewline -ForegroundColor White
Write-Host "]" -NoNewline -ForegroundColor Red
Write-Host " | " -NoNewline -ForegroundColor Red
Write-Host "BE HEARD" -ForegroundColor White
Write-Host " " -NoNewline
Write-Host "| " -NoNewline -ForegroundColor Red
Write-Host ">" -NoNewline -ForegroundColor Gray
Write-Host " | " -ForegroundColor Red
Write-Host " " -NoNewline
Write-Host "`\_____" -NoNewline -ForegroundColor Red
Write-Host "/ " -NoNewline -ForegroundColor Red
Write-Host "turning virtual events into extraordinary experiences" -ForegroundColor DarkGray
Write-Host " " -NoNewline
Write-Host "|" -ForegroundColor Red
Write-Host ""
Write-Host "✨ PowerShell profile loaded! Type 'help' for available shortcuts and features" -ForegroundColor Green
#!/usr/bin/env pwsh
# Standalone script to restart ZoomISO (for remote execution)
# Returns exit code: 0 = success, 1 = failed
# Usage: pwsh -NoProfile /Users/lynbh/.config/powershell/scripts/ziso-restart.ps1
# Load the zoomiso module directly (no profile)
$moduleScript = Join-Path $PSScriptRoot "../modules/zoomiso.ps1"
. $moduleScript
# Call the function
Invoke-Zoomiso -Command restart
# Check final status and return exit code
$checkProcess = pgrep -i "zoomiso" 2>$null
if ($checkProcess) {
exit 0 # Success - ZoomISO is running after restart
} else {
exit 1 # Failed - ZoomISO not running after restart
}
#!/usr/bin/env pwsh
# Standalone script to start ZoomISO (for remote execution)
# Returns exit code: 0 = success, 1 = failed
# Usage: pwsh -NoProfile /Users/lynbh/.config/powershell/scripts/ziso-start.ps1
# Load the zoomiso module directly (no profile)
$moduleScript = Join-Path $PSScriptRoot "../modules/zoomiso.ps1"
. $moduleScript
# Call the function
Invoke-Zoomiso -Command start
# Check final status and return exit code
$checkProcess = pgrep -i "zoomiso" 2>$null
if ($checkProcess) {
exit 0 # Success - ZoomISO is running
} else {
exit 1 # Failed - ZoomISO not running
}
#!/usr/bin/env pwsh
# Standalone script to check ZoomISO status (for remote execution)
# Returns exit code: 0 = running, 1 = not running
# Usage: pwsh -NoProfile /Users/lynbh/.config/powershell/scripts/ziso-status.ps1
# Load the zoomiso module directly (no profile)
$moduleScript = Join-Path $PSScriptRoot "../modules/zoomiso.ps1"
. $moduleScript
# Call the function
Invoke-Zoomiso -Command status
# Return appropriate exit code
$checkProcess = pgrep -i "zoomiso" 2>$null
if ($checkProcess) {
exit 0 # Running
} else {
exit 1 # Not running
}
#!/usr/bin/env pwsh
# Standalone script to stop ZoomISO (for remote execution)
# Returns exit code: 0 = success, 1 = failed
# Usage: pwsh -NoProfile /Users/lynbh/.config/powershell/scripts/ziso-stop.ps1
# Load the zoomiso module directly (no profile)
$moduleScript = Join-Path $PSScriptRoot "../modules/zoomiso.ps1"
. $moduleScript
# Call the function
Invoke-Zoomiso -Command stop
# Check final status and return exit code
$checkProcess = pgrep -i "zoomiso" 2>$null
if (-not $checkProcess) {
exit 0 # Success - ZoomISO stopped
} else {
exit 1 # Failed - ZoomISO still running
}
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"blocks": [
{
"alignment": "left",
"segments": [
// {
// "foreground": "#26C6DA",
// "options": {
// "macos": "mac"
// },
// "style": "plain",
// "template": "{{ if .WSL }}WSL at {{ end }}{{.Icon}}",
// "type": "os"
// },
{
"foreground": "#26C6DA",
"style": "plain",
"template": "{{ .HostName }}: ",
"type": "session"
},
{
"foreground": "lightGreen",
"style": "plain",
"type": "path",
"template": "{{ .PWD }} ",
"options": {
"style": "folder"
}
}
],
"type": "prompt"
},
{
"alignment": "left",
"newline": true,
"segments": [
{
"options": {
"branch_icon": "\ue725 ",
"fetch_status": true,
"fetch_upstream_icon": true,
"fetch_worktree_count": true
},
"style": "plain",
"template": "<#ffffff>on</> {{ .UpstreamIcon }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0 }} \ueb4b {{ .StashCount }}{{ end }} ",
"type": "git"
},
{
"foreground": "#906cff",
"style": "powerline",
"template": "[\ue235 {{ if .Error }}{{ .Error }}{{ else }}{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}{{ end }}] ",
"type": "python"
},
{
"foreground": "#7FD5EA",
"style": "powerline",
"template": "[\ue626 {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }}] ",
"type": "go"
},
{
"foreground": "#76b367",
"style": "powerline",
"template": "[\ue718 {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }}] ",
"type": "node"
},
{
"foreground": "#6600f5",
"style": "powerline",
"template": "[ {{ .Full }}] ",
"type": "dotnet"
},
{
"type": "angular",
"style": "powerline",
"powerline_symbol": "",
"foreground": "#000000",
"background": "#1976d2",
"template": "  {{ .Full }} "
},
{
"type": "yarn",
"style": "powerline",
// "powerline_symbol": "",
"foreground": "#FFFFFF",
// "background": "#2E2A65",
"template": "  {{ .Full }} "
},
{
"type": "php",
"style": "powerline",
"powerline_symbol": "",
"foreground": "#ffffff",
"background": "#4063D8",
"template": "  {{ .Full }} "
}
],
"type": "prompt"
},
{
"alignment": "left",
"newline": true,
"segments": [
{
"foreground": "#e91e63",
"foreground_templates": [
"{{ if and (gt .Code 0) (ne .Code 130) }}#ef5350{{ end }}"
],
"style": "plain",
"template": "{{ if and (gt .Code 0) (ne .Code 130) }}❌ {{ .Code }} {{ end }}",
"type": "status"
},
{
"foreground": "#FFD54F",
"style": "plain",
"template": "\u276f ",
"type": "text"
}
],
"type": "prompt"
}
],
"version": 3
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment