Skip to content

PowerShell scriptlets in trusted workspace require repeated one-off approvals (no “remember/allow” option) #1359

@CaptainEmerson

Description

@CaptainEmerson

Describe the feature or problem you'd like to solve

Copilot CLI repeatedly prompts for one-off approval to run common multi-statement PowerShell commands even within a trusted workspace, with no option to remember or persist that permission.

Proposed solution

Describe the feature or problem you'd like to solve

Copilot CLI frequently proposes PowerShell “scriptlets” (multi-statement commands containing variables, loops, conditionals, scriptblocks, and pipelines). Even when all referenced files are inside a trusted workspace, the approval prompt appears as a one-off:

  • Do you want to run this command?
    1. Yes
    2. No, and tell Copilot what to do differently

There is no option to remember / allow for session / allow within this workspace, so routine analysis and small refactors become very prompt-heavy.

I’m not proposing a specific fix/implementation here—just reporting the behavior and the expectation that there should be some way to avoid repeatedly granting permission for these “same shape” commands within a trusted workspace.

What I expected

When commands only operate on files inside a trusted workspace, I expected Copilot CLI to provide some mechanism to reduce repeated prompts for common/repeated operations (e.g., remember for session/workspace, or another workflow that avoids repeated one-off approvals).

What happened instead

For PowerShell multi-statement “scriptlets”, Copilot CLI repeatedly shows a one-time approval prompt with only Yes/No choices and no apparent way to persist the approval, despite the paths being in a trusted workspace.

Environment

  • GitHub Copilot CLI version: 0.0.406
  • OS: Windows (PowerShell)
  • All paths below refer to files within a trusted workspace

Example prompts or workflows

Examples that trigger one-off approval prompts (paths + values anonymized)

Example 1: Search for terms in a trace file (variable + loop)

# Search reasoning for "1234" or "1,234" or "SomeDomainTerm"
$trace = Get-Content "C:\Users\me\source\experiments\project-alpha\tests\traces\latest_run.json" -Raw
foreach ($term in @("1234", "1,234", "SomeDomainTerm")) {
    $count = ([regex]::Matches($trace, $term)).Count
    Write-Output "$term : $count occurrences"
}

Example 2: Read JSON and print a summary (pipeline + scriptblock)

$trace = Get-Content "C:\Users\me\source\experiments\project-alpha\tests\traces\latest_run.json" |
    ConvertFrom-Json;
$trace.result.issues |
    ForEach-Object { "$($_.severity): $($_.description)" }

Example 3: Update a fixture JSON in place (read + replace + write + verify)

# Update the fixture JSON directly - rename the fields
$fixture = Get-Content "C:\Users\me\source\experiments\project-alpha\tests\e2e\fixtures-json\2023-sample.fixture.json" -Raw
$fixture = $fixture -replace '"OriginalSomeField"', '"SomeField_Original"' -replace '"CorrectSomeField"', '"SomeField_Correct"'
Set-Content "C:\Users\me\source\experiments\project-alpha\tests\e2e\fixtures-json\2023-sample.fixture.json" $fixture

# Verify
Get-Content "C:\Users\me\source\experiments\project-alpha\tests\e2e\fixtures-json\2023-sample.fixture.json"

Example 4: Update two fixtures (variables + pipeline + write + verify)

# Update both fixtures: Correct -> Corrected
$f1 = "C:\Users\me\source\experiments\project-alpha\tests\e2e\fixtures-json\2023-sample-a.fixture.json"
$f2 = "C:\Users\me\source\experiments\project-alpha\tests\e2e\fixtures-json\2023-sample-b.fixture.json"

(Get-Content $f1 -Raw) -replace '"SomeField_Correct"', '"SomeField_Corrected"' | Set-Content $f1
(Get-Content $f2 -Raw) -replace '"SomeField_Correct"', '"SomeField_Corrected"' | Set-Content $f2

# Verify
Select-String -Path $f1,$f2 -Pattern "SomeField" | Select-Object Filename, Line

Example 5: Read-only analysis of trace JSON (nested loops + conditionals)

$trace = Get-Content "C:\Users\me\source\experiments\project-alpha\tests\traces\latest_run.json" -Raw | ConvertFrom-Json

# Check tool calls - what items were retrieved
$trace.turns | ForEach-Object {
    if ($_.tool_calls) {
        $_.tool_calls | ForEach-Object {
            if ($_.function.name -eq "get_item") {
                $args = $_.function.arguments | ConvertFrom-Json
                "Retrieved: $($args.item_name)"
            }
        }
    }
}

Write-Host "`nTotal turns: $($trace.turns.Count)"
Write-Host "Reasoning length: $($trace.reasoning.Length)"

Example 6: Read Copilot tool output from temp file (temp path + string checks)

# The issues text is lowercased in the assertion. Check if "4567" or "1234" (without commas) appears
$output = Get-Content "C:\Users\me\AppData\Local\Temp\copilot-tool-output-1234567890000-abcdef.txt" -Raw

# The model outputs "1,234" and "4,567" with commas
# The test checks for "4567", "1234", "999" without commas
# That's why it fails - comma formatting
Write-Host "Contains '4,567': $($output.Contains('4,567'))"
Write-Host "Contains '1,234': $($output.Contains('1,234'))"
Write-Host "Contains '4567' (no comma): $($output.Contains('4567'))"
Write-Host "Contains '1234' (no comma): $($output.Contains('1234'))"

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions