Skip to content

Conversation

@gigberg
Copy link

@gigberg gigberg commented Sep 30, 2025

  • scoop shim add/rm now remembers manually added shims in the config file.
  • scoop shim list adds a --added option to show only manually added shims; output uses Format-Table.
  • scoop shim info and scoop shim list display an isAdded field to indicate if a shim was manually added.
  • scoop shim alter now supports paths to non-scoop-installed executables.

Description

Motivation and Context

Relates to #6503

How Has This Been Tested?

Checklist:

  • I have read the Contributing Guide.
  • I have ensured that I am targeting the develop branch.
  • I have updated the documentation accordingly.
  • I have updated the tests accordingly.
  • I have added an entry in the CHANGELOG.

Summary by CodeRabbit

  • New Features

    • Persist manually added shims and keep them in sync when adding/removing.
    • New option to list only manually added shims.
    • Shim listing now indicates whether a shim was manually added.
  • Bug Fixes

    • Improved app name detection with a fallback when expected directory patterns don’t match.
  • Chores

    • Added .vscode and *.sln to .gitignore.

- `scoop shim add/rm` now remembers manually added shims in the config file.
- `scoop shim list` adds a `--added` option to show only manually added shims; output uses `Format-Table`.
- `scoop shim info` and `scoop shim list` display an `isAdded` field to indicate if a shim was manually added.
- `scoop shim alter` now supports paths to non-scoop-installed executables.
@coderabbitai
Copy link

coderabbitai bot commented Sep 30, 2025

Walkthrough

