Skip to content

Add PowerShell language model tools and redesign the Command Explorer#5508

Open
andyleejordan wants to merge 6 commits into
mainfrom
andyleejordan/lm-tools-command-explorer
Open

Add PowerShell language model tools and redesign the Command Explorer#5508
andyleejordan wants to merge 6 commits into
mainfrom
andyleejordan/lm-tools-command-explorer

Conversation

@andyleejordan

@andyleejordan andyleejordan commented Jun 9, 2026

Copy link
Copy Markdown
Member

Adds PowerShell language model tools, redesigns the Command Explorer, and renders Show Help in a read-only editor pane. Backed by new LSP requests in PowerShell/PowerShellEditorServices#2298 — that PR must land with this one.

  1. Render Show Help in a read-only editor pane — request help text via powerShell/showHelp and show it in a virtual document; fix the off-word getWordRangeAtPosition bug where an empty selection sent the whole document.
  2. Redesign the Command Explorer around modules and lazy loading — lazy module→command tree, versioned module nodes, getModule hover tooltips (cached), a neutral "Functions & Scripts" bucket with a folder icon, excludeParameters/excludeDefaultFunctions request flags, inline-action gating to viewItem == command, and defaulting the view to visible (with a revertIsObservable guard so the ISE compatibility tests still pass).
  3. Add PowerShell language model tools — four read-only tools (powershell_get_command, powershell_get_help, powershell_get_environment, powershell_expand_alias), each backed by an existing PSES request, contributed in package.json and wired up in extension.ts.
  4. Document the cross-repo dev and test cycle for Copilot.

Drafted by Copilot (Claude Opus 4.8)

andyleejordan and others added 4 commits June 9, 2026 15:22
Show Help previously printed `Get-Help` output into the integrated console and,
when nothing was selected, sent the entire document as the help target. The
latter was a `getWordRangeAtPosition` bug: off a word it returns `undefined`,
and `doc.getText(undefined)` returns the whole document.

Resolve the word at the cursor explicitly (falling back to the selection), send
it to the new `powerShell/showHelp` request, and render the returned text in a
read-only virtual document so help opens in a proper editor pane instead of an
editable untitled buffer.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Command Explorer fetched the full command table up front (names, modules,
and parameter metadata for everything), which took minutes to populate and
showed a flat, hard-to-scan list. Rework it into a lazy, module-grouped tree:

- Top-level nodes are modules (with version), expanded on demand; their
  commands are fetched per module with `excludeParameters` so only names and
  modules cross the wire.
- Module-less commands are collected under a neutral "Functions & Scripts"
  node with a folder icon, and the request sets `excludeDefaultFunctions` so
  PowerShell's default-session plumbing doesn't clutter it.
- Hovering a module shows its metadata via the new `powerShell/getModule`
  request, cached so repeated hovers don't re-fetch.
- The inline Show Help / Insert command actions are gated to `viewItem ==
  command` so they no longer appear on module nodes.

Default the Command Explorer to visible. Because its default now equals the
value the ISE profile sets, the ISE compatibility tests could no longer
observe a revert by inequality; guard those assertions with
`revertIsObservable`, which skips settings whose default already matches the
ISE value.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Register four read-only language model tools that Copilot can call when
working with PowerShell, each backed by an existing PSES request so they
report exactly what the user's session would:

- `powershell_get_command` lists commands, optionally scoped by name and/or
  module, using the now-parameterized `powerShell/getCommand` request with
  `excludeParameters` so large listings stay cheap.
- `powershell_get_help` returns `Get-Help` output via `powerShell/showHelp`.
- `powershell_get_environment` reports the PowerShell version table.
- `powershell_expand_alias` resolves aliases to their underlying commands.

All four are read-only and declare no confirmation requirement. Contribute
them in `package.json` and wire up `LanguageModelToolsFeature` in
`extension.ts`.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Record that `modules/` is a symlink to the sibling PowerShellEditorServices
`module` directory, so building PSES deploys its DLLs straight into the path
the extension and its tests load from — meaning C# changes require rebuilding
PSES by hand, and `npm test` exercises the real Extension Host against the
locally built server. This trips up anyone (including Copilot) who edits PSES
and wonders why the extension didn't pick it up.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Point the `PowerShellEditorServices` checkout at the coupled PR branch
`andyleejordan/lm-tools-command-explorer` so #5508's new language model
tool tests run against the server contract they depend on (name/module
filtering on `getCommand`, `showHelp` returning `helpText`).

Must be reverted before merge — once PSES #2298 lands on `main`, the
default checkout passes.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@andyleejordan

Copy link
Copy Markdown
Member Author

⚠️ Temporary commit e98f196 points this PR's CI at the coupled PSES branch andyleejordan/lm-tools-command-explorer (PR PowerShell/PowerShellEditorServices#2298), since the new language model tool tests depend on server-side changes there (name/module filtering on getCommand, showHelp returning helpText). CI builds PSES from main by default, so those tests fail until #2298 lands.

This commit must be reverted before merge — once #2298 is on PSES main, the default checkout passes.

Drafted by Copilot (Claude Opus 4.8).

`@vscode/test-cli` defaults `--user-data-dir` to `.vscode-test/user-data`
inside the workspace. On CI the workspace is nested three deep
(`.../vscode-powershell/vscode-powershell/vscode-powershell/`), so the VS
Code instance's IPC socket (`<user-data-dir>/<version>-main.sock`) exceeds
macOS's 104-char `AF_UNIX` limit and the editor dies at startup with
`listen EINVAL`, flaking the whole matrix (and cancelling the other OSes
via fail-fast).

