ShellPilot

0.4.0-preview0011

GitHub Copilot in your PowerShell terminal: device-flow auth, model listing, chat and agentic tool-calling with usage and cost.

Minimum PowerShell version

7.0

This is a prerelease version of ShellPilot.
There is a newer prerelease version of this module available.
See the version list below for details.

Installation Options

Copy and Paste the following command to install this package using PowerShellGet More Info

Install-Module -Name ShellPilot -RequiredVersion 0.4.0-preview0011 -AllowPrerelease

Copy and Paste the following command to install this package using Microsoft.PowerShell.PSResourceGet More Info

Install-PSResource -Name ShellPilot -Version 0.4.0-preview0011 -Prerelease

You can deploy this package directly to Azure Automation. Note that deploying packages with dependencies will deploy all the dependencies to Azure Automation. Learn More

Manually download the .nupkg file to your system's default download location. Note that the file won't be unpacked, and won't include any dependencies. Learn More

Owners

Copyright

(c) raandree. All rights reserved.

Package Details

Author(s)

  • raandree

Tags

GitHubCopilot Copilot AI LLM Chat Agent

Functions

Clear-ShpChat Clear-ShpContext Clear-ShpRedactionPolicy Clear-ShpToolPolicy Clear-ShpUsage Compress-ShpChat ConvertTo-ShpAnnotation ConvertTo-ShpTokenCount Get-ShpChat Get-ShpContext Get-ShpCosineSimilarity Get-ShpCostEstimate Get-ShpDefault Get-ShpMcpServer Get-ShpModel Get-ShpModelName Get-ShpRedactionPolicy Get-ShpTool Get-ShpToolPolicy Get-ShpUsage Initialize-Shp Invoke-Shp Invoke-ShpBatch Register-ShpMcpServer Register-ShpTool Request-ShpEmbedding Resolve-ShpError Select-ShpModel Set-ShpContext Set-ShpRedactionPolicy Set-ShpToolPolicy Start-ShpChat Test-ShpCiReadiness Unregister-ShpMcpServer Unregister-ShpTool

PSEditions

Core

Dependencies

This module has no dependencies.

Release Notes

## [0.4.0-preview0011] - 2026-09-06

### Security

- **A disabled tool can no longer be executed.** `-DisableTerminal`,
 `-DisableFileAccess`, `-DisableBrowsing`, `-DisableUserPrompts` and
 `-DisableTodoList` removed a tool from the set offered to the model, but the
 dispatch switch matched built-in tool names unconditionally — so a model that
 named a disabled tool anyway, from its own priors or from a replayed history,
 had it run. `-DisableTerminal` bounded what was advertised and nothing about
 what executed.

 Dispatch now refuses any built-in that this call did not offer, before the
 tool runs. The refusal reuses the existing tool-policy path: the `tool.call`
 event carries `policy = denied`, the reason names the disabled tool, the call
 appears on `ToolCallsDenied`, and the model receives `{"denied": "..."}` so it
 can choose another route instead of failing the turn. The offered set is
 derived from the assembled tool list rather than re-tested against each
 switch, so a tool added later cannot be offered under one condition and
 dispatched under another.

- **`Register-ShpTool` refuses a built-in tool name.** Dispatch matches built-in
 names before it consults the user tool table, so registering `run_command`,
 `read_file` or any other built-in produced a tool that was advertised to the
 model and then silently ignored while the built-in ran instead — with the
 caller believing it had replaced it. An attached MCP server has always been
 refused a colliding name; a local registration now fails the same way, loudly
 and at registration time. Choose a distinct `-ToolName`.

### Added

- **`glob_files` and `grep_files` let the model search without a shell.**
 `glob_files` finds files by name pattern under a directory; `grep_files`
 searches their contents and returns only the path, line number and matching
 line, leaving `read_file` to read around a hit. Both are offered with the
 other file tools and withdrawn by `-DisableFileAccess`.

 Both are governed by the existing `Read()` rules of `Set-ShpToolPolicy`, and
 that is the point: until now the only way to make the model *find* something
 was `run_command`, so a policy tight enough to be worth setting had to grant
 `Shell(...)` — far more reach than searching needs. `Set-ShpToolPolicy -Rule
 'Read(./**)'` is now enough to let the model locate a file by name or by
 content while `run_command` stays denied.

 Every returned hit is policy-checked, not just the search root, because a
 glob rooted at an allowed directory can still match a path that resolves,
 through a link, to somewhere no rule covers. An excluded hit is counted in
 `excludedByPolicy` rather than dropped silently. Both results are bounded —
 files examined, matches returned, and characters returned — and set
 `truncated` when any cap bites, so the model narrows the pattern instead of
 overflowing the context window.

