diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..09e2a69
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,13 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+ FriedrichWeinmann
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..7d3751a
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,24 @@
+on:
+ push:
+ branches:
+ - master
+ - main
+
+jobs:
+ build:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install Prerequisites
+ run: .\build\psf-prerequisites.ps1 -Bootstrap
+ shell: powershell
+ - name: Validate
+ run: .\build\vsts-validate.ps1
+ shell: powershell
+ - name: Build
+ run: .\build\psf-build.ps1 -ApiKey $env:APIKEY
+ shell: powershell
+ env:
+ APIKEY: ${{ secrets.ApiKey }}
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
new file mode 100644
index 0000000..fd51397
--- /dev/null
+++ b/.github/workflows/validate.yml
@@ -0,0 +1,15 @@
+on: [pull_request]
+
+jobs:
+ validate:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install Prerequisites
+ run: .\build\psf-prerequisites.ps1 -Bootstrap
+ shell: powershell
+ - name: Validate
+ run: .\build\vsts-validate.ps1
+ shell: powershell
diff --git a/PSUtil/PSUtil.psd1 b/PSUtil/PSUtil.psd1
index 6bfd9eb..c77a737 100644
--- a/PSUtil/PSUtil.psd1
+++ b/PSUtil/PSUtil.psd1
@@ -27,8 +27,8 @@
# Modules that must be imported into the global environment prior to importing
# this module
RequiredModules = @(
- @{ ModuleName = 'PSFramework'; ModuleVersion = '1.6.205' }
- @{ ModuleName = 'string'; ModuleVersion = '1.0.0' }
+ @{ ModuleName = 'PSFramework'; ModuleVersion = '1.12.346' }
+ @{ ModuleName = 'string'; ModuleVersion = '1.1.5' }
)
# Assemblies that must be loaded prior to importing this module
@@ -55,6 +55,7 @@
'Register-PSUObjectConversion'
'Register-PSUObjectExpansion'
'Read-PSUKnowledge'
+ 'Remove-PSUDirectory'
'Remove-PSUKnowledge'
'Remove-PSUPathAlias'
'Select-PSUFunctionCode'
@@ -108,6 +109,7 @@
'ocb'
'ov'
'page'
+ 'rcd'
'rdk'
'read'
'Remove-PSUString'
diff --git a/PSUtil/changelog.md b/PSUtil/changelog.md
index 44d99a8..acdc59d 100644
--- a/PSUtil/changelog.md
+++ b/PSUtil/changelog.md
@@ -1,90 +1,108 @@
# Changelog
+## 2.2.38 (2025-05-31)
+
++ New: Remove-PSUDirectory - Removes the current directory and moves the console to the parent folder.
++ Upd: Prompt Fred - added nested prompt level if greater than 0
++ Fix: Remove-PSUKnowledge - now actually works
+
## 2.2.35 (2021-10-13)
-- New: Object Conversion - NT <--> SID
-- New: Object Conversion - string <--> base64
-- New: Command Get-PSUCalendarWeek - Calculates the calendar week of the specified date.
-- Upd: Start-PSUTimer - added `-RandomInterval` parameter, randomizing the sound playback interval
-- Upd: Start-PSUTimer - added `-DisableScreensaver` parameter, blocking screensaver for the duration of the timer
-- Upd: Start-PSUTimer - now automatically adds a day if the destination-time is in the past
-- Upd: Manifest - added ctx and cfx to the aliases exported, enabling auto-import from using those.
++ New: Object Conversion - NT <--> SID
++ New: Object Conversion - string <--> base64
++ New: Command Get-PSUCalendarWeek - Calculates the calendar week of the specified date.
++ Upd: Start-PSUTimer - added `-RandomInterval` parameter, randomizing the sound playback interval
++ Upd: Start-PSUTimer - added `-DisableScreensaver` parameter, blocking screensaver for the duration of the timer
++ Upd: Start-PSUTimer - now automatically adds a day if the destination-time is in the past
++ Upd: Manifest - added ctx and cfx to the aliases exported, enabling auto-import from using those.
## 2.1.28 (2020-03-21)
- - New: Component: Knowledge - store and search knowledge
- - New: Command: Read-PSUKnowledge (read, page, learn) - retrieves stored knowledge
- - New: Command: Write-PSUKnowledge - writes knowledge for later retrieval
- - New: Command: Remove-PSUKnowledge - removes knowledge from storage. Use on alternative facts.
- - New: Command: Out-PSUVariable - writes input to variable, enabling variable assignment at the end of the pipeline
- - New: Alias rmn --> Remove-PSFNull
- - New: Dependency on string module, dropping own string cmdlets, ading aliases for compatibility
- - New: Adding psf tab completion for property names to common object commands
+
++ New: Component: Knowledge - store and search knowledge
++ New: Command: Read-PSUKnowledge (read, page, learn) - retrieves stored knowledge
++ New: Command: Write-PSUKnowledge - writes knowledge for later retrieval
++ New: Command: Remove-PSUKnowledge - removes knowledge from storage. Use on alternative facts.
++ New: Command: Out-PSUVariable - writes input to variable, enabling variable assignment at the end of the pipeline
++ New: Alias rmn --> Remove-PSFNull
++ New: Dependency on string module, dropping own string cmdlets, ading aliases for compatibility
++ New: Adding psf tab completion for property names to common object commands
## 2.0.20 (2019-09-18)
- - New: Command: Set-PSUPrompt - Applies a prompt from a set of pre-defined prompts
- - New: Command: Register-PSUObjectExpansion - Registers custom expansion rules for Expand-PSUObject
- - Upd: Start-PSUTimer - Added -MinFrequency and -MaxFrequency parameters.
- - Upd: Start-PSUTimer - Refactored parameter order, rationalized message handling
- - Upd: Invoke-PSUExplorer - Enabled opt-in exceptions and improved path resolution
- - Upd: Set-PSUPath - Now supports environment variable expansion using %name% notation.
- - Fix: Importing module from UNC path works
- - Fix: Module import concurrency issue
- - Fix: Convert-PSUObject unsafe Scriptblock handling
- - Fix: Set-PSUShell foreground color handling (requires latest pre-release version of PSReadline)
- - Fix: F1 Keybinding: On non-windows default to detailed help, rather than -ShowWindow
- - Fix: F1 Keybinding: In-Console help display now uses same application as launching application
+
++ New: Command: Set-PSUPrompt - Applies a prompt from a set of pre-defined prompts
++ New: Command: Register-PSUObjectExpansion - Registers custom expansion rules for Expand-PSUObject
++ Upd: Start-PSUTimer - Added -MinFrequency and -MaxFrequency parameters.
++ Upd: Start-PSUTimer - Refactored parameter order, rationalized message handling
++ Upd: Invoke-PSUExplorer - Enabled opt-in exceptions and improved path resolution
++ Upd: Set-PSUPath - Now supports environment variable expansion using %name% notation.
++ Fix: Importing module from UNC path works
++ Fix: Module import concurrency issue
++ Fix: Convert-PSUObject unsafe Scriptblock handling
++ Fix: Set-PSUShell foreground color handling (requires latest pre-release version of PSReadline)
++ Fix: F1 Keybinding: On non-windows default to detailed help, rather than -ShowWindow
++ Fix: F1 Keybinding: In-Console help display now uses same application as launching application
## Version 2.0.8 (2019-01-13)
- - New: Keybinding for `Shift+SpaceBar` on PSReadline 2.0 that inserts a whitespace, helping to mitigate the typing issue in the windows release version.
- - Upd: Switched input property tab completion to PSFramework implementation: PSFramework-Input-ObjectProperty
- - Upd: Default keybinding for expanding aliases was changed to `Alt` + `q`. The previous one was inoperable under PSReadline 2.0 or later.
- - Fix: Get-PSUPathAlias returns empty objects (thanks Corbob)
+
++ New: Keybinding for `Shift+SpaceBar` on PSReadline 2.0 that inserts a whitespace, helping to mitigate the typing issue in the windows release version.
++ Upd: Switched input property tab completion to PSFramework implementation: PSFramework-Input-ObjectProperty
++ Upd: Default keybinding for expanding aliases was changed to `Alt` + `q`. The previous one was inoperable under PSReadline 2.0 or later.
++ Fix: Get-PSUPathAlias returns empty objects (thanks Corbob)
## Version 2.0.4 (2018-12-23)
- - Fix: Persisting default aliases redirection fails
+
++ Fix: Persisting default aliases redirection fails
## Version 2.0.3 (2018-12-23)
- - New: Configuration setting 'PSUtil.Import.Alias.SystemOverride'. Persisting this will have PSUtil replace system default aliases.
- - New: Tab Expansion for PowerShellGet
- - New: Tab Expansion for Select-Object and Select-PSFObject
+
++ New: Configuration setting 'PSUtil.Import.Alias.SystemOverride'. Persisting this will have PSUtil replace system default aliases.
++ New: Tab Expansion for PowerShellGet
++ New: Tab Expansion for Select-Object and Select-PSFObject
## Version 2.0.0 (2018-12-15)
- - new: Command Get-PSUPathAlias lists all current path aliases
- - new: Command Remove-PSUPathAlias removes a path alias
- - new: Command Set-PSUPath used to implement the path alias functionality
- - new: Command Set-PSUPathAlias creates or updates an alias for a path
- - other: Major project refactoring
- - rem: Command `Select-PSUObject` has been removed
- - new: Alias `Select-PSUObject` pointing at `Select-PSFPobject`
- - upd: Alias `ex` now points at `Export-PSFClixml`
- - upd: Alias `ix` now points at `Import-PSFClixml`
- - upd: Alias `spo` now points at `Select-PSFObject`
- - upd: Moved the list of default properties to expand using Expand-PSUObject to configuration
+
++ new: Command Get-PSUPathAlias lists all current path aliases
++ new: Command Remove-PSUPathAlias removes a path alias
++ new: Command Set-PSUPath used to implement the path alias functionality
++ new: Command Set-PSUPathAlias creates or updates an alias for a path
++ other: Major project refactoring
++ rem: Command `Select-PSUObject` has been removed
++ new: Alias `Select-PSUObject` pointing at `Select-PSFPobject`
++ upd: Alias `ex` now points at `Export-PSFClixml`
++ upd: Alias `ix` now points at `Import-PSFClixml`
++ upd: Alias `spo` now points at `Select-PSFObject`
++ upd: Moved the list of default properties to expand using Expand-PSUObject to configuration
## Version 1.1.5.17 (2018-06-19)
- - new: Command Select-PSUObject - Select-Object in awesome.
- - upd: Command Set-PSUString / replace - can now do lambda replacement
+
++ new: Command Select-PSUObject - Select-Object in awesome.
++ upd: Command Set-PSUString / replace - can now do lambda replacement
## Version 1.1.4.15 (2018-03-30)
- - new: Select-PSUFunctionCode / Inspect
+
++ new: Select-PSUFunctionCode / Inspect
## Version 1.1.3.13 (2018-03-20)
- - new: Command Start-PSUTimer / timer
+
++ new: Command Start-PSUTimer / timer
## Version 1.1.2.12 (2018-03-06)
- - new: Command Backup-PSULocation / bu (#18)
- - new: Command Set-PSUDrive / set-as (#18)
- - new: Command New-PSUDirectory / mcd (#18)
+
++ new: Command Backup-PSULocation / bu (#18)
++ new: Command Set-PSUDrive / set-as (#18)
++ new: Command New-PSUDirectory / mcd (#18)
## Version 1.1.1.10 (2018-02-23)
- - upd: Select-PSUObjectSample / s can now select last items (#15)
- - fix: Format-PSUString / format will now format numbers correctly (#14)
+
++ upd: Select-PSUObjectSample / s can now select last items (#15)
++ fix: Format-PSUString / format will now format numbers correctly (#14)
## Version 1.1.1.9 (2018-02-02)
- - new: Command Convert-PSUObject / convert. (#9)
- Allows converting input from a source format to a destination format. Can be dynamically extended.
- - new: Command Register-PSUObjectConversion. (#9)
- Registers conversion path for Convert-PSUObject.
- - new: Added module tests and build definition for CI/CD integration
- - upd: All keybindings can now be configured using the config system. Changes require reimport to apply. (#7)
- - fix: Fixed 'replace' not accepting empty string as replace with option (#8)
\ No newline at end of file
+
++ new: Command Convert-PSUObject / convert. (#9)
+ Allows converting input from a source format to a destination format. Can be dynamically extended.
++ new: Command Register-PSUObjectConversion. (#9)
+ Registers conversion path for Convert-PSUObject.
++ new: Added module tests and build definition for CI/CD integration
++ upd: All keybindings can now be configured using the config system. Changes require reimport to apply. (#7)
++ fix: Fixed 'replace' not accepting empty string as replace with option (#8)
\ No newline at end of file
diff --git a/PSUtil/en-us/strings.psd1 b/PSUtil/en-us/strings.psd1
index 22c131a..58e5cc3 100644
--- a/PSUtil/en-us/strings.psd1
+++ b/PSUtil/en-us/strings.psd1
@@ -1,5 +1,8 @@
@{
+ 'Remove-PSUDirectory.Error.IsRootPath' = 'This is the drive root, not deleting the entire volume for you! {0}' # location
+ 'Remove-PSUDirectory.Error.NotFileSystem' = 'Not a filesystem path, cannot remove the current location: {0}' # $location
+ 'Remove-PSUDirectory.RemoveItem' = 'Removing {0} items in {1}' # $children.Count, $location
'Set-PSUKnowledge.Library.Path.CreationFailed' = 'Failed to create library path: {0}. Validate the path and ensure the necessary permissions are in place.' # $libraryPath
- 'Set-PSUKnowledge.Book.ImportFailed' = 'Failed to read the book "{0}" from "{1}"' # $Book, $bookPath
- 'Set-PSUKnowledge.Book.ExportFailed' = 'Failed to write the page "{0}" into the book "{1}" at "{2}"' # $Name, $Book, $bookPath
+ 'Set-PSUKnowledge.Book.ImportFailed' = 'Failed to read the book "{0}" from "{1}"' # $Book, $bookPath
+ 'Set-PSUKnowledge.Book.ExportFailed' = 'Failed to write the page "{0}" into the book "{1}" at "{2}"' # $Name, $Book, $bookPath
}
\ No newline at end of file
diff --git a/PSUtil/functions/explorer/Remove-PSUDirectory.ps1 b/PSUtil/functions/explorer/Remove-PSUDirectory.ps1
new file mode 100644
index 0000000..3973e6b
--- /dev/null
+++ b/PSUtil/functions/explorer/Remove-PSUDirectory.ps1
@@ -0,0 +1,61 @@
+function Remove-PSUDirectory {
+ <#
+ .SYNOPSIS
+ Removes the current directory and moves the console to the parent folder.
+
+ .DESCRIPTION
+ Removes the current directory and moves the console to the parent folder.
+
+ .PARAMETER Force
+ Don't ask questions, just do it.
+
+ .PARAMETER EnableException
+ This parameters disables user-friendly warnings and enables the throwing of exceptions.
+ This is less user friendly, but allows catching exceptions in calling scripts.
+
+ .PARAMETER WhatIf
+ If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
+
+ .PARAMETER Confirm
+ If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
+
+ .EXAMPLE
+ PS C:\> rcd
+
+ Tries to remove the directory and step back one level.
+ #>
+ [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
+ param (
+ [switch]
+ $Force,
+
+ [switch]
+ $EnableException
+ )
+ process {
+ $location = Get-Location
+ if ($location.Provider.Name -ne 'FileSystem') {
+ Stop-PSFFunction -String 'Remove-PSUDirectory.Error.NotFileSystem' -StringValues $location -EnableException $EnableException -Cmdlet $PSCmdlet
+ return
+ }
+
+ if ($location.Path -eq "$($location.Drive):\") {
+ Stop-PSFFunction -String 'Remove-PSUDirectory.Error.IsRootPath' -StringValues $location -EnableException $EnableException -Cmdlet $PSCmdlet
+ return
+ }
+
+ $children = Get-ChildItem -LiteralPath $location -Force -Recurse
+ if ($children.Count -lt 1) {
+ Set-Location -Path '..'
+ Remove-Item -LiteralPath $location -Force -Confirm:$false
+ return
+ }
+
+ if ($Force -or (Test-PSFShouldProcess -Target $location -ActionString 'PSUtil.Remove-PSUDirectory.RemoveItem' -ActionStringValues $children.Count, $location -PSCmdlet $PSCmdlet)) {
+ Set-Location -Path '..'
+ Remove-Item -LiteralPath $location -Force -Confirm:$false
+ }
+ }
+}
+
+Import-PSUAlias -Name "rcd" -Command "Remove-PSUDirectory"
\ No newline at end of file
diff --git a/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1 b/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1
index ac28d44..f34e8dc 100644
--- a/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1
+++ b/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1
@@ -42,7 +42,7 @@
{
if (-not (Test-Path -Path $libraryPath)) { return }
- $bookName = '{0}.json' -f [System.Text.Encoding]::UTF8.GetBytes($Book)
+ $bookName = '{0}.json' -f [System.Convert]::ToBase64String(([System.Text.Encoding]::UTF8.GetBytes($Book)))
$bookPath = Join-Path $libraryPath $bookName
if (-not (Test-Path -Path $bookPath)) { return }
diff --git a/PSUtil/internal/prompts/fred.prompt.ps1 b/PSUtil/internal/prompts/fred.prompt.ps1
index 5a18166..3a85d5e 100644
--- a/PSUtil/internal/prompts/fred.prompt.ps1
+++ b/PSUtil/internal/prompts/fred.prompt.ps1
@@ -9,6 +9,9 @@
}
}
catch { }
+ if ($NestedPromptLevel -gt 0) {
+ Write-PSFHostColor -String "[$NestedPromptLevel] " -NoNewLine -DefaultColor DarkGreen
+ }
$segments = $executionContext.SessionState.Path.CurrentLocation.Path -split "\\"
if ($segments.Count -lt 4) { Write-Host "$($executionContext.SessionState.Path.CurrentLocation.Path)" -NoNewline }
else { Write-Host "($($segments.Count - 2)) ..\$($segments[-2])\$($segments[-1])" -NoNewline }
diff --git a/build/psf-build.ps1 b/build/psf-build.ps1
new file mode 100644
index 0000000..04f2850
--- /dev/null
+++ b/build/psf-build.ps1
@@ -0,0 +1,72 @@
+<#
+This script publishes the module to the gallery.
+It expects as input an ApiKey authorized to publish the module.
+
+Insert any build steps you may need to take before publishing it here.
+#>
+param (
+ $ApiKey,
+
+ $WorkingDirectory = $env:SYSTEM_DEFAULTWORKINGDIRECTORY
+)
+
+# Prepare publish folder
+Write-PSFMessage -Level Important -Message "Creating and populating publishing directory"
+$publishDir = New-Item -Path $WorkingDirectory -Name publish -ItemType Directory -Force
+Remove-Item -Path "$publishDir\*" -Recurse -Force -ErrorAction SilentlyContinue
+Copy-Item -Path "$($WorkingDirectory)\PSUtil" -Destination $publishDir.FullName -Recurse -Force
+
+#region Gather text data to compile
+$text = @()
+$processed = @()
+
+# Gather Stuff to run before
+foreach ($line in (Get-Content "$($PSScriptRoot)\filesBefore.txt" | Where-Object { $_ -notlike "#*" }))
+{
+ if ([string]::IsNullOrWhiteSpace($line)) { continue }
+
+ $basePath = Join-Path "$($publishDir.FullName)\PSUtil" $line
+ foreach ($entry in (Resolve-PSFPath -Path $basePath))
+ {
+ $item = Get-Item $entry
+ if ($item.PSIsContainer) { continue }
+ if ($item.FullName -in $processed) { continue }
+ $text += [System.IO.File]::ReadAllText($item.FullName)
+ $processed += $item.FullName
+ }
+}
+
+# Gather commands
+Get-ChildItem -Path "$($publishDir.FullName)\PSUtil\internal\functions\" -Recurse -File -Filter "*.ps1" | ForEach-Object {
+ $text += [System.IO.File]::ReadAllText($_.FullName)
+}
+Get-ChildItem -Path "$($publishDir.FullName)\PSUtil\functions\" -Recurse -File -Filter "*.ps1" | ForEach-Object {
+ $text += [System.IO.File]::ReadAllText($_.FullName)
+}
+
+# Gather stuff to run afterwards
+foreach ($line in (Get-Content "$($PSScriptRoot)\filesAfter.txt" | Where-Object { $_ -notlike "#*" }))
+{
+ if ([string]::IsNullOrWhiteSpace($line)) { continue }
+
+ $basePath = Join-Path "$($publishDir.FullName)\PSUtil" $line
+ foreach ($entry in (Resolve-PSFPath -Path $basePath))
+ {
+ $item = Get-Item $entry
+ if ($item.PSIsContainer) { continue }
+ if ($item.FullName -in $processed) { continue }
+ $text += [System.IO.File]::ReadAllText($item.FullName)
+ $processed += $item.FullName
+ }
+}
+#endregion Gather text data to compile
+
+#region Update the psm1 file
+$fileData = Get-Content -Path "$($publishDir.FullName)\PSUtil\PSUtil.psm1" -Raw
+$fileData = $fileData.Replace('""', '""')
+$fileData = $fileData.Replace('""', ($text -join "`n`n"))
+[System.IO.File]::WriteAllText("$($publishDir.FullName)\PSUtil\PSUtil.psm1", $fileData, [System.Text.Encoding]::UTF8)
+#endregion Update the psm1 file
+
+# Publish to Gallery
+Publish-PSFModule -Path "$($publishDir.FullName)\PSUtil" -ApiKey $ApiKey
\ No newline at end of file
diff --git a/build/psf-prerequisites.ps1 b/build/psf-prerequisites.ps1
new file mode 100644
index 0000000..0645544
--- /dev/null
+++ b/build/psf-prerequisites.ps1
@@ -0,0 +1,28 @@
+param (
+ [string]
+ $Repository = 'PSGallery',
+
+ [switch]
+ $Bootstrap
+)
+
+if ($Bootstrap) {
+ Invoke-WebRequest 'https://raw.githubusercontent.com/PowershellFrameworkCollective/PSFramework.NuGet/refs/heads/master/bootstrap.ps1' | Invoke-Expression
+}
+
+$modules = @("Pester", "PSFramework", "PSModuleDevelopment", "PSScriptAnalyzer")
+
+# Automatically add missing dependencies
+$data = Import-PowerShellDataFile -Path "$PSScriptRoot\..\PSUtil\PSUtil.psd1"
+foreach ($dependency in $data.RequiredModules) {
+ if ($dependency -is [string]) {
+ if ($modules -contains $dependency) { continue }
+ $modules += $dependency
+ }
+ else {
+ if ($modules -contains $dependency.ModuleName) { continue }
+ $modules += $dependency.ModuleName
+ }
+}
+
+Install-PSFModule -Name $modules
\ No newline at end of file