Anchor `--user-data-dir` in `os.tmpdir()` so the socket path stays short
on every platform.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@andyleejordan andyleejordan marked this pull request as ready for review June 10, 2026 01:08
@andyleejordan andyleejordan requested a review from a team as a code owner June 10, 2026 01:08
Copilot AI review requested due to automatic review settings June 10, 2026 01:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the PowerShell VS Code extension by adding Language Model (LM) tools backed by existing PSES requests, redesigning the Command Explorer for module-oriented lazy loading, and updating Show Help to render as a read-only virtual document.

Changes:

  • Added four read-only PowerShell LM tools (powershell_get_command, powershell_get_help, powershell_get_environment, powershell_expand_alias) and integration tests.
  • Redesigned Command Explorer to group by module (including version), lazy-load tooltips, and avoid expensive parameter metadata transfer.
  • Updated Show Help to fetch Get-Help output via an LSP request and render it in a read-only editor pane; updated Copilot cross-repo dev/test documentation and test harness configuration.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
test/features/LanguageModelTools.test.ts Adds integration tests validating LM tool registration and basic behavior.
test/features/ISECompatibility.test.ts Skips revert assertions for settings whose defaults equal their ISE values (to keep tests meaningful after default changes).
src/features/ShowHelp.ts Switches Show Help to an LSP request and serves help output via a virtual read-only document.
src/features/LanguageModelTools.ts Implements and registers the new PowerShell LM tools.
src/features/GetCommands.ts Redesigns Command Explorer tree to module→command grouping, adds tooltip requests, and adds new request flags.
src/extension.ts Wires up the LanguageModelTools feature during activation.
package.json Contributes LM tools, gates Command Explorer inline actions to command nodes, and defaults Command Explorer visibility to true.
.vscode-test.mjs Adjusts VS Code test launch args to shorten IPC socket path via --user-data-dir.
.github/workflows/ci-test.yml Temporarily pins PSES checkout ref for coupled PR testing.
.github/copilot-instructions.md Documents the cross-repo PSES + extension dev/test cycle for Copilot contributors.

Comment on lines +175 to +177
private didChangeTreeData: vscode.EventEmitter<
CommandExplorerNode | undefined
> = new vscode.EventEmitter<CommandExplorerNode>();
Comment on lines +171 to +173
// Tooltips are cached by key (command name / module+version) so they survive
// tree rebuilds and repeat hovers don't re-issue the (slow) request.
private readonly commandTooltips = new Map<string, vscode.MarkdownString>();
Comment thread src/features/ShowHelp.ts
Comment on lines +34 to +40
public async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
const commandName = uri.path;
const client = await LanguageClientConsumer.getLanguageClient();
const result = await client.sendRequest(ShowHelpRequestType, {
text: commandName,
});
return result.helpText || `No help found for '${commandName}'.`;
Comment thread .vscode-test.mjs
Comment on lines +16 to +20
// workspace path, and its IPC socket blows past macOS's 104-char
// unix-socket limit, causing a flaky `listen EINVAL`. Anchor it in the
// OS temp dir so the socket path stays short on every platform.
"--user-data-dir",
join(tmpdir(), "vscode-powershell-test"),
Comment on lines 30 to 34
repository: PowerShell/PowerShellEditorServices
# TEMPORARY: test against the coupled PSES PR #2298 until it merges.
# Revert this `ref` before merging (see PR #5508).
ref: andyleejordan/lm-tools-command-explorer
path: PowerShellEditorServices
Comment on lines +28 to +50
public async invoke(
options: vscode.LanguageModelToolInvocationOptions<IGetCommandInput>,
_token: vscode.CancellationToken,
): Promise<vscode.LanguageModelToolResult> {
const name = options.input.name?.trim();
const module = options.input.module?.trim();

if (!name && !module) {
return toToolResult(
"Provide a 'name' and/or 'module' filter to look up PowerShell commands.",
);
}

// Get-Command -Name matches literally, so wrap bare text in wildcards to
// get intuitive "contains" matching while leaving explicit wildcards
// (and module names) untouched.
const namePattern = name && !/[*?[\]]/.test(name) ? `*${name}*` : name;

const client = await LanguageClientConsumer.getLanguageClient();
const matches = await client.sendRequest(GetCommandRequestType, {
name: namePattern,
module,
});
Comment on lines +88 to +99
public async invoke(
options: vscode.LanguageModelToolInvocationOptions<IGetHelpInput>,
_token: vscode.CancellationToken,
): Promise<vscode.LanguageModelToolResult> {
const client = await LanguageClientConsumer.getLanguageClient();
const result = await client.sendRequest(ShowHelpRequestType, {
text: options.input.command,
});
return toToolResult(
result.helpText || `No help found for '${options.input.command}'.`,
);
}
Comment on lines +104 to +110
public async invoke(
_options: vscode.LanguageModelToolInvocationOptions<EmptyInput>,
_token: vscode.CancellationToken,
): Promise<vscode.LanguageModelToolResult> {
const client = await LanguageClientConsumer.getLanguageClient();
const version = await client.sendRequest(PowerShellVersionRequestType);
const output = [
Comment on lines +126 to +135
public async invoke(
options: vscode.LanguageModelToolInvocationOptions<IExpandAliasInput>,
_token: vscode.CancellationToken,
): Promise<vscode.LanguageModelToolResult> {
const client = await LanguageClientConsumer.getLanguageClient();
const result = await client.sendRequest(ExpandAliasRequestType, {
text: options.input.text,
});
return toToolResult(result.text);
}
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.

2 participants