- **`ConvertTo-ShpAnnotation` surfaces structured findings in CI.** Pipe a
 `ShellPilot.Result` from `Invoke-Shp -JsonSchema`, or any plain finding
 object, into the cmdlet to produce GitHub Actions annotations, Azure DevOps
 `task.logissue` commands, or readable text. `Level`, `Path`, `Line`,
 `Column`, `Title`, and `Message` are matched case-insensitively and can be
 redirected with `-PropertyMap`; an unknown or missing level is always a
 warning. Vendor-specific escaping keeps newlines and delimiters from
 corrupting a workflow command. Output stays on the success stream unless
 `-Emit` writes it to the host, and `-Summary` appends a Markdown table to
 `$env:GITHUB_STEP_SUMMARY` when available.
 See [specs/028-ci-annotations.md](specs/028-ci-annotations.md).

- **`Invoke-Shp -EventStream <path>` writes a headless JSONL event stream.** A
 CI log collector reads lines, not prose: everything the module said about a
 running turn was aimed at a person, so a nineteen-iteration turn that was
 refused twice by the tool policy, retried once on an expired session token
 and then stopped on `-MaxBudgetUSD` left one object saying
 `BudgetExceeded = $true` and nothing about the shape of the failure. The
 stream appends one JSON object per line - `turn.start`, `model.request`,
 `usage`, `reasoning` (one per streamed chunk under `-ShowThinking`),
 `tool.call`, `tool.result`, `todo`, `retry`, `error`, `final` - each carrying
 `schemaVersion`, a monotonic `sequence`, an ISO 8601 UTC `timestamp`, a
 `type` and a flat `data` object. Pass `-` to write the records to the
 Information stream instead of a file. Every line is appended whole, so a run
 killed mid-turn still leaves a file that parses up to its last complete line;
 a later call appending to that valid stream continues the sequence. Every
 string payload goes through the same redaction seam the request body does, so
 a secret a tool printed does not reach the stream verbatim; a `run_command`
 tool-call record names the tool and the policy decision but never the command
 line. The complete streamed reasoning trace is redacted before it is divided
 back into Event records, so an SSE boundary cannot split a secret around the
 redaction seam; partial reasoning is retained before a `retry` or `error`.
 Transient HTTP and network-outage retries from the shared request wrapper are
 recorded with attempt, delay and status data, and an invented `ask_user` call
 in a non-interactive turn records its denied Tool call and terminal error
 before the call stops. `-DisableProgressEvents` no longer switches this off -
 the two sinks are gated independently.
 See [specs/027-headless-event-stream.md](specs/027-headless-event-stream.md).

- **`Invoke-Shp -AsJob` and `Invoke-ShpBatch -AsJob` run a call in the
 background.** Both return a thread job whose `Receive-Job` resolves to the
 same `ShellPilot.Result` / `ShellPilot.BatchResult` objects the synchronous
 call returns - the same process, so nothing is serialised into a
 `Deserialized.*` copy. The job runspace inherits no module state, so the
 session context, session defaults, cached model limits, tool policy,
 redaction policy and registered tools are replayed into it and the module is
 imported by path. `Invoke-Shp -AsJob` is seeded from a snapshot of the
 session conversation and stays stateless from there, because a job that
 finishes at an arbitrary time must not race the caller's next call. The CI
 entitlement gate is still evaluated at the call site, so a refused backend
 fails where you typed it rather than in the background of a green build. An
 event stream is honoured: `-AsJob` does not silently turn it off.
 See [specs/027-headless-event-stream.md](specs/027-headless-event-stream.md).

