Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ test/tmp/*
*~
TestResults.xml
supporting/sqlite/*
.vscode/*
*.sln
5 changes: 5 additions & 0 deletions lib/core.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -835,8 +835,13 @@ function get_app_name($path) {
$appName = $Matches[1].ToLower()
} elseif ((Test-Path (appsdir $true)) -and ($path -match "$([Regex]::Escape($(Convert-Path (appsdir $true))))[/\\]([^/\\]+)")) {
$appName = $Matches[1].ToLower()
} elseif ($path -match '[/\\]([^/\\]+?)(?:\.[^/\\"]+?)') {
# Fallback: extract the executable file name (with extension if present): [/\\]([^/\\]+?)(?:\.?[^./\\]+?)`"
$appName = $Matches[1].ToLower()
Write-Host $path + '1' + $appName
} else {
$appName = ''
Write-Host $path + '2' + $appName
}
return $appName
}
Expand Down
141 changes: 118 additions & 23 deletions libexec/scoop-shim.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
#
# scoop shim rm <shim_name> [<shim_name>...]
#
# To list all shims or matching shims, use the 'list' subcommand:
# To list all shims or matching shims, use the 'list' subcommand (`-added` to show shims added by user in config):
#
# scoop shim list [<regex_pattern>...]
# scoop shim list -added [<regex_pattern>...]
#
# To show a shim's information, use the 'info' subcommand:
#
Expand All @@ -31,12 +31,68 @@
#
# scoop shim add myapp 'D:\path\myapp.exe' '--' myapp_args --global

# Main updated features:
# 1) `scoop shim add/rm` persist shims in the config file,
# 2) `scoop shim list` supports the --added [pattern|optional] option to display or search only user-added shims.

param($SubCommand)

. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\core.ps1" # for config related ops
. "$PSScriptRoot\..\lib\install.ps1" # for rm_shim
. "$PSScriptRoot\..\lib\system.ps1" # 'Add-Path' (indirectly)


# Read the configuration of manually added shims
function Get-AddedShimsConfig {
$added = get_config 'added'
if ($null -eq $added) {
return [PSCustomObject]@{}
}
return $added
}

# Add shim to config
function Add-ShimToConfig($shimName, $global, $commandPath, $commandArgs) {
$added = Get-AddedShimsConfig

# Use new structure: shimName as key, global as a boolean field
$shimInfo = [PSCustomObject]@{
CommandPath = $commandPath
CommandArgs = $commandArgs -join ' '
AddedDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
global = $global
}

# If shimName does not exist, add it
if (-not $added.PSObject.Properties[$shimName]) {
$added | Add-Member -MemberType NoteProperty -Name $shimName -Value $shimInfo
} else {
$added.$shimName = $shimInfo
}

set_config 'added' $added | Out-Null
}
Comment on lines +56 to +75
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid dotted property access for arbitrary shim names; use PSObject property bag.

Shim names may contain dots/hyphens. $added.$shimName will break. Use PSObject.Properties[$name] for both add and update. Also store args as an array to preserve tokens.

 function Add-ShimToConfig($shimName, $global, $commandPath, $commandArgs) {
     $added = Get-AddedShimsConfig
 
     # Use new structure: shimName as key, global as a boolean field
     $shimInfo = [PSCustomObject]@{
         CommandPath = $commandPath
-        CommandArgs = $commandArgs -join ' '
+        CommandArgs = $commandArgs
         AddedDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
         global = $global
     }
 
-    # If shimName does not exist, add it
-    if (-not $added.PSObject.Properties[$shimName]) {
-        $added | Add-Member -MemberType NoteProperty -Name $shimName -Value $shimInfo
-    } else {
-        $added.$shimName = $shimInfo
-    }
+    $prop = $added.PSObject.Properties[$shimName]
+    if ($prop) {
+        $prop.Value = $shimInfo
+    } else {
+        $added | Add-Member -MemberType NoteProperty -Name $shimName -Value $shimInfo
+    }
 
     set_config 'added' $added | Out-Null
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function Add-ShimToConfig($shimName, $global, $commandPath, $commandArgs) {
$added = Get-AddedShimsConfig
# Use new structure: shimName as key, global as a boolean field
$shimInfo = [PSCustomObject]@{
CommandPath = $commandPath
CommandArgs = $commandArgs -join ' '
AddedDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
global = $global
}
# If shimName does not exist, add it
if (-not $added.PSObject.Properties[$shimName]) {
$added | Add-Member -MemberType NoteProperty -Name $shimName -Value $shimInfo
} else {
$added.$shimName = $shimInfo
}
set_config 'added' $added | Out-Null
}
function Add-ShimToConfig($shimName, $global, $commandPath, $commandArgs) {
$added = Get-AddedShimsConfig
# Use new structure: shimName as key, global as a boolean field
$shimInfo = [PSCustomObject]@{
CommandPath = $commandPath
CommandArgs = $commandArgs
AddedDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
global = $global
}
$prop = $added.PSObject.Properties[$shimName]
if ($prop) {
$prop.Value = $shimInfo
} else {
$added | Add-Member -MemberType NoteProperty -Name $shimName -Value $shimInfo
}
set_config 'added' $added | Out-Null
}


# Remove shim from config
function Remove-ShimFromConfig($shimName, $global) {
$added = Get-AddedShimsConfig

# Check if shim exists and matches global flag
if ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global) {
$added.PSObject.Properties.Remove($shimName)
set_config 'added' $added | Out-Null
}
}
Comment on lines +77 to +86
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Property removal must not rely on dotted access; compare flag via property value.

Direct $added.$shimName.global fails for keys with special chars. Use the property bag value.

 function Remove-ShimFromConfig($shimName, $global) {
     $added = Get-AddedShimsConfig
 
     # Check if shim exists and matches global flag
-    if ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global) {
-        $added.PSObject.Properties.Remove($shimName)
+    $prop = $added.PSObject.Properties[$shimName]
+    if ($prop -and $prop.Value.global -eq $global) {
+        [void]$added.PSObject.Properties.Remove($shimName)
         set_config 'added' $added | Out-Null
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Remove shim from config
function Remove-ShimFromConfig($shimName, $global) {
$added = Get-AddedShimsConfig
# Check if shim exists and matches global flag
if ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global) {
$added.PSObject.Properties.Remove($shimName)
set_config 'added' $added | Out-Null
}
}
# Remove shim from config
function Remove-ShimFromConfig($shimName, $global) {
$added = Get-AddedShimsConfig
# Check if shim exists and matches global flag
$prop = $added.PSObject.Properties[$shimName]
if ($prop -and $prop.Value.global -eq $global) {
[void]$added.PSObject.Properties.Remove($shimName)
set_config 'added' $added | Out-Null
}
}
🤖 Prompt for AI Agents
In libexec/scoop-shim.ps1 around lines 77 to 86, the code uses dotted access
($added.$shimName.global) which breaks for property names with special
characters; instead retrieve the property object and read its Value field.
Change the condition to get the property: $prop =
$added.PSObject.Properties[$shimName]; then check if ($prop -and
$prop.Value.global -eq $global) before removing the property and calling
set_config 'added' $added | Out-Null.


# Test if shim is manually added
function Test-ShimAdded($shimName, $gqlobal) {
$added = Get-AddedShimsConfig

# Check if shim exists and matches global flag
return ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global)
}
Comment on lines +89 to +94
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Fix parameter name typo causing undefined variable reference.

Line 89 declares the parameter as $gqlobal (typo), but line 93 references $global, which is undefined in this function scope. This will cause the function to always return $false or produce incorrect results.

Apply this diff to fix the typo:

-function Test-ShimAdded($shimName, $gqlobal) {
+function Test-ShimAdded($shimName, $global) {
     $added = Get-AddedShimsConfig
 
     # Check if shim exists and matches global flag
     return ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function Test-ShimAdded($shimName, $gqlobal) {
$added = Get-AddedShimsConfig
# Check if shim exists and matches global flag
return ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global)
}
function Test-ShimAdded($shimName, $global) {
$added = Get-AddedShimsConfig
# Check if shim exists and matches global flag
return ($added.PSObject.Properties[$shimName] -and $added.$shimName.global -eq $global)
}
🤖 Prompt for AI Agents
In libexec/scoop-shim.ps1 around lines 89 to 94, the function Test-ShimAdded
declares the parameter as $gqlobal but later checks $global, causing an
undefined variable reference; rename the parameter to $global (or update all
references to match the intended name) so the parameter and its usage are
consistent, then run a quick syntax check to ensure no other references to the
misspelled name remain.


if ($SubCommand -notin @('add', 'rm', 'list', 'info', 'alter')) {
if (!$SubCommand) {
error '<subcommand> missing'
Expand All @@ -47,19 +103,21 @@ if ($SubCommand -notin @('add', 'rm', 'list', 'info', 'alter')) {
exit 1
}

$opt, $other, $err = getopt $Args 'g' 'global'
# Update getopt parsing to support --added option
$opt, $other, $err = getopt $Args 'ga' 'global', 'added'
if ($err) { "scoop shim: $err"; exit 1 }

$global = $opt.g -or $opt.global
$showOnlyAdded = $opt.a -or $opt.added

if ($SubCommand -ne 'list' -and $other.Length -eq 0) {
error "<shim_name> must be specified for subcommand '$SubCommand'"
my_usage
my_usages
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify function name change.

Line 115 calls my_usages (plural), but the help text at the top shows my_usage (singular). Ensure this function exists in the imported modules.


🏁 Script executed:

#!/bin/bash
# Search for my_usage and my_usages function definitions
rg -n "function (my_usage|my_usages)" lib/ supporting/ libexec/

Length of output: 71


Rename function call to match defined helper
In libexec/scoop-shim.ps1 at line 115, the code calls my_usages, but only my_usage (singular) is defined (lib/help.ps1:14). Update the invocation:

-    my_usages
+    my_usage
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
my_usages
my_usage
🤖 Prompt for AI Agents
In libexec/scoop-shim.ps1 around line 115, the script calls my_usages but the
defined helper is my_usage (singular); replace the incorrect call with my_usage
so the correct function is invoked and avoid undefined function errors.

exit 1
}

if (-not (Get-FormatData ScoopShims)) {
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
Update-FormatData "$PSScriptRoot\..\apps\scoop\current\supporting\formats\ScoopTypes.Format.ps1xml"
}
Comment on lines 119 to 121
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Derive format data path robustly; avoid hardcoded apps\scoop\current.

Fallback to repo-relative supporting path first; only try the apps path if present.

-if (-not (Get-FormatData ScoopShims)) {
-    Update-FormatData "$PSScriptRoot\..\apps\scoop\current\supporting\formats\ScoopTypes.Format.ps1xml"
-}
+if (-not (Get-FormatData ScoopShims)) {
+    $candidates = @(
+        "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml",
+        "$PSScriptRoot\..\apps\scoop\current\supporting\formats\ScoopTypes.Format.ps1xml"
+    )
+    foreach ($p in $candidates) {
+        if (Test-Path -LiteralPath $p) { Update-FormatData $p; break }
+    }
+}
🤖 Prompt for AI Agents
In libexec/scoop-shim.ps1 around lines 119 to 121, the format data path is
hardcoded to ..\apps\scoop\current\supporting\formats\ScoopTypes.Format.ps1xml;
change it to first try a repo-relative supporting path (e.g. resolve
"$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml" or similar
relative-to-repo location) and if that file doesn't exist, fall back to the apps
path only if Test-Path confirms it exists; construct both candidate paths with
Join-Path/Resolve-Path based on $PSScriptRoot, check existence with Test-Path,
and call Update-FormatData with the first existing path (or skip/emit a warning
if neither exists).


$localShimDir = shimdir $false
Expand All @@ -77,6 +135,8 @@ function Get-ShimInfo($ShimPath) {
}
$info.IsGlobal = $ShimPath.StartsWith("$globalShimDir")
$info.IsHidden = !((Get-Command -Name $info.Name).Path -eq $info.Path)
# Add "Added" field
$info.IsAdded = Test-ShimAdded $info.Name $info.IsGlobal
[PSCustomObject]$info
}

Expand All @@ -95,7 +155,7 @@ switch ($SubCommand) {
error "<command_path> must be specified for subcommand 'add'"
my_usage
exit 1
}
}
$shimName = $other[0]
$commandPath = $other[1]
if ($other.Length -gt 2) {
Expand All @@ -108,14 +168,16 @@ switch ($SubCommand) {
$exCommand = Get-Command $shortPath -ErrorAction SilentlyContinue
if ($exCommand -and $exCommand.CommandType -eq 'Application') {
$commandPath = $exCommand.Path
} # TODO - add support for more command types: Alias, Cmdlet, ExternalScript, Filter, Function, Script, and Workflow
}
}
}
if ($commandPath -and (Test-Path $commandPath)) {
Write-Host "Adding $(if ($global) { 'global' } else { 'local' }) shim " -NoNewline
Write-Host $shimName -ForegroundColor Cyan -NoNewline
Write-Host '...'
shim $commandPath $global $shimName $commandArgs
# Save shim to config
Add-ShimToConfig $shimName $global $commandPath $commandArgs
} else {
Write-Host "ERROR: Command path does not exist: " -ForegroundColor Red -NoNewline
Write-Host $($other[1]) -ForegroundColor Cyan
Expand All @@ -127,6 +189,8 @@ switch ($SubCommand) {
$other | ForEach-Object {
if (Get-ShimPath $_ $global) {
rm_shim $_ (shimdir $global)
# Remove shim from config
Remove-ShimFromConfig $_ $global
} else {
$failed += $_
}
Expand All @@ -140,9 +204,11 @@ switch ($SubCommand) {
}
}
'list' {
$other = @($other) -ne '*'
# Validate all given patterns before matching.
$other | ForEach-Object {
# Handle pattern matching
$patterns = @($other) -ne '*'

# Validate regex patterns
$patterns | ForEach-Object {
try {
$pattern = $_
[void][Regex]::New($pattern)
Expand All @@ -152,19 +218,48 @@ switch ($SubCommand) {
exit 1
}
}
$pattern = $other -join '|'
$shims = @()
if (!$global) {
$shims += Get-ChildItem -Path $localShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$pattern -or ($_.BaseName -match $pattern) } |
Select-Object -ExpandProperty FullName
}
if (Test-Path $globalShimDir) {
$shims += Get-ChildItem -Path $globalShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$pattern -or ($_.BaseName -match $pattern) } |
Select-Object -ExpandProperty FullName

$pattern = $patterns -join '|'
$shimInfos = @()

if ($showOnlyAdded) {
# When --added option is used, read from config directly
$added = Get-AddedShimsConfig

# Iterate over each shim in config
$added.PSObject.Properties | ForEach-Object {
$shimName = $_.Name
$shimConfig = $_.Value

# Apply regex filter
if (!$pattern -or ($shimName -match $pattern)) {
# Determine shim path based on global flag
$shimPath = Get-ShimPath $shimName $shimConfig.global

if ($shimPath) {
$shimInfos += Get-ShimInfo $shimPath
}
}
}
} else {
Comment on lines +225 to +244
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Honor -g with --added; current code lists both scopes.

Mirror the non---added behavior: without -g, show both; with -g, show only global.

-                # Apply regex filter
-                if (!$pattern -or ($shimName -match $pattern)) {
-                    # Determine shim path based on global flag
-                    $shimPath = Get-ShimPath $shimName $shimConfig.global
-                    if ($shimPath) {
-                        $shimInfos += Get-ShimInfo $shimPath
-                    }
-                }
+                # Apply regex filter and optional -g scope filter
+                if ((-not $pattern -or ($shimName -match $pattern)) -and ((-not $global) -or $shimConfig.global)) {
+                    $shimPath = Get-ShimPath $shimName $shimConfig.global
+                    if ($shimPath) { $shimInfos += Get-ShimInfo $shimPath }
+                }
🤖 Prompt for AI Agents
In libexec/scoop-shim.ps1 around lines 225 to 244, the --added branch currently
ignores the -g/$global switch and always uses the shim's configured scope;
adjust the loop to honor -g by filtering added entries: if $global is set only
include shims whose $shimConfig.global is true, otherwise include all added
shims (no scope filter). Implement this by checking the $global switch inside
the ForEach-Object and skipping entries that are not global when $global is
present, then continue to call Get-ShimPath/Get-ShimInfo as before.

# Original logic: scan file system
$shims = @()

if (!$global) {
$shims += Get-ChildItem -Path $localShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$pattern -or ($_.BaseName -match $pattern) } |
Select-Object -ExpandProperty FullName
}
if (Test-Path $globalShimDir) {
$shims += Get-ChildItem -Path $globalShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$pattern -or ($_.BaseName -match $pattern) } |
Select-Object -ExpandProperty FullName
}

$shimInfos = $shims.ForEach({ Get-ShimInfo $_ })
}
$shims.ForEach({ Get-ShimInfo $_ }) | Add-Member -TypeName 'ScoopShims' -PassThru

$shimInfos | Format-Table -Property * -AutoSize
}
'info' {
$shimName = $other[0]
Expand Down Expand Up @@ -232,7 +327,7 @@ switch ($SubCommand) {
Write-Host "run 'scoop shim alter $shimName$(if (!$global) { ' --global' })' to alternate its source"
exit 2
}
exit 3
exit
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Return a consistent non-zero exit code on error path.

Use exit 3 (as above) instead of bare exit.

-            exit
+            exit 3
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exit
exit 3
🤖 Prompt for AI Agents
In libexec/scoop-shim.ps1 around line 330, the error path uses a bare "exit"
which returns a zero/ambiguous status; change it to "exit 3" to return a
consistent non-zero exit code (matching other error paths). Replace the bare
exit statement with exit 3 so callers can reliably detect failure.

}
}
}
Expand Down