Adds .vscode/* and *.sln to .gitignore; updates lib/core.ps1 get_app_name with a regex fallback and diagnostic Write-Host outputs; extends libexec/scoop-shim.ps1 to persist manually added shims in a config, adds helper functions, augments listing (including --added), and integrates persistence into add/rm flows.

Changes

Cohort / File(s) Summary
Ignore rules
\.gitignore
Add rules to ignore the .vscode directory and Visual Studio solution files (*.sln).
Core name extraction
lib/core.ps1
Add regex fallback to derive appName from path when appsdir patterns fail; add diagnostic Write-Host outputs; preserve existing branches and behavior.
Shim persistence and CLI flow
libexec/scoop-shim.ps1
Add config-backed persistence for added shims with new helpers (Get-AddedShimsConfig, Add-ShimToConfig, Remove-ShimFromConfig, Test-ShimAdded); augment Get-ShimInfo with IsAdded; add --added listing mode; persist added shims on add and remove from config on rm; adjust option parsing, messages, and help routing.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as scoop-shim.ps1
  participant FS as Filesystem
  participant Cfg as AddedShims Config

  rect rgb(235,245,255)
  note over CLI: Add subcommand
  User->>CLI: shim add <name> <path> [--global] [args]
  CLI->>FS: Create shim at local/global path
  FS-->>CLI: Shim created
  CLI->>Cfg: Add-ShimToConfig(name, global, path, args)
  Cfg-->>CLI: Persisted
  CLI-->>User: Added shim (local/global)
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant CLI as scoop-shim.ps1
  participant FS as Filesystem
  participant Cfg as AddedShims Config

  rect rgb(245,255,235)
  note over CLI: List subcommand
  User->>CLI: shim list [--added] [--global]
  alt --added
    CLI->>Cfg: Get-AddedShimsConfig()
    Cfg-->>CLI: Entries
    CLI->>CLI: Compute shim paths by global flag
  else filesystem scan
    CLI->>FS: Enumerate shims (local/global)
    FS-->>CLI: Shim paths
    CLI->>Cfg: Test-ShimAdded per shim
    Cfg-->>CLI: IsAdded flags
  end
  CLI-->>User: Table of shims (+IsAdded)
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant CLI as scoop-shim.ps1
  participant FS as Filesystem
  participant Cfg as AddedShims Config

  rect rgb(255,245,235)
  note over CLI: Remove subcommand
  User->>CLI: shim rm <name> [--global]
  CLI->>FS: Delete shim at local/global path
  FS-->>CLI: Deleted/NotFound
  CLI->>Cfg: Remove-ShimFromConfig(name, global)
  Cfg-->>CLI: Removed/No-op
  CLI-->>User: Removal result
  end
Loading
sequenceDiagram
  autonumber
  actor Caller
  participant Core as lib/core.ps1

  Caller->>Core: get_app_name(path)
  alt matches appsdir pattern
    Core-->>Caller: appName (matched)
  else matches appsdir true pattern
    Core-->>Caller: appName (matched)
  else fallback
    Core->>Core: Regex extract filename, toLower()
    Core-->>Caller: appName (derived or empty)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I tap my paw on keys so trim,
New shims remember where they’ve been,
A name from paths, now found and clear,
Config burrows hold them near,
.vscode swept away — I hop with cheer. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the primary change to the shim system by indicating a new feature for managing local executable shims, matching the PR’s focus on enhancing shim add/rm/list/info behavior.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (7)
libexec/scoop-shim.ps1 (7)

46-53: Consider error handling for config operations.

Get-AddedShimsConfig calls get_config which may fail or return unexpected data. Consider adding error handling to ensure the function always returns a valid object.

 function Get-AddedShimsConfig {
-    $added = get_config 'added'
-    if ($null -eq $added) {
-        return [PSCustomObject]@{}
+    try {
+        $added = get_config 'added'
+        if ($null -eq $added -or $added -isnot [PSCustomObject]) {
+            return [PSCustomObject]@{}
+        }
+        return $added
+    } catch {
+        warn "Failed to read added shims config: $_"
+        return [PSCustomObject]@{}
     }
-    return $added
 }

56-75: Validate parameters and handle edge cases.

The Add-ShimToConfig function should validate inputs and handle potential failures:

  1. Empty or null $shimName could cause issues
  2. $commandArgs might be $null when joining
  3. No error handling if set_config fails
 function Add-ShimToConfig($shimName, $global, $commandPath, $commandArgs) {
+    if ([string]::IsNullOrWhiteSpace($shimName)) {
+        throw "shimName cannot be empty"
+    }
+    
     $added = Get-AddedShimsConfig
 
     # Use new structure: shimName as key, global as a boolean field
     $shimInfo = [PSCustomObject]@{
         CommandPath = $commandPath
-        CommandArgs = $commandArgs -join ' '
+        CommandArgs = if ($commandArgs) { $commandArgs -join ' ' } else { '' }
         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
+    try {
+        set_config 'added' $added | Out-Null
+    } catch {
+        error "Failed to save shim configuration: $_"
+        throw
+    }
 }

154-158: Fix inconsistent indentation.

Line 158 has incorrect indentation with the closing brace.

             error "<command_path> must be specified for subcommand 'add'"
             my_usage
             exit 1
-            }
+        }

164-172: Fix inconsistent indentation.

Line 171 has incorrect indentation with the closing brace.

                 $exCommand = Get-Command $shortPath -ErrorAction SilentlyContinue
                 if ($exCommand -and $exCommand.CommandType -eq 'Application') {
                     $commandPath = $exCommand.Path
-                }
+                }
             }

206-222: Handle edge case: empty pattern array.

If $other is empty, $patterns becomes an empty array after filtering out '*'. The code should handle this case explicitly to avoid unexpected behavior.

-        # Handle pattern matching
-        $patterns = @($other) -ne '*'
+        # Handle pattern matching: if no patterns provided, match all
+        $patterns = @($other) | Where-Object { $_ -ne '*' }
+        $hasPatterns = $patterns.Count -gt 0
 
         # Validate regex patterns
         $patterns | ForEach-Object {
             try {
                 $pattern = $_
                 [void][Regex]::New($pattern)
             } catch {
                 Write-Host "ERROR: Invalid pattern: " -ForegroundColor Red -NoNewline
                 Write-Host $pattern -ForegroundColor Magenta
                 exit 1
             }
         }
 
-        $pattern = $patterns -join '|'
+        $pattern = if ($hasPatterns) { $patterns -join '|' } else { $null }

225-243: Consider performance impact of config-based listing.

When --added is used, the code iterates through config entries and calls Get-ShimPath for each one. If a shim was removed from the filesystem but not from config, it will be silently skipped. Consider notifying the user about orphaned config entries.

             # 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 {
+                        warn "Shim '$shimName' is in config but not found in filesystem"
                     }
                 }
             }

330-330: Inconsistent exit code.

Line 330 uses exit without an exit code, while other error paths use exit 3. This creates inconsistent behavior.

-            exit
+            exit 3
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca0506c and 8b637be.

📒 Files selected for processing (3)
  • .gitignore (1 hunks)
  • lib/core.ps1 (1 hunks)
  • libexec/scoop-shim.ps1 (10 hunks)
🔇 Additional comments (4)
.gitignore (1)

10-10: LGTM!

Adding .vscode/* to .gitignore is a standard practice to avoid committing IDE-specific configuration files.

libexec/scoop-shim.ps1 (2)

175-180: LGTM with minor suggestion.

The add subcommand correctly persists shims to config after creating them. The messaging clearly indicates whether it's a global or local shim.

One small improvement: Consider adding error handling around Add-ShimToConfig in case config persistence fails after the shim is already created.


191-193: LGTM!

The rm subcommand correctly removes shims from both the filesystem and config. The global flag is properly passed through.

lib/core.ps1 (1)

838-845: Critical: Fix syntax errors and remove debug statements.

Multiple critical issues detected:

  1. Syntax Error (Lines 841, 844): PowerShell doesn't support the + operator for string concatenation with Write-Host. The current code Write-Host $path + '1' + $appName will output three separate values, not a concatenated string.

  2. Debug Code in Production: The Write-Host statements appear to be diagnostic/debug output that should not be in production code. These will pollute the console output for all users.

  3. Regex Pattern Concern: The pattern [/\\]([^/\\]+?)(?:\.[^/\\"]+?) uses a non-capturing group (?:...) but doesn't capture the extension, and the comment suggests it should handle filenames with extensions. The pattern may not work as intended for all executable paths.

Apply this diff to fix the syntax errors and remove debug output:

     } 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
     }

If diagnostic output is needed for debugging, use the debug function instead:

     } elseif ($path -match '[/\\]([^/\\]+?)(?:\.[^/\\"]+?)') {
         $appName = $Matches[1].ToLower()
+        debug "Fallback extraction: path=$path, appName=$appName"
     } else {
         $appName = ''
+        debug "No app name extracted: path=$path"
     }

Likely an incorrect or invalid review comment.

Comment on lines 40 to 43
. "$PSScriptRoot\..\apps\scoop\current\lib\getopt.ps1"
. "$PSScriptRoot\..\apps\scoop\current\lib\core.ps1" # for config related ops
. "$PSScriptRoot\..\apps\scoop\current\lib\install.ps1" # for rm_shim
. "$PSScriptRoot\..\apps\scoop\current\lib\system.ps1" # 'Add-Path' (indirectly)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify hardcoded relative paths.

The import paths use hardcoded relative paths ($PSScriptRoot\..\apps\scoop\current\...) that assume a specific directory structure. This will break if:

  • The script is moved to a different location
  • Scoop is installed in a non-standard location
  • The script is invoked from a different context

Run the following script to check if there are standard path resolution patterns used elsewhere in the codebase:


🏁 Script executed:

#!/bin/bash
# Search for how other libexec scripts resolve paths to lib files
rg -n -C3 '^\. .*(lib|supporting)/' libexec/ | head -50

Length of output: 0


🏁 Script executed:

#!/bin/bash
# List all scripts under libexec to identify candidates
find libexec -maxdepth 1 -type f -print

# Search for PSScriptRoot usage to find alternative resolution patterns
rg -n '\$PSScriptRoot' -C2 libexec/

Length of output: 16025


🏁 Script executed:

#!/bin/bash
# Verify if apps/scoop/current/lib exists in the repo
find . -maxdepth 5 -type d -path "./apps/scoop/current/lib"
# If found, list its contents
if [ -d "apps/scoop/current/lib" ]; then
  ls -1 apps/scoop/current/lib
else
  echo "apps/scoop/current/lib not found"
fi

Length of output: 32


Derive the scoop install path dynamically instead of hardcoded relative imports in scoop-shim.ps1. Lines 40–43 import from $PSScriptRoot\..\apps\scoop\current\lib\*.ps1, which assumes a fixed install layout (and diverges from other libexec scripts that load from ..\lib). Refactor to compute the scoop root at runtime (e.g. via an environment variable or config) or clearly document the required directory structure. [lines 40–43]

Comment on lines +89 to +94
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)
}
Copy link

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 -ne 'list' -and $other.Length -eq 0) {
error "<shim_name> must be specified for subcommand '$SubCommand'"
my_usage
my_usages
Copy link

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (2)
libexec/scoop-shim.ps1 (2)

89-94: Critical: typo in parameter name causes undefined variable usage.

$gqlobal vs $global breaks the check; also avoid dotted access for the same reason as above.

-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)
+    $prop = $added.PSObject.Properties[$shimName]
+    return ($prop -and $prop.Value.global -eq $global)
 }

115-115: Call the correct helper: my_usage (singular), not my_usages.

-    my_usages
+    my_usage
🧹 Nitpick comments (3)
libexec/scoop-shim.ps1 (3)

179-181: Persisting args: avoid lossy join when calling shim and saving config.

If $commandArgs is an array, passing it as-is preserves tokenization; the config change above stores the array.

-            shim $commandPath $global $shimName $commandArgs
+            if ($null -ne $commandArgs) {
+                shim $commandPath $global $shimName @($commandArgs)
+            } else {
+                shim $commandPath $global $shimName
+            }

248-257: Guard local scan with Test-Path to avoid exceptions on fresh installs.

$localShimDir may not exist yet.

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

262-262: Consider not forcing Format-Table for scriptability.

Formatting at the cmdlet breaks piping. Prefer emitting objects and letting type data shape the view; add --table later if needed.

-        $shimInfos | Format-Table -Property * -AutoSize
+        $shimInfos
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b637be and 9300e72.

📒 Files selected for processing (2)
  • .gitignore (1 hunks)
  • libexec/scoop-shim.ps1 (10 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🔇 Additional comments (3)
libexec/scoop-shim.ps1 (3)

138-140: IsAdded depends on Test-ShimAdded fix; re-evaluate after correcting property access.

Once Test-ShimAdded is fixed, this is fine. Consider guarding with $null -ne $info.Name if any shim has unexpected names.


192-194: Nice: config removal mirrored in rm flow.

This keeps filesystem and config consistent. Reuse the property-bag-safe Remove-ShimFromConfig after you patch it.


41-41: Import comment is fine; verify core helpers availability for config ops.

Assuming get_config/set_config are from core.ps1, this import is correct. Please confirm no circular import.

Comment on lines +56 to +75
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
}
Copy link

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
}

Comment on lines +77 to +86
# 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
}
}
Copy link

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.

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

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).

Comment on lines +225 to +244
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 {
Copy link

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.

exit 2
}
exit 3
exit
Copy link

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
libexec/scoop-shim.ps1 (2)

174-185: Missing error handling for config persistence.

If Add-ShimToConfig or its internal set_config call fails (line 180), the shim will be created but not tracked in config, leading to inconsistent state. Consider wrapping in try/catch or checking the result.

Consider adding error handling:

         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
+            try {
+                Add-ShimToConfig $shimName $global $commandPath $commandArgs
+            } catch {
+                warn "Failed to save shim to config: $_"
+            }
         } else {

187-204: Missing error handling for config cleanup.

If Remove-ShimFromConfig fails (line 193), the shim will be deleted but remain tracked in config, creating inconsistent state.

Consider adding error handling:

         $other | ForEach-Object {
             if (Get-ShimPath $_ $global) {
                 rm_shim $_ (shimdir $global)
-                # Remove shim from config
-                Remove-ShimFromConfig $_ $global
+                try {
+                    Remove-ShimFromConfig $_ $global
+                } catch {
+                    warn "Failed to remove shim from config: $_"
+                }
             } else {
♻️ Duplicate comments (7)
libexec/scoop-shim.ps1 (7)

119-121: Format data path should handle missing files gracefully.

While the path now uses supporting\formats instead of the hardcoded apps\scoop\current path, there's no fallback or error handling if the file doesn't exist.

Consider adding existence check:

 if (-not (Get-FormatData ScoopShims)) {
-    Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
+    $formatPath = "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
+    if (Test-Path -LiteralPath $formatPath) {
+        Update-FormatData $formatPath
+    }
 }

89-94: CRITICAL: Parameter typo causes undefined variable reference.

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

Apply this diff:

-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)
 }

56-75: Property access will fail for shim names with special characters.

Line 71 uses $added.$shimName which breaks when $shimName contains dots, hyphens, or other special characters. Additionally, line 62 joins args as a string, losing token boundaries.

Apply this diff:

 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
 }

77-86: Property access will fail for shim names with special characters.

Line 82 uses $added.$shimName.global which breaks when $shimName contains dots, hyphens, or other special characters.

Apply this diff:

 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
     }
 }

115-115: Function name error - my_usages does not exist.

Line 115 calls my_usages, but only my_usage (singular) is defined in lib/help.ps1. This will cause a runtime error.

Apply this diff:

-    my_usages
+    my_usage

225-244: The --added branch ignores the -g flag.

When --added is used, the code lists all manually added shims regardless of the -g/--global flag. Users would expect -g --added to show only global added shims.

Apply this diff to honor the -g flag:

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

330-330: Use consistent non-zero exit code.

Line 330 uses bare exit which returns zero/ambiguous status. Other error paths in this file use exit 3.

Apply this diff:

-            exit
+            exit 3
🧹 Nitpick comments (2)
libexec/scoop-shim.ps1 (2)

154-158: Inconsistent indentation on closing brace.

Line 158's closing brace is indented excessively compared to the rest of the code block.

Apply this diff:

         if ($other.Length -lt 2 -or $other[1] -eq '') {
             error "<command_path> must be specified for subcommand 'add'"
             my_usage
             exit 1
-            }
+        }

164-172: Inconsistent indentation on closing brace.

Line 171's closing brace is indented excessively compared to the surrounding code block structure.

Apply this diff:

                 if ($exCommand -and $exCommand.CommandType -eq 'Application') {
                     $commandPath = $exCommand.Path
-                }
+                }
             }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9300e72 and e03f3df.

📒 Files selected for processing (1)
  • libexec/scoop-shim.ps1 (10 hunks)
🔇 Additional comments (1)
libexec/scoop-shim.ps1 (1)

207-220: Good defensive pattern validation.

The early regex validation (lines 211-220) prevents runtime errors during pattern matching and provides clear error messages to users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant