kim.ps1
#! /usr/bin/pwsh
$f=$args[0]
$remainingArgs=@($args | Select-Object -Skip 1)
function RemoveUnusedDockerContainersAndVolumes {
Write-Host "Action: $function - removes all unused Docker containers & volumes."
docker container prune -f && docker volume prune -f
}
function RemoveUnusedDockerVolumes {
Write-Host "Action: $function - removes all unused Docker volumes."
docker volume prune -f
}
function StopAndRemoveAllDockerContainers {
Write-Host "Action: $function - stops and removes all Docker containers."
docker stop $(docker ps -a -q) && docker container prune -f
}
function StopAndRemoveUnusedDockerContainersAndVolumes {
Write-Host "Action: $function - stops and removes all Docker containers, and removes all unused Docker volumes."
docker stop $(docker ps -a -q) && docker container prune -f && docker volume prune -f
}
function SpinUpNewMysqlDockerContainer {
Write-Host "Action: $function - spins up new MySQL Docker container and copies env vars to clipboard."
$currentContainer = docker ps -f "name=local_mysql" --format "{{.ID}}"
if ($null -eq $currentContainer) {
Write-Host "No MySQL container found. Spinning up new container..."
} else {
Write-Host "MySQL container found. Stopping and removing container..."
docker stop $currentContainer && docker container prune -f
Start-Sleep -Seconds 2
}
docker run --name=local_mysql --env=MYSQL_DATABASE=test --env=MYSQL_PASSWORD=test --env=MYSQL_USER=test --env=MYSQL_ROOT_PASSWORD=test -p 3306 -d mysql:8.0 --character-set-server=latin1 --collation_server=latin1_swedish_ci --log-bin-trust-function-creators=1
Start-Sleep -Seconds 1
$mysqlPortOutput = docker port local_mysql
$mysqlPort = $mysqlPortOutput.Split(":")[-1]
Write-Host "MySQL container accessible on port: "$mysqlPort
$testEnvVars = "MYSQL_URL=jdbc:mysql://localhost:$($mysqlPort)/test;MYSQL_USERNAME=root;MYSQL_PASSWORD=test" # Change accordingly
Set-Clipboard -Value $testEnvVars
Write-Host "Copied to clipboard: $($testEnvVars)."
Write-Host "Open the run configuration for any test and paste as environment vars."
Write-Host "In case of errors above, clean up with: 'docker stop `$(docker ps -f `"name=local_mysql`" --format `"{{.ID}}`") && docker container prune -f'."
}
function GetMysqlDockerContainerPortNumber {
Write-Host "Action: $function - copies MySQL Docker container port number to clipboard."
$sql_docker_container=$(docker ps | Select-String -Pattern "mysql:")
if ($null -eq $sql_docker_container) {
Write-Host "Port number not found. Check if MySQL container is running."
} else {
$port_number=$sql_docker_container -replace '.*:(\d+).*', '$1' | ForEach-Object { $_.Trim() }
Set-Clipboard -Value $port_number
Write-Host "MySQL port number ($port_number) copied to clipboard."
}
}
function GetMysqlDockerContainerId {
Write-Host "Action: $function - sets the MySQL Docker container id as a variable."
$sql_container_name=$(docker ps | Select-String -Pattern "mysql:")
if ($null -eq $sql_container_name) {
Write-Host "Container not found. Check if MySQL container is running."
Set-Variable -Name cn -Value $null -Scope Global
} else {
$containerId = $sql_container_name.Line.Split(" ")[0]
Set-Clipboard -Value $containerId
Set-Variable -Name cn -Value $containerId -Scope Global
Write-Host "MySQL container id ($containerId) copied to clipboard and set as `$cn."
}
}
function ExecuteCommandInMySqlContainer {
Write-Host "Action: $function - executes a MySQL command in a Docker container e.g. local_mysql SHOW DATABASES (or just SHOW DATABASES)."
if ($args.Count -eq 0) {
Write-Host "Error: Provide a command, optionally preceded by a container name e.g. 'mc local_mysql SHOW DATABASES'."
return
}
$containerId = docker ps -q -f "name=$($args[0])"
if ($containerId) {
Write-Host "Found container with name $($args[0])."
$command = ($args | Select-Object -Skip 1) -join ' '
} else {
Write-Host "No container with name '$($args[0])' found. Treating all arguments as the command and fetching MySQL container id for you..."
GetMysqlDockerContainerId
if ($null -eq $cn) {
return
}
$containerId = $cn
$command = $args -join ' '
}
if ($command -eq [string]::empty) {
Write-Host "Error: No command provided."
return
}
Write-Host "Executing command: $command."
docker exec -it $containerId bash -c "mysql -u test -ptest -e `"$command;`"" # Change accordingly
}
function CopyInfraVariablesToClipboard {
Write-Host "Action: $function - copies variables of running local infra with localstack and MySQL to clipboard."
$mysqlPortOutput = docker port local_mysql
$mysqlPort = 'ERROR'
if ($null -eq $mysqlPortOutput) {
Write-Host "Error: MySQL container not found. Check if MySQL container is running."
} else {
$mysqlPort = $mysqlPortOutput.Split(":")[-1]
Write-Host "MySQL is accessible on port: "$mysqlPort
}
$localstackPortOutput = docker port local-localstack-1
$localstackPort = 'ERROR'
if ($null -eq $localstackPortOutput) {
Write-Host "Error: Localstack container not found. Check if Localstack container is running."
} else {
$localstackPort = $localstackPortOutput.Split(":")[-1]
Write-Host "Localstack is accessible on port: "$localstackPort
}
$testEnvVars = "LOCALSTACK=http://127.0.0.1:$($localstackPort);MYSQL_URL=jdbc:mysql://localhost:$($mysqlPort)/test;MYSQL_USERNAME=root;MYSQL_PASSWORD=test" # Change accordingly
Set-Clipboard -Value $testEnvVars
Write-Host "Copied to clipboard: $($testEnvVars)"
}
function GetPortInfo {
Write-Host "Action: $function - lists TCP connections (optional: using port number specified)."
$port = Read-Host -Prompt "Enter port number"
Write-Host ""
if ($port -eq [string]::empty) {
netstat -nao
} else {
netstat -nao | findstr "PID :$port"
$foundProcesses = netstat -nao | findstr "PID :$port"
$activePortPattern = ":$port\s.+LISTENING\s+\d+$"
$pidNumberPattern = "\d+$"
if ($foundProcesses | Select-String -Pattern $activePortPattern -Quiet) {
$matched = $foundProcesses | Select-String -Pattern $activePortPattern
$firstMatch = $matched.Matches.Get(0).Value
$pidNumber = [regex]::match($firstMatch, $pidNumberPattern).Value
Get-Process -id $pidNumber
$action = Read-Host -Prompt "`nAction ([k] kill, [ENTER] none)"
switch ($action) {
{$_ -eq "k" -or $_ -eq "kill"} { taskkill /pid $pidNumber /f }
default { Write-Host "No action taken."; break }
}
} else {
Write-Host "No matching process found."
}
}
}
function GetProcessInfo {
Write-Host "Action: $function - returns information about process for a given PID."
$process_id = Read-Host -Prompt "Enter PID"
if ($process_id -eq [string]::empty) {
Write-Host "No PID provided."
} else {
$ErrorActionPreference = 'SilentlyContinue'
Get-Process -id $process_id | Select-Object *
}
}
function WhereIs {
Write-Host "Action: $function - finds path of an executable."
$executableName = $args[0]
if ($executableName -eq [string]::empty -or $executableName -eq $null) {
Write-Host "No executable name provided."
$executableName = Read-Host -Prompt "Enter name of executable (excluding .exe)"
$ErrorActionPreference = 'SilentlyContinue'
$executablePath = (Get-Command "$executableName.exe").Path
if ($null -eq $executablePath) {
Write-Host "Error: Executable $executableName.exe cannot be found."
} else {
Write-Host $executablePath
}
return
}
Write-Host "Attempting to find location of $executableName.exe..."
$ErrorActionPreference = 'SilentlyContinue'
$executablePath = (Get-Command "$executableName.exe").Path
if ($null -eq $executablePath) {
Write-Host "Error: Executable $executableName.exe cannot be found."
} else {
Write-Host $executablePath
}
}
function SwitchToJava17 {
Write-Host "Action: $function - switches to specified Java version."
$Env:JAVA_HOME="C:\Program Files\Eclipse Adoptium\jdk-17.0.20.101-hotspot\"
$Env:Path="$Env:JAVA_HOME\bin;$Env:Path"
Write-Host "Java Adoptium Temurin 17 activated."
}
function SwitchToJava21 {
Write-Host "Action: $function - switches to specified Java version."
$Env:JAVA_HOME="C:\Program Files\Eclipse Adoptium\jdk-21.0.8.9-hotspot\"
$Env:Path="$Env:JAVA_HOME\bin;$Env:Path"
Write-Host "Java Adoptium Temurin 21 activated."
}
function SwitchToJava25 {
Write-Host "Action: $function - switches to specified Java version."
$Env:JAVA_HOME="C:\Program Files\Eclipse Adoptium\jdk-25.0.4.101-hotspot\"
$Env:Path="$Env:JAVA_HOME\bin;$Env:Path"
Write-Host "Java Adoptium Temurin 25 activated."
}
function ToggleGradleInitFile {
Write-Host "Action: $function - renames init.gradle file to activate/deactivate it."
$gradleFolder = Join-Path -Path $env:USERPROFILE -ChildPath ".gradle"
$initFile = Join-Path -Path $gradleFolder -ChildPath "init.gradle"
$inactiveInitFile = Join-Path -Path $gradleFolder -ChildPath "INACTIVE_init.gradle_INACTIVE"
if (Test-Path -Path $initFile -PathType Leaf) {
Rename-Item -Path $initFile -NewName "INACTIVE_init.gradle_INACTIVE" -Force
Write-Host "Outcome: Deactivated i.e. renamed init.gradle to INACTIVE_init.gradle_INACTIVE"
}
# Check if the INACTIVE_init.gradle_INACTIVE file exists
elseif (Test-Path -Path $inactiveInitFile -PathType Leaf) {
Rename-Item -Path $inactiveInitFile -NewName "init.gradle" -Force
Write-Host "Outcome: Activated i.e. renamed INACTIVE_init.gradle_INACTIVE to init.gradle"
}
else {
Write-Host "Error: No init.gradle or INACTIVE_init.gradle_INACTIVE file found."
}
}
function DecodeBase64EncodedString {
Write-Host "Action: $function - decodes, prints, and copies a base64 encoded string to your clipboard."
$base64String = $args -join ' '
if ($base64String -eq [string]::empty) {
Write-Host "No string provided."
} else {
$decodedBytes = [System.Convert]::FromBase64String($base64String)
$decodedString = [System.Text.Encoding]::UTF8.GetString($decodedBytes)
Set-Clipboard -Value $decodedString
Write-Host "The result, shown below, has been copied to your clipboard:"
Write-Host ""
Write-Host $decodedString
}
}
function EncodeStringAsBase64 {
Write-Host "Action: $function - encodes, prints, and copies a string as base64 to your clipboard."
$string = $args -join ' '
if ($string -eq [string]::empty) {
Write-Host "No string provided."
} else {
$bytes = [System.Text.Encoding]::UTF8.GetBytes($string)
$base64String = [System.Convert]::ToBase64String($bytes)
Set-Clipboard -Value $base64String
Write-Host "The result, shown below, has been copied to your clipboard:"
Write-Host ""
Write-Host $base64String
}
}
function DecodeCertificateString {
$certString = $args -join ' '
if ($certString -eq [string]::empty) {
Write-Host "No string provided."
} else {
$base64Cert = $certString -replace '-----BEGIN CERTIFICATE-----', '' -replace '-----END CERTIFICATE-----', '' -replace '\s+', ''
$decodedBytes = [System.Convert]::FromBase64String($base64Cert)
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @(,$decodedBytes)
Write-Host "Content of decoded certificate:"
Write-Host $certificate
$certString = ""
}
}
function DecodeCertificatePemFile {
Write-Host "Action: $function - decodes and prints the content of a certificate PEM file."
$pemFile = $args -join ' '
if ($pemFile -eq [string]::empty) {
Write-Host "No file provided."
} else {
$pemContent = Get-Content -Path $pemFile -Raw
DecodeCertificateString $pemContent
# $base64Cert = $pemContent -replace '-----BEGIN CERTIFICATE-----', '' -replace '-----END CERTIFICATE-----', '' -replace '\s+', ''
# $decodedBytes = [System.Convert]::FromBase64String($base64Cert)
# $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @(,$decodedBytes)
# Write-Host "Content of decoded PEM certificate:"
# Write-Host $certificate
}
}
function DecodePrivateKeyPemFile {
Write-Host "Action: $function - decodes and prints the content of a PEM private key file."
$pemFile = $args -join ' '
if ($pemFile -eq [string]::empty) {
Write-Host "No file provided."
} else {
$privKeyContent = Get-Content -Path $pemFile -Raw
$base64PrivKey = $privKeyContent -replace '-----BEGIN PRIVATE KEY-----', '' -replace '-----END PRIVATE KEY-----', '' -replace '\s+', ''
$decodedBytes = [System.Convert]::FromBase64String($base64PrivKey)
$stringPrivKey = [System.Text.Encoding]::UTF8.GetString($decodedBytes)
Write-Output "Content of the private key:"
Write-Output $stringPrivKey
}
}
function GenerateUuid {
Write-Host "Action: $function - generates random UUID."
$uuid=[guid]::NewGuid().ToString()
Set-Clipboard -Value $uuid
Write-Host "UUID ($uuid) copied to clipboard."
}
function GenerateUlid {
Write-Host "Action: $function - generates random ULID."
$ulid_encoding = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
$ulid_encoding_length = $ulid_encoding.Length
$time_now = [UInt64]((([datetime]::UtcNow).Ticks - 621355968000000000) / 10000)
$created_string = ''
for ($i = 10; $i -gt 0; $i--) {
$Mod = [int]($time_now % $ulid_encoding_length)
$created_string = $ulid_encoding[$Mod] + $created_string
$time_now = ($time_now - $Mod) / $ulid_encoding_length
}
$timestamp_component = $created_string
$created_string = ''
$random = [random]::new()
for ($i = 16; $i -gt 0; $i--) {
$random_index = [int]([math]::Floor($ulid_encoding_length * $random.NextDouble()))
$created_string = $ulid_encoding[$random_index] + $created_string
Start-Sleep -Milliseconds 1
}
$random_component = $created_string
$ulid = $timestamp_component + $random_component
Set-Clipboard -Value $ulid
Write-Host "ULID ($ulid) copied to clipboard."
}
function GenerateUlidSilently {
$ulid_encoding = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
$ulid_encoding_length = $ulid_encoding.Length
$time_now = [UInt64]((([datetime]::UtcNow).Ticks - 621355968000000000) / 10000)
$created_string = ''
for ($i = 10; $i -gt 0; $i--) {
$Mod = [int]($time_now % $ulid_encoding_length)
$created_string = $ulid_encoding[$Mod] + $created_string
$time_now = ($time_now - $Mod) / $ulid_encoding_length
}
$timestamp_component = $created_string
$created_string = ''
$random = [random]::new()
for ($i = 16; $i -gt 0; $i--) {
$random_index = [int]([math]::Floor($ulid_encoding_length * $random.NextDouble()))
$created_string = $ulid_encoding[$random_index] + $created_string
Start-Sleep -Milliseconds 1
}
$random_component = $created_string
$ulid = $timestamp_component + $random_component
Set-Clipboard -Value $ulid
}
function FixPackageJsonPortsNames {
Write-Host "Action: $function - recursively searches through a repo for package.json files and replaces all occurrences of '-p `${PORT:=3000}' with '-p 3000'."
Write-Host "Fetching files..."
$files = Get-ChildItem -Path "$HOME\projects\some-repository" -Filter 'package.json' -Recurse -File
foreach ($file in $files) {
if ($file.DirectoryName -notmatch '\\node_modules\\' -and $file.DirectoryName -notmatch '\\prisma\\' -and $file.DirectoryName -notmatch '\\db\\' -and $file.DirectoryName -notmatch '\\jest\\' -and $file.DirectoryName -notmatch '\\.next\\') {
Write-Host "Processing file: $file..."
$content = [System.IO.File]::ReadAllText($file)
$newContent = $content.Replace("-p `${PORT:=3000}","-p 3000")
if ($content -ne $newContent) {
[System.IO.File]::WriteAllText($file, $newContent)
Write-Host "Modified: $file."
}
}
}
Write-Host "Done!"
}
function UpdateAllRepositories {
Write-Host "Action: $function - checks out the default branch and pulls, for every repository."
& "$env:ProgramFiles\Git\bin\bash.exe" "$HOME\Documents\PowerShell\Scripts\update-repos.sh" @args
}
function Help {
Write-Host "Action: $function - lists available commands."
Write-Host "[ Docker ]"
Write-Host "drcv : Remove unused containers and volumes"
Write-Host "drv : Remove unused volumes"
Write-Host "dsrc : Stop and remove all containers"
Write-Host "dsrcv : Stop and remove all containers and volumes"
Write-Host "nmc : Spin up new MySQL container and copy environment variables to clipboard"
Write-Host "mp : Copy MySQL container port number to clipboard"
Write-Host "mid : Set MySQL container id as variable"
Write-Host "mc : Execute a MySQL command in a container (use with container name and command as arguments e.g. 'local_mysql SHOW DATABASES', or just the command to use the running MySQL container)"
Write-Host "infv : Copy variables of running local infra with localstack and MySQL to clipboard"
Write-Host "[ System ]"
Write-Host "gp : Get TCP connections (for port specified)"
Write-Host "pr : Get process information for a given PID"
Write-Host "where : Find path of an executable"
Write-Host "[ Java ]"
Write-Host "j17 : Switch to Java Temurin 17"
Write-Host "j21 : Switch to Java Temurin 21"
Write-Host "j25 : Switch to Java Temurin 25"
Write-Host "ginit : Set Gradle init file to active or inactive"
Write-Host "[ Security ]"
Write-Host "decb64 : Decode and prints a base64 encoded string (use with base64 string as argument)"
Write-Host "encb64 : Encode and prints a string as base64 (use with string as argument)"
Write-Host "deccertstr : Decode and prints the content certificate (use with certificate string as argument)"
Write-Host "deccertpem : Decode and prints the content of a PEM certificate file (use with absolute file path as argument)"
Write-Host "deckeypem : Decode and prints the content of a PEM private key file (use with absolute file path as argument)"
Write-Host "[ Utility ]"
Write-Host "uuid : Generate a random UUID"
Write-Host "ulid : Generate a random ULID"
Write-Host "ulids : Generate a random ULID silently i.e only copy to clipboard"
Write-Host "fixfe : Search for package.json files recursively in a repo and make ports Powershell syntax compatible"
Write-Host "ur : Update all repositories i.e. check out the default branch and pull (use with repository names as arguments to skip them)"
Write-Host "? : Show this list of parameters"
}
function ExecuteParameter($function, $remainingArgs) {
if ($remainingArgs) {
Write-Host "Arguments: '$remainingArgs' - will be ignored if function does not require them."
}
switch ($function) {
{$_ -eq "drcv" -or $_ -eq "drmcv"} { RemoveUnusedDockerContainersAndVolumes }
{$_ -eq "drv" -or $_ -eq "drmv"} { RemoveUnusedDockerVolumes }
{$_ -eq "dsrc"} { StopAndRemoveAllDockerContainers }
{$_ -eq "dsrcv"} { StopAndRemoveUnusedDockerContainersAndVolumes }
{$_ -eq "nmc"} { SpinUpNewMysqlDockerContainer }
{$_ -eq "mp"} { GetMysqlDockerContainerPortNumber }
{$_ -eq "mid"} { GetMysqlDockerContainerId }
{$_ -eq "mc"} { ExecuteCommandInMySqlContainer @remainingArgs }
{$_ -eq "infv"} { CopyInfraVariablesToClipboard }
{$_ -eq "gp"} { GetPortInfo }
{$_ -eq "pr"} { GetProcessInfo }
{$_ -eq "where"} { WhereIs @remainingArgs }
{$_ -eq "j21"} { SwitchToJava21 }
{$_ -eq "j25"} { SwitchToJava25 }
{$_ -eq "ginit"} { ToggleGradleInitFile }
{$_ -eq "decb64"} { DecodeBase64EncodedString @remainingArgs }
{$_ -eq "encb64"} { EncodeStringAsBase64 @remainingArgs }
{$_ -eq "deccertstr"} { DecodeCertificateString @remainingArgs }
{$_ -eq "deccertpem"} { DecodeCertificatePemFile @remainingArgs }
{$_ -eq "deckeypem"} { DecodePrivateKeyPemFile @remainingArgs }
{$_ -eq "uuid"} { GenerateUuid }
{$_ -eq "ulid"} { GenerateUlid }
{$_ -eq "ulids"} { GenerateUlidSilently }
{$_ -eq "fixfe"} { FixPackageJsonPortsNames }
{$_ -eq "ur"} { UpdateAllRepositories @remainingArgs }
{$_ -eq "?" -or $_ -eq "help"} { Help }
default { Write-Host "Error: Invalid parameter. No action taken."; break }
}
}
if ($null -eq $f) {
Write-Host "Need to provide one parameter (and optionally arguments). No action taken."
} else {
ExecuteParameter $f $remainingArgs
}
Write-Host ""
Save this script with any name (e.g. your name) and the
.ps1 extension. Add the script’s location to PATH in your
environment variables. Remember to update any locations used in the script (e.g. for switching Java versions). Then run
the script with e.g. kim ? to display the available commands.Microsoft.PowerShell_profile.ps1
# Initiate OMP styling
oh-my-posh init pwsh --config "$HOME\.config\oh-my-posh\kim-dark-island.omp.json" | Invoke-Expression
# Set general PowerShell settings
Set-PSReadLineKeyHandler -Chord Ctrl+u -Function RevertLine
Set-PSReadLineKeyHandler -Chord "Tab" -Function ForwardWord
Set-PSReadLineOption -Colors @{
Command = "`e[38;2;86;168;245m" # Dark Island blue/functions
Parameter = "`e[38;2;199;125;187m" # Dark Island purple/constants
String = "`e[38;2;106;171;115m" # Dark Island green/strings
Variable = "`e[38;2;207;142;109m" # Dark Island orange/keywords
}
# Disable PowerShell update warnings - may no longer work here; consider setting as an environment variable
$env:POWERSHELL_UPDATECHECK = 'Off'
# Set preferred editor for file-opening shortcuts (used by function fi below)
$PREFERRED_EDITOR = 'zed'
# Some IDEs set the working directory via -WorkingDirectory but PowerShell sometimes ignores it.
# This ensures we stay where the IDE puts us, assuming that you start the terminal in the IDE with:
# cmd /c "set IDE_TERMINAL=1 & pwsh"
# if ($env:IDE_TERMINAL) {
# return
# }
# Refresh Chocolatey profile (so that .$profile will refresh Chocolatey profile after a choco install)
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1
# Import the Chocolatey Profile
# Be aware that if you are missing these lines from your profile, tab completion for `choco` will not function. See https://ch0.co/tab-completion for details.
$ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
if (Test-Path($ChocolateyProfile)) {
Import-Module "$ChocolateyProfile"
}
# Declare aliases for git
function Invoke-GitPull
{
& git pull @args
}
Set-Alias -Name gp -Value Invoke-GitPull -Option AllScope -Force
function Invoke-GitCheckoutDefault
{
& git show-ref --verify --quiet refs/heads/main
if ($LASTEXITCODE -eq 0)
{
& git checkout main
} else
{
& git checkout master
}
}
Set-Alias -Name gcd -Value Invoke-GitCheckoutDefault -Option AllScope -Force
function Invoke-GitBranch
{
& git branch -a
}
Set-Alias -Name gb -Value Invoke-GitBranch -Option AllScope -Force
# Set miscellaneous aliases
Set-Alias -Name c -Value clear
Set-Alias -Name vim -Value 'nvim'
Set-Alias -Name idea -Value "$HOME\AppData\Local\Programs\IntelliJ IDEA Ultimate\bin\idea64.exe"
Set-Alias -Name webstorm -Value "$HOME\AppData\Local\Programs\WebStorm\bin\webstorm64.exe"
Set-Alias -Name rider -Value "$HOME\AppData\Local\Programs\Rider\bin\rider64.exe"
Set-Alias -Name rustrover -Value "$HOME\AppData\Local\Programs\RustRover\bin\rustrover64.exe"
Set-Alias -Name '..' -Value cd..
function ... { Set-Location -Path ..\.. }
function .... { Set-Location -Path ..\..\.. }
function ..... { Set-Location -Path ..\..\..\.. }
# Enable folder shortcuts
function fo {
switch ($args[0]) {
{ $_ -eq "user" -or $_ -eq "home" } { Set-Location -Path "$HOME" }
{ $_ -eq "downloads" -or $_ -eq "dl" } { Set-Location -Path "$HOME\Downloads" }
{ $_ -eq "pro" } { Set-Location -Path "$HOME\projects" }
{ $_ -eq "nvim" } { Set-Location -Path "$HOME\AppData\Local\nvim" }
{ $_ -eq "yazi" } { Set-Location -Path "$HOME\AppData\Roaming\yazi\config" }
{ $_ -eq "pi" } { Set-Location -Path "$HOME\.pi\agent" }
{ $_ -eq "claude" } { Set-Location -Path "$HOME\.claude" }
{ $_ -eq "help" -or $_ -eq "h" -or $_ -eq "?" }
Write-Host "Recognised are: user/home, dl/downloads, pro, nvim, yazi, pi, claude."
}
default {
Write-Host "Error: Folder not recognised. Run 'fo help' for a list of recognised folders."
Write-Host "Attempting to run 'cd $args' instead..."
$ErrorActionPreference = 'SilentlyContinue'
Set-Location -Path $args[0]
$ErrorActionPreference = 'Continue'
}
}
}
# Allow opening files in your $PREFERRED_EDITOR e.g. 'fi ssh'
function fi {
switch ($args[0]) {
{ $_ -eq "ssh" } { & $PREFERRED_EDITOR "$HOME\.ssh\config" }
{ $_ -eq "aws" } { & $PREFERRED_EDITOR "$HOME\.aws\config" }
{ $_ -eq "profile" -or $_ -eq "p" } { & $PREFERRED_EDITOR "$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1" }
{ $_ -eq "kim" } { & $PREFERRED_EDITOR "$HOME\Documents\PowerShell\Scripts\kim.ps1" }
{ $_ -eq "tridactyl" } { & $PREFERRED_EDITOR "$HOME\.config\tridactyl\tridactlyrc" }
{ $_ -eq "wezterm" } { & $PREFERRED_EDITOR "$HOME\.wezterm.lua" }
{ $_ -eq "yazi" } { & $PREFERRED_EDITOR "$HOME\AppData\Roaming\yazi\config\yazi.toml" }
default {
Write-Host "Error: File not recognised."
Write-Host "Recognised files are ssh, aws, p/profile, kim, tridactyl, wezterm, yazi."
}
}
}
# Set y as Yazi shortcut and return to Yazi directory in the terminal
function y {
$tmp = [System.IO.Path]::GetTempFileName()
yazi $args --cwd-file="$tmp"
$cwd = Get-Content -Path $tmp -Encoding UTF8
if (-not [String]::IsNullOrEmpty($cwd) -and $cwd -ne $PWD.Path) {
Set-Location -LiteralPath ([System.IO.Path]::GetFullPath($cwd))
}
Remove-Item -Path $tmp
}
# Review or explain a GitHub pull request from anywhere
function pr
{
param([string]$Url, [string]$Mode)
if (-not $Url -or -not $Mode)
{
Write-Host "Error: Usage is pr {pull request URL} {review|explain}."
return
}
if ($Mode -ne "review" -and $Mode -ne "explain")
{
Write-Host "Error: Mode must be 'review' or 'explain', not '$Mode'."
return
}
if ($Url -notmatch "github\.com/([^/]+)/([^/]+)/pull/(\d+)")
{
Write-Host "Error: Not a GitHub pull request URL: $Url"
return
}
$owner = $Matches[1]
$repository = $Matches[2]
$number = $Matches[3]
$path = $null
foreach ($root in @("$HOME\projects")) # Comma-separated list of folders to check
{
if (Test-Path -Path "$root\$repository")
{
$path = "$root\$repository"
break
}
}
if (-not $path)
{
$path = "$HOME\projects\$repository"
Write-Host "Cloning $owner/$repository into $path..."
& gh repo clone "$owner/$repository" $path
if ($LASTEXITCODE -ne 0)
{
Write-Host "Error: Could not clone $owner/$repository."
return
}
}
Set-Location -Path $path
& git fetch --quiet origin
& claude "/$Mode-pr $number"
}
update-repos.sh
#!/bin/sh
# Checks out the default branch and pulls, for every git repo in $REPOSITORY_BASE_FOLDER,
# or in ~/projects when that variable is unset or empty.
# Repo names given as arguments are skipped, e.g. ./update-repos.sh some-repo other-repo
set -u
base_folder="${REPOSITORY_BASE_FOLDER:-$HOME/projects}"
# Never updated, whatever the arguments say.
always_excluded="agent-storage"
failures=""
excluded=" $always_excluded $* "
for directory in "$base_folder"/*/
do
[ -e "$directory/.git" ] || continue
name=$(basename "$directory")
case "$excluded" in
*" $name "*)
printf '%s: skipped\n' "$name"
continue
;;
esac
branch=$(git -C "$directory" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||')
if [ -z "$branch" ]
then
for candidate in main master
do
git -C "$directory" show-ref --verify --quiet "refs/heads/$candidate" 2>/dev/null && branch=$candidate && break
done
fi
if [ -z "$branch" ]
then
failures="$failures$name: no default branch (main/master) found
"
continue
fi
printf '%s (%s): ' "$name" "$branch"
if ! error=$(git -C "$directory" checkout "$branch" 2>&1)
then
printf 'checkout failed\n'
failures="$failures$name: checkout $branch failed - $(printf '%s' "$error" | head -n 1)
"
continue
fi
if ! error=$(git -C "$directory" pull --ff-only 2>&1)
then
printf 'pull failed\n'
failures="$failures$name: pull failed - $(printf '%s' "$error" | head -n 1)
"
continue
fi
printf 'ok\n'
done
if [ -n "$failures" ]
then
printf '\nNot updated:\n'
printf '%s' "$failures" | sed 's/^/ /'
else
printf '\nAll repositories updated.\n'
fi