- **`Invoke-Shp` now redacts secrets before they leave the runner.** A CI job
 feeds the model diffs, build logs and attachments produced by untrusted
 pull-request content, and nothing scrubbed them before now - a leaked token
 in a log became a token sent to a third party. Immediately before each
 round-trip, the prompt, every inlined `-Attachment`, and every tool result
 (`run_command`, `read_file`, `fetch_url`, an MCP tool, a user-defined tool)
 is scanned for six built-in shapes - GitHub tokens, AWS access key ids, PEM
 private-key blocks, JWTs, basic-auth URL credentials, and connection-string
 password fields - and a match is replaced with a stable, named placeholder
 such as `[redacted:github-token]`, never simply deleted. The result reports
 `Redactions`: pattern name and count only, never the matched value.
 `Set-ShpRedactionPolicy` / `Get-ShpRedactionPolicy` / `Clear-ShpRedactionPolicy`
 add custom patterns on top of the built-ins, in the same `Name(Pattern)`
 shape `Set-ShpToolPolicy` already uses, and the custom policy travels to
 every `Invoke-ShpBatch` worker the same way the tool policy does. Redaction
 is on by default; pass `-DisableRedaction` to send a call verbatim. Only the
 model's own reply is exempt - it was generated from input already redacted
 before it was sent, so it cannot reflect a secret it was never shown, and a
 `-JsonSchema` reply still parses onto `ContentObject` exactly as it would
 with redaction off.
 See [specs/026-egress-redaction.md](specs/026-egress-redaction.md).

- **An unattended run is now a supported, deliberate profile rather than an
 accident.** `Invoke-Shp`, `Invoke-ShpBatch` and `Initialize-Shp` take
 `-NonInteractive`, on automatically when `$env:CI` is truthy and overridable
 with `-NonInteractive:$false`. It withdraws `ask_user`, refuses `-Confirm`
 instead of silently answering it yes, and refuses the device-code flow before
 the browser launch and the clipboard write - because a prompt on a runner does
 not fail, it burns the job's whole timeout and then fails for the wrong
 reason. A model that calls `ask_user` anyway ends the turn with
 `ShpNonInteractivePrompt` rather than continuing on an answer nobody gave.
 See [specs/025-ci-profile.md](specs/025-ci-profile.md).

- **In CI, the default Copilot backend is refused unless you opt in.** That
 backend reaches the Copilot endpoints with the public VS Code client id, on
 the token owner's personal entitlement - fine for a shell, a decision for a
 pipeline. Configure an OpenAI-compatible endpoint instead, or set
 `SHELLPILOT_ALLOW_COPILOT_BACKEND_IN_CI`. The error carries the id
 `ShpCopilotBackendInCi` and names both remedies, and it is raised **before**
 the token exchange so nothing is spent proving the point.
 A warning was the obvious alternative and is the wrong shape: nobody reads a
 warning in a green build, which is the whole finding behind `-FailOn`.

- **`$env:SHELLPILOT_API_BASE` and `$env:SHELLPILOT_API_KEY`** are read as
 backend defaults, below an explicit `-ApiBase` and the session context and
 above the built-in Copilot endpoint - so a pipeline points ShellPilot at its
 own endpoint with the variables it already injects.

- **`Test-ShpCiReadiness`** reports the

FileList

Version History

Version Downloads Last updated
0.4.0-previe... 7 9/7/2026
0.4.0-previe... 3 9/7/2026
0.4.0-previe... 3 9/6/2026
0.4.0-previe... (current version) 4 9/6/2026
0.4.0-previe... 7 8/26/2026
0.4.0-previe... 4 8/24/2026
0.4.0-previe... 9 8/19/2026
0.4.0-previe... 5 8/12/2026
0.4.0-previe... 7 8/12/2026
0.4.0-previe... 6 8/11/2026
0.4.0-previe... 3 8/11/2026
0.4.0-previe... 10 8/6/2026
0.4.0-previe... 11 7/28/2026
0.4.0-previe... 3 7/28/2026
0.3.1 87 7/23/2026
0.3.1-previe... 3 7/23/2026
0.3.0-previe... 8 7/12/2026
0.3.0-previe... 7 7/9/2026
0.3.0-previe... 4 7/9/2026
0.3.0-previe... 7 7/9/2026
0.2.1-previe... 8 7/8/2026
0.2.0 37 7/8/2026
0.2.0-previe... 6 7/8/2026
0.2.0-previe... 4 7/8/2026
0.2.0-previe... 31 6/12/2026
Show more