From a8b28e0193194c1232db2ce8cca7128eddf10074 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Olav=20R=C3=B8nnestad=20Birkeland?=
 <6450056+o-l-a-v@users.noreply.github.com>
Date: Wed, 13 Nov 2024 23:41:04 +0100
Subject: [PATCH 01/21] Fix #5086 - Invalid JSON in the snippets file (#5087)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fixed JSON

* Added test for making sure all JSON files are valid JSON

* Run in correct dir

* Simplify title of the new step

Co-authored-by: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>

* Fix path double nesting

Co-authored-by: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>

* Add spaces after commas

Co-authored-by: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>

* Use $PWD instead of '.\' for current dir 🐧

* Only test the snippets JSON file

* Oops, typo

---------

Co-authored-by: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
---
 .github/workflows/ci-test.yml | 13 +++++++++----
 snippets/PowerShell.json      | 33 ++++++++++++++++-----------------
 2 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml
index 1b448c7eb4..8d416d460a 100644
--- a/.github/workflows/ci-test.yml
+++ b/.github/workflows/ci-test.yml
@@ -2,19 +2,19 @@ name: CI Tests
 
 on:
   push:
-    branches: [ main ]
+    branches: [main]
   pull_request:
     # The branches below must be a subset of the branches above
-    branches: [ main ]
+    branches: [main]
   merge_group:
-    types: [ checks_requested ]
+    types: [checks_requested]
 
 jobs:
   ci:
     name: node
     strategy:
       matrix:
-        os: [ windows-latest, macos-latest, ubuntu-latest ]
+        os: [windows-latest, macos-latest, ubuntu-latest]
     runs-on: ${{ matrix.os }}
     env:
       DOTNET_NOLOGO: true
@@ -33,6 +33,11 @@ jobs:
         with:
           path: vscode-powershell
 
+      - name: Validate snippets JSON file
+        shell: pwsh
+        run: $null = ConvertFrom-Json -InputObject (Get-Content -Raw -Path './snippets/PowerShell.json')
+        working-directory: vscode-powershell
+
       - name: Install dotnet
         uses: actions/setup-dotnet@v4
         with:
diff --git a/snippets/PowerShell.json b/snippets/PowerShell.json
index bd478f1ff3..5e25f10286 100644
--- a/snippets/PowerShell.json
+++ b/snippets/PowerShell.json
@@ -162,24 +162,23 @@
     ]
   },
   "Foreach with Progress": {
-		"prefix": "foreach-progress",
+    "prefix": "foreach-progress",
     "description": "Insert a foreach loop with Write-Progress initialized",
-		"body": [
-\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$array.count,4) * 100)",
-
-Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$array.count - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
-		  "\\$i = 1",
-		  "foreach ($${2:item} in $${1:array}) {",
-		  "    \\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
-		  "    Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
-		  "    # Insert Code Here",
-		  "    ${0}",
-		  "    ",
-		  "    \\$i++",
-		  "}",
-		  ""
-		]
-	},
+    "body": [
+      "\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$array.count,4) * 100)",
+      "Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$array.count - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
+      "\\$i = 1",
+      "foreach ($${2:item} in $${1:array}) {",
+      "    \\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
+      "    Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
+      "    # Insert Code Here",
+      "    ${0}",
+      "    ",
+      "    \\$i++",
+      "}",
+      ""
+    ]
+  },
   "ForEach-Object -Parallel": {
     "prefix": "foreach-parallel",
     "description": "[PS 7+] Process multiple objects in parallel using runspaces. This has some limitations compared to a regular ForEach-Object. More: Get-Help ForEach-Object",

From 7126891b9a21a3bf77f9c1b3a5003b51e33b8f09 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Fri, 15 Nov 2024 11:45:07 -0800
Subject: [PATCH 02/21] Update the end-of-support message for PowerShell <7.4

As 7.2 LTS (and 7.3) have reached such.
---
 src/session.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/session.ts b/src/session.ts
index 71164b4564..a5f4314845 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -569,7 +569,7 @@ export class SessionManager implements Middleware {
             void this.setSessionFailedGetPowerShell(`PowerShell v${version} is not supported, please update!`);
         } else if (satisfies(version, ">=5.1.0 <6.0.0")) {
             void this.setSessionFailedGetPowerShell("It looks like you're trying to use Windows PowerShell, which is supported on a best-effort basis. Can you try PowerShell 7?");
-        } else if (satisfies(version, ">=6.0.0 <7.2.0")) {
+        } else if (satisfies(version, ">=6.0.0 <7.4.0")) {
             void this.setSessionFailedGetPowerShell(`PowerShell v${version} has reached end-of-support, please update!`);
         } else {
             shouldUpdate = false;

From 400fd75b96f3c3dea4285c5b621cfaa24508f539 Mon Sep 17 00:00:00 2001
From: Justin Grote <JustinGrote@users.noreply.github.com>
Date: Fri, 15 Nov 2024 14:03:59 -0800
Subject: [PATCH 03/21] Implement LogOutputWindow for Logging (#5065)

* Implement LogOutputChannel and move settings to UI

* Remove unnecessary comment

* Remove File Logging (done by LogOutputWindow automatically)

* First Connect

* Add output adapters, LSP restart settings

* Fix Log Uri Test

* Forgot to add to extension facing API

* Accidentally made a recursive rather than reference what I wanted to. Thanks Copilot...

* Pre-Restart Experiments

* Move Commands out of logger temporarily

* Initial Cleanup of Logging, looks good ATM

* Merge client and server editorservices logs

* Add new MergedOutputChannel log

* Remove unnecessary Import

* Update settings for new EditorServicesLogLevels

* Wire up loglevels in-band due to LSP bug

* Rework multiple classes into a parser function injection

* Fix some glyphs

* Revert extra config settings for dynamic log configuration for now

* Remove SetLSPTrace for now

* Clean import

* Align logging terminology to vscode output windows and remove editorServices from options definitions
---
 docs/troubleshooting.md          |   4 +-
 package.json                     |  46 ++---
 src/extension.ts                 |  22 ++-
 src/features/DebugSession.ts     |  45 ++---
 src/features/ExternalApi.ts      |  13 +-
 src/features/UpdatePowerShell.ts |  24 +--
 src/logging.ts                   | 292 +++++++++++++++++++------------
 src/process.ts                   |  13 +-
 src/session.ts                   | 103 +++++++----
 src/settings.ts                  |  97 ++++++++--
 test/core/paths.test.ts          |   4 +-
 test/utils.ts                    |   4 +-
 12 files changed, 422 insertions(+), 245 deletions(-)

diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 3c341a6e19..c6ccb7dbdc 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -253,11 +253,11 @@ Logs provide context for what was happening when the issue occurred. **You shoul
 your logs for any sensitive information you would not like to share online!**
 
 * Before sending through logs, try and reproduce the issue with **log level set to
-  Diagnostic**. You can set this in the [VS Code Settings][]
+  Trace**. You can set this in the [VS Code Settings][]
   (<kbd>Ctrl</kbd>+<kbd>,</kbd>) with:
 
   ```json
-  "powershell.developer.editorServicesLogLevel": "Diagnostic"
+  "powershell.developer.editorServicesLogLevel": "Trace"
   ```
 
 * After you have captured the issue with the log level turned up, you may want to return
diff --git a/package.json b/package.json
index 55b397dab6..73687b5fae 100644
--- a/package.json
+++ b/package.json
@@ -916,24 +916,24 @@
         },
         "powershell.developer.editorServicesLogLevel": {
           "type": "string",
-          "default": "Normal",
+          "default": "Warning",
           "enum": [
-            "Diagnostic",
-            "Verbose",
-            "Normal",
+            "Trace",
+            "Debug",
+            "Information",
             "Warning",
             "Error",
             "None"
           ],
           "markdownEnumDescriptions": [
             "Enables all logging possible, please use this setting when submitting logs for bug reports!",
-            "Enables more logging than normal.",
-            "The default logging level.",
-            "Only log warnings and errors.",
+            "Enables more detailed logging of the extension",
+            "Logs high-level information about what the extension is doing.",
+            "Only log warnings and errors. This is the default setting",
             "Only log errors.",
             "Disable all logging possible. No log files will be written!"
           ],
-          "markdownDescription": "Sets the log verbosity for both the extension and its LSP server, PowerShell Editor Services. **Please set to `Diagnostic` when recording logs for a bug report!**"
+          "markdownDescription": "Sets the log verbosity for both the extension and its LSP server, PowerShell Editor Services. **Please set to `Trace` when recording logs for a bug report!**"
         },
         "powershell.developer.editorServicesWaitForDebugger": {
           "type": "boolean",
@@ -953,6 +953,21 @@
           "default": [],
           "markdownDescription": "An array of strings that enable experimental features in the PowerShell extension. **No flags are currently available!**"
         },
+        "powershell.developer.traceDap": {
+          "type": "boolean",
+          "default": false,
+          "markdownDescription": "Traces the DAP communication between VS Code and the PowerShell Editor Services [DAP Server](https://microsoft.github.io/debug-adapter-protocol/). The output will be logged and also visible in the Output pane, where the verbosity is configurable. **For extension developers and issue troubleshooting only!**"
+        },
+        "powershell.trace.server": {
+          "type": "string",
+          "enum": [
+            "off",
+            "messages",
+            "verbose"
+          ],
+          "default": "off",
+          "markdownDescription": "Traces the communication between VS Code and the PowerShell Editor Services [LSP Server](https://microsoft.github.io/language-server-protocol/). The output will be logged and also visible in the Output pane, where the verbosity is configurable. **For extension developers and issue troubleshooting only!**"
+        },
         "powershell.developer.waitForSessionFileTimeoutSeconds": {
           "type": "number",
           "default": 240,
@@ -1002,21 +1017,6 @@
           "type": "boolean",
           "default": false,
           "markdownDescription": "Show buttons in the editor's title bar for moving the terminals pane (with the PowerShell Extension Terminal) around."
-        },
-        "powershell.trace.server": {
-          "type": "string",
-          "enum": [
-            "off",
-            "messages",
-            "verbose"
-          ],
-          "default": "off",
-          "markdownDescription": "Traces the communication between VS Code and the PowerShell Editor Services [LSP Server](https://microsoft.github.io/language-server-protocol/). **only for extension developers and issue troubleshooting!**"
-        },
-        "powershell.trace.dap": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Traces the communication between VS Code and the PowerShell Editor Services [DAP Server](https://microsoft.github.io/debug-adapter-protocol/). **This setting is only meant for extension developers and issue troubleshooting!**"
         }
       }
     },
diff --git a/src/extension.ts b/src/extension.ts
index 0cb8f3f52e..8676724ab1 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -22,7 +22,7 @@ import { ShowHelpFeature } from "./features/ShowHelp";
 import { SpecifyScriptArgsFeature } from "./features/DebugSession";
 import { Logger } from "./logging";
 import { SessionManager } from "./session";
-import { LogLevel, getSettings } from "./settings";
+import { getSettings } from "./settings";
 import { PowerShellLanguageId } from "./utils";
 import { LanguageClientConsumer } from "./languageClientConsumer";
 
@@ -43,14 +43,12 @@ const documentSelector: DocumentSelector = [
 ];
 
 export async function activate(context: vscode.ExtensionContext): Promise<IPowerShellExtensionClient> {
-    const logLevel = vscode.workspace.getConfiguration(`${PowerShellLanguageId}.developer`)
-        .get<string>("editorServicesLogLevel", LogLevel.Normal);
-    logger = new Logger(logLevel, context.globalStorageUri);
+    logger = new Logger();
 
     telemetryReporter = new TelemetryReporter(TELEMETRY_KEY);
 
     const settings = getSettings();
-    logger.writeVerbose(`Loaded settings:\n${JSON.stringify(settings, undefined, 2)}`);
+    logger.writeDebug(`Loaded settings:\n${JSON.stringify(settings, undefined, 2)}`);
 
     languageConfigurationDisposable = vscode.languages.setLanguageConfiguration(
         PowerShellLanguageId,
@@ -141,6 +139,19 @@ export async function activate(context: vscode.ExtensionContext): Promise<IPower
         new PesterTestsFeature(sessionManager, logger),
         new CodeActionsFeature(logger),
         new SpecifyScriptArgsFeature(context),
+
+        vscode.commands.registerCommand(
+            "PowerShell.OpenLogFolder",
+            async () => {await vscode.commands.executeCommand(
+                "vscode.openFolder",
+                context.logUri,
+                { forceNewWindow: true }
+            );}
+        ),
+        vscode.commands.registerCommand(
+            "PowerShell.ShowLogs",
+            () => {logger.showLogPanel();}
+        )
     ];
 
     const externalApi = new ExternalApiFeature(context, sessionManager, logger);
@@ -169,6 +180,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<IPower
         getPowerShellVersionDetails: uuid => externalApi.getPowerShellVersionDetails(uuid),
         waitUntilStarted: uuid => externalApi.waitUntilStarted(uuid),
         getStorageUri: () => externalApi.getStorageUri(),
+        getLogUri: () => externalApi.getLogUri(),
     };
 }
 
diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts
index f9e4feae07..4af8c83b89 100644
--- a/src/features/DebugSession.ts
+++ b/src/features/DebugSession.ts
@@ -335,8 +335,8 @@ export class DebugSessionFeature extends LanguageClientConsumer
         // Create or show the debug terminal (either temporary or session).
         this.sessionManager.showDebugTerminal(true);
 
-        this.logger.writeVerbose(`Connecting to pipe: ${sessionDetails.debugServicePipeName}`);
-        this.logger.writeVerbose(`Debug configuration: ${JSON.stringify(session.configuration, undefined, 2)}`);
+        this.logger.writeDebug(`Connecting to pipe: ${sessionDetails.debugServicePipeName}`);
+        this.logger.writeDebug(`Debug configuration: ${JSON.stringify(session.configuration, undefined, 2)}`);
 
         return new DebugAdapterNamedPipeServer(sessionDetails.debugServicePipeName);
     }
@@ -424,7 +424,7 @@ export class DebugSessionFeature extends LanguageClientConsumer
                 // The dispose shorthand demonry for making an event one-time courtesy of: https://github.com/OmniSharp/omnisharp-vscode/blob/b8b07bb12557b4400198895f82a94895cb90c461/test/integrationTests/launchConfiguration.integration.test.ts#L41-L45
                 startDebugEvent.dispose();
 
-                this.logger.writeVerbose(`Debugger session detected: ${dotnetAttachSession.name} (${dotnetAttachSession.id})`);
+                this.logger.writeDebug(`Debugger session detected: ${dotnetAttachSession.name} (${dotnetAttachSession.id})`);
 
                 tempConsoleDotnetAttachSession = dotnetAttachSession;
 
@@ -434,7 +434,7 @@ export class DebugSessionFeature extends LanguageClientConsumer
                     // Makes the event one-time
                     stopDebugEvent.dispose();
 
-                    this.logger.writeVerbose(`Debugger session terminated: ${tempConsoleSession.name} (${tempConsoleSession.id})`);
+                    this.logger.writeDebug(`Debugger session terminated: ${tempConsoleSession.name} (${tempConsoleSession.id})`);
 
                     // HACK: As of 2023-08-17, there is no vscode debug API to request the C# debugger to detach, so we send it a custom DAP request instead.
                     const disconnectRequest: DebugProtocol.DisconnectRequest = {
@@ -462,8 +462,8 @@ export class DebugSessionFeature extends LanguageClientConsumer
             // Start a child debug session to attach the dotnet debugger
             // TODO: Accommodate multi-folder workspaces if the C# code is in a different workspace folder
             await debug.startDebugging(undefined, dotnetAttachConfig, session);
-            this.logger.writeVerbose(`Dotnet attach debug configuration: ${JSON.stringify(dotnetAttachConfig, undefined, 2)}`);
-            this.logger.writeVerbose(`Attached dotnet debugger to process: ${pid}`);
+            this.logger.writeDebug(`Dotnet attach debug configuration: ${JSON.stringify(dotnetAttachConfig, undefined, 2)}`);
+            this.logger.writeDebug(`Attached dotnet debugger to process: ${pid}`);
         }
 
         return this.tempSessionDetails;
@@ -606,36 +606,27 @@ export class DebugSessionFeature extends LanguageClientConsumer
 
 class PowerShellDebugAdapterTrackerFactory implements DebugAdapterTrackerFactory, Disposable {
     disposables: Disposable[] = [];
-    dapLogEnabled: boolean = workspace.getConfiguration("powershell").get<boolean>("trace.dap") ?? false;
-    constructor(private adapterName = "PowerShell") {
-        this.disposables.push(workspace.onDidChangeConfiguration(change => {
-            if (
-                change.affectsConfiguration("powershell.trace.dap")
-            ) {
-                this.dapLogEnabled = workspace.getConfiguration("powershell").get<boolean>("trace.dap") ?? false;
-                if (this.dapLogEnabled) {
-                    // Trigger the output pane to appear. This gives the user time to position it before starting a debug.
-                    this.log?.show(true);
-                }
-            }
-        }));
-    }
+    constructor(private adapterName = "PowerShell") {}
 
-    /* We want to use a shared output log for separate debug sessions as usually only one is running at a time and we
-    *  dont need an output window for every debug session. We also want to leave it active so user can copy and paste
-    *  even on run end. When user changes the setting and disables it getter will return undefined, which will result
+
+    _log: LogOutputChannel | undefined;
+    /** Lazily creates a {@link LogOutputChannel} for debug tracing, and presents it only when DAP logging is enabled.
+    *
+    * We want to use a shared output log for separate debug sessions as usually only one is running at a time and we
+    * dont need an output window for every debug session. We also want to leave it active so user can copy and paste
+    * even on run end. When user changes the setting and disables it getter will return undefined, which will result
     * in a noop for the logging activities, effectively pausing logging but not disposing the output channel. If the
     * user re-enables, then logging resumes.
     */
-    _log: LogOutputChannel | undefined;
     get log(): LogOutputChannel | undefined {
-        if (this.dapLogEnabled && this._log === undefined) {
-            this._log = window.createOutputChannel(`${this.adapterName} Trace - DAP`, { log: true });
+        if (workspace.getConfiguration("powershell.developer").get<boolean>("traceDap") && this._log === undefined) {
+            this._log = window.createOutputChannel(`${this.adapterName}: Trace DAP`, { log: true });
             this.disposables.push(this._log);
         }
-        return this.dapLogEnabled ? this._log : undefined;
+        return this._log;
     }
 
+    // This tracker effectively implements the logging for the debug adapter to a LogOutputChannel
     createDebugAdapterTracker(session: DebugSession): DebugAdapterTracker {
         const sessionInfo = `${this.adapterName} Debug Session: ${session.name} [${session.id}]`;
         return {
diff --git a/src/features/ExternalApi.ts b/src/features/ExternalApi.ts
index 7943bf8fa6..29e3427f88 100644
--- a/src/features/ExternalApi.ts
+++ b/src/features/ExternalApi.ts
@@ -19,6 +19,7 @@ export interface IPowerShellExtensionClient {
     getPowerShellVersionDetails(uuid: string): Promise<IExternalPowerShellDetails>;
     waitUntilStarted(uuid: string): Promise<void>;
     getStorageUri(): vscode.Uri;
+    getLogUri(): vscode.Uri;
 }
 
 /*
@@ -55,7 +56,7 @@ export class ExternalApiFeature implements IPowerShellExtensionClient {
         string session uuid
     */
     public registerExternalExtension(id: string, apiVersion = "v1"): string {
-        this.logger.writeVerbose(`Registering extension '${id}' for use with API version '${apiVersion}'.`);
+        this.logger.writeDebug(`Registering extension '${id}' for use with API version '${apiVersion}'.`);
 
         // eslint-disable-next-line @typescript-eslint/no-unused-vars
         for (const [_name, externalExtension] of ExternalApiFeature.registeredExternalExtension) {
@@ -96,7 +97,7 @@ export class ExternalApiFeature implements IPowerShellExtensionClient {
         true if it worked, otherwise throws an error.
     */
     public unregisterExternalExtension(uuid = ""): boolean {
-        this.logger.writeVerbose(`Unregistering extension with session UUID: ${uuid}`);
+        this.logger.writeDebug(`Unregistering extension with session UUID: ${uuid}`);
         if (!ExternalApiFeature.registeredExternalExtension.delete(uuid)) {
             throw new Error(`No extension registered with session UUID: ${uuid}`);
         }
@@ -133,7 +134,7 @@ export class ExternalApiFeature implements IPowerShellExtensionClient {
     */
     public async getPowerShellVersionDetails(uuid = ""): Promise<IExternalPowerShellDetails> {
         const extension = this.getRegisteredExtension(uuid);
-        this.logger.writeVerbose(`Extension '${extension.id}' called 'getPowerShellVersionDetails'.`);
+        this.logger.writeDebug(`Extension '${extension.id}' called 'getPowerShellVersionDetails'.`);
 
         await this.sessionManager.waitUntilStarted();
         const versionDetails = this.sessionManager.getPowerShellVersionDetails();
@@ -161,7 +162,7 @@ export class ExternalApiFeature implements IPowerShellExtensionClient {
     */
     public async waitUntilStarted(uuid = ""): Promise<void> {
         const extension = this.getRegisteredExtension(uuid);
-        this.logger.writeVerbose(`Extension '${extension.id}' called 'waitUntilStarted'.`);
+        this.logger.writeDebug(`Extension '${extension.id}' called 'waitUntilStarted'.`);
         await this.sessionManager.waitUntilStarted();
     }
 
@@ -171,6 +172,10 @@ export class ExternalApiFeature implements IPowerShellExtensionClient {
         return this.extensionContext.globalStorageUri.with({ scheme: "file"});
     }
 
+    public getLogUri(): vscode.Uri {
+        return this.extensionContext.logUri.with({ scheme: "file"});
+    }
+
     public dispose(): void {
         // Nothing to dispose.
     }
diff --git a/src/features/UpdatePowerShell.ts b/src/features/UpdatePowerShell.ts
index 01c31eb385..6805272cc8 100644
--- a/src/features/UpdatePowerShell.ts
+++ b/src/features/UpdatePowerShell.ts
@@ -51,20 +51,20 @@ export class UpdatePowerShell {
     private shouldCheckForUpdate(): boolean {
         // Respect user setting.
         if (!this.sessionSettings.promptToUpdatePowerShell) {
-            this.logger.writeVerbose("Setting 'promptToUpdatePowerShell' was false.");
+            this.logger.writeDebug("Setting 'promptToUpdatePowerShell' was false.");
             return false;
         }
 
         // Respect environment configuration.
         if (process.env.POWERSHELL_UPDATECHECK?.toLowerCase() === "off") {
-            this.logger.writeVerbose("Environment variable 'POWERSHELL_UPDATECHECK' was 'Off'.");
+            this.logger.writeDebug("Environment variable 'POWERSHELL_UPDATECHECK' was 'Off'.");
             return false;
         }
 
         // Skip prompting when using Windows PowerShell for now.
         if (this.localVersion.compare("6.0.0") === -1) {
             // TODO: Maybe we should announce PowerShell Core?
-            this.logger.writeVerbose("Not prompting to update Windows PowerShell.");
+            this.logger.writeDebug("Not prompting to update Windows PowerShell.");
             return false;
         }
 
@@ -78,13 +78,13 @@ export class UpdatePowerShell {
 
             // Skip if PowerShell is self-built, that is, this contains a commit hash.
             if (commit.length >= 40) {
-                this.logger.writeVerbose("Not prompting to update development build.");
+                this.logger.writeDebug("Not prompting to update development build.");
                 return false;
             }
 
             // Skip if preview is a daily build.
             if (daily.toLowerCase().startsWith("daily")) {
-                this.logger.writeVerbose("Not prompting to update daily build.");
+                this.logger.writeDebug("Not prompting to update daily build.");
                 return false;
             }
         }
@@ -106,7 +106,7 @@ export class UpdatePowerShell {
         //     "ReleaseTag": "v7.2.7"
         // }
         const data = await response.json();
-        this.logger.writeVerbose(`Received from '${url}':\n${JSON.stringify(data, undefined, 2)}`);
+        this.logger.writeDebug(`Received from '${url}':\n${JSON.stringify(data, undefined, 2)}`);
         return data.ReleaseTag;
     }
 
@@ -115,18 +115,18 @@ export class UpdatePowerShell {
             return undefined;
         }
 
-        this.logger.writeVerbose("Checking for PowerShell update...");
+        this.logger.writeDebug("Checking for PowerShell update...");
         const tags: string[] = [];
         if (process.env.POWERSHELL_UPDATECHECK?.toLowerCase() === "lts") {
             // Only check for update to LTS.
-            this.logger.writeVerbose("Checking for LTS update...");
+            this.logger.writeDebug("Checking for LTS update...");
             const tag = await this.getRemoteVersion(UpdatePowerShell.LTSBuildInfoURL);
             if (tag != undefined) {
                 tags.push(tag);
             }
         } else {
             // Check for update to stable.
-            this.logger.writeVerbose("Checking for stable update...");
+            this.logger.writeDebug("Checking for stable update...");
             const tag = await this.getRemoteVersion(UpdatePowerShell.StableBuildInfoURL);
             if (tag != undefined) {
                 tags.push(tag);
@@ -134,7 +134,7 @@ export class UpdatePowerShell {
 
             // Also check for a preview update.
             if (this.localVersion.prerelease.length > 0) {
-                this.logger.writeVerbose("Checking for preview update...");
+                this.logger.writeDebug("Checking for preview update...");
                 const tag = await this.getRemoteVersion(UpdatePowerShell.PreviewBuildInfoURL);
                 if (tag != undefined) {
                     tags.push(tag);
@@ -181,11 +181,11 @@ export class UpdatePowerShell {
 
         // If the user cancels the notification.
         if (!result) {
-            this.logger.writeVerbose("User canceled PowerShell update prompt.");
+            this.logger.writeDebug("User canceled PowerShell update prompt.");
             return;
         }
 
-        this.logger.writeVerbose(`User said '${UpdatePowerShell.promptOptions[result.id].title}'.`);
+        this.logger.writeDebug(`User said '${UpdatePowerShell.promptOptions[result.id].title}'.`);
 
         switch (result.id) {
         // Yes
diff --git a/src/logging.ts b/src/logging.ts
index a7176c08ef..7ce8d09e16 100644
--- a/src/logging.ts
+++ b/src/logging.ts
@@ -1,30 +1,16 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-import utils = require("./utils");
-import os = require("os");
-import vscode = require("vscode");
-
-// NOTE: This is not a string enum because the order is used for comparison.
-export enum LogLevel {
-    Diagnostic,
-    Verbose,
-    Normal,
-    Warning,
-    Error,
-    None,
-}
+import { LogOutputChannel, LogLevel, window, Event } from "vscode";
 
 /** Interface for logging operations. New features should use this interface for the "type" of logger.
  *  This will allow for easy mocking of the logger during unit tests.
  */
 export interface ILogger {
-    logDirectoryPath: vscode.Uri;
-    updateLogLevel(logLevelName: string): void;
     write(message: string, ...additionalMessages: string[]): void;
     writeAndShowInformation(message: string, ...additionalMessages: string[]): Promise<void>;
-    writeDiagnostic(message: string, ...additionalMessages: string[]): void;
-    writeVerbose(message: string, ...additionalMessages: string[]): void;
+    writeTrace(message: string, ...additionalMessages: string[]): void;
+    writeDebug(message: string, ...additionalMessages: string[]): void;
     writeWarning(message: string, ...additionalMessages: string[]): void;
     writeAndShowWarning(message: string, ...additionalMessages: string[]): Promise<void>;
     writeError(message: string, ...additionalMessages: string[]): void;
@@ -35,47 +21,16 @@ export interface ILogger {
 }
 
 export class Logger implements ILogger {
-    public logDirectoryPath: vscode.Uri; // The folder for all the logs
-    private logLevel: LogLevel;
-    private commands: vscode.Disposable[];
-    private logChannel: vscode.OutputChannel;
-    private logFilePath: vscode.Uri; // The client's logs
-    private logDirectoryCreated = false;
-    private writingLog = false;
-
-    constructor(logLevelName: string, globalStorageUri: vscode.Uri) {
-        this.logLevel = Logger.logLevelNameToValue(logLevelName);
-        this.logChannel = vscode.window.createOutputChannel("PowerShell Extension Logs");
-        // We have to override the scheme because it defaults to
-        // 'vscode-userdata' which breaks UNC paths.
-        this.logDirectoryPath = vscode.Uri.joinPath(
-            globalStorageUri.with({ scheme: "file" }),
-            "logs",
-            `${Math.floor(Date.now() / 1000)}-${vscode.env.sessionId}`);
-        this.logFilePath = vscode.Uri.joinPath(this.logDirectoryPath, "vscode-powershell.log");
-
-        // Early logging of the log paths for debugging.
-        if (LogLevel.Diagnostic >= this.logLevel) {
-            const uriMessage = Logger.timestampMessage(`Log file path: '${this.logFilePath}'`, LogLevel.Verbose);
-            this.logChannel.appendLine(uriMessage);
-        }
-
-        this.commands = [
-            vscode.commands.registerCommand(
-                "PowerShell.ShowLogs",
-                () => { this.showLogPanel(); }),
+    // Log output channel handles all the verbosity management so we don't have to.
+    private logChannel: LogOutputChannel;
+    public get logLevel(): LogLevel { return this.logChannel.logLevel;}
 
-            vscode.commands.registerCommand(
-                "PowerShell.OpenLogFolder",
-                async () => { await this.openLogFolder(); }),
-        ];
+    constructor(logChannel?: LogOutputChannel) {
+        this.logChannel = logChannel ?? window.createOutputChannel("PowerShell", {log: true});
     }
 
     public dispose(): void {
         this.logChannel.dispose();
-        for (const command of this.commands) {
-            command.dispose();
-        }
     }
 
     private writeAtLevel(logLevel: LogLevel, message: string, ...additionalMessages: string[]): void {
@@ -89,24 +44,24 @@ export class Logger implements ILogger {
     }
 
     public write(message: string, ...additionalMessages: string[]): void {
-        this.writeAtLevel(LogLevel.Normal, message, ...additionalMessages);
+        this.writeAtLevel(LogLevel.Info, message, ...additionalMessages);
     }
 
     public async writeAndShowInformation(message: string, ...additionalMessages: string[]): Promise<void> {
         this.write(message, ...additionalMessages);
 
-        const selection = await vscode.window.showInformationMessage(message, "Show Logs", "Okay");
+        const selection = await window.showInformationMessage(message, "Show Logs", "Okay");
         if (selection === "Show Logs") {
             this.showLogPanel();
         }
     }
 
-    public writeDiagnostic(message: string, ...additionalMessages: string[]): void {
-        this.writeAtLevel(LogLevel.Diagnostic, message, ...additionalMessages);
+    public writeTrace(message: string, ...additionalMessages: string[]): void {
+        this.writeAtLevel(LogLevel.Trace, message, ...additionalMessages);
     }
 
-    public writeVerbose(message: string, ...additionalMessages: string[]): void {
-        this.writeAtLevel(LogLevel.Verbose, message, ...additionalMessages);
+    public writeDebug(message: string, ...additionalMessages: string[]): void {
+        this.writeAtLevel(LogLevel.Debug, message, ...additionalMessages);
     }
 
     public writeWarning(message: string, ...additionalMessages: string[]): void {
@@ -116,7 +71,7 @@ export class Logger implements ILogger {
     public async writeAndShowWarning(message: string, ...additionalMessages: string[]): Promise<void> {
         this.writeWarning(message, ...additionalMessages);
 
-        const selection = await vscode.window.showWarningMessage(message, "Show Logs");
+        const selection = await window.showWarningMessage(message, "Show Logs");
         if (selection !== undefined) {
             this.showLogPanel();
         }
@@ -129,7 +84,7 @@ export class Logger implements ILogger {
     public async writeAndShowError(message: string, ...additionalMessages: string[]): Promise<void> {
         this.writeError(message, ...additionalMessages);
 
-        const choice = await vscode.window.showErrorMessage(message, "Show Logs");
+        const choice = await window.showErrorMessage(message, "Show Logs");
         if (choice !== undefined) {
             this.showLogPanel();
         }
@@ -147,7 +102,7 @@ export class Logger implements ILogger {
 
         const actionKeys: string[] = fullActions.map((action) => action.prompt);
 
-        const choice = await vscode.window.showErrorMessage(message, ...actionKeys);
+        const choice = await window.showErrorMessage(message, ...actionKeys);
         if (choice) {
             for (const action of fullActions) {
                 if (choice === action.prompt && action.action !== undefined ) {
@@ -158,70 +113,177 @@ export class Logger implements ILogger {
         }
     }
 
-    // TODO: Make the enum smarter about strings so this goes away.
-    private static logLevelNameToValue(logLevelName: string): LogLevel {
-        switch (logLevelName.trim().toLowerCase()) {
-        case "diagnostic": return LogLevel.Diagnostic;
-        case "verbose": return LogLevel.Verbose;
-        case "normal": return LogLevel.Normal;
-        case "warning": return LogLevel.Warning;
-        case "error": return LogLevel.Error;
-        case "none": return LogLevel.None;
-        default: return LogLevel.Normal;
+    public showLogPanel(): void {
+        this.logChannel.show();
+    }
+
+    private async writeLine(message: string, level: LogLevel = LogLevel.Info): Promise<void> {
+        return new Promise<void>((resolve) => {
+            switch (level) {
+            case LogLevel.Off: break;
+            case LogLevel.Trace: this.logChannel.trace(message); break;
+            case LogLevel.Debug: this.logChannel.debug(message); break;
+            case LogLevel.Info: this.logChannel.info(message); break;
+            case LogLevel.Warning: this.logChannel.warn(message); break;
+            case LogLevel.Error: this.logChannel.error(message); break;
+            default: this.logChannel.appendLine(message); break;
+            }
+            resolve();
+        });
+    }
+}
+
+/** Parses logs received via the legacy OutputChannel to LogOutputChannel with proper severity.
+ *
+ * HACK: This is for legacy compatability and can be removed when https://github.com/microsoft/vscode-languageserver-node/issues/1116 is merged and replaced with a normal LogOutputChannel. We don't use a middleware here because any direct logging calls like client.warn() and server-initiated messages would not be captured by middleware.
+ */
+export class LanguageClientOutputChannelAdapter implements LogOutputChannel {
+    private _channel: LogOutputChannel | undefined;
+    private get channel(): LogOutputChannel {
+        if (!this._channel) {
+            this._channel = window.createOutputChannel(this.channelName, {log: true});
         }
+        return this._channel;
     }
 
-    public updateLogLevel(logLevelName: string): void {
-        this.logLevel = Logger.logLevelNameToValue(logLevelName);
+    /**
+     * Creates an instance of the logging class.
+     *
+     * @param channelName - The name of the output channel.
+     * @param parser - A function that parses a log message and returns a tuple containing the parsed message and its log level, or undefined if the log should be filtered.
+     */
+    constructor(
+        private channelName: string,
+        private parser: (message: string) => [string, LogLevel] | undefined = LanguageClientOutputChannelAdapter.omnisharpLspParser.bind(this)
+    ) {
     }
 
-    private showLogPanel(): void {
-        this.logChannel.show();
+    public appendLine(message: string): void {
+        this.append(message);
     }
 
-    private async openLogFolder(): Promise<void> {
-        if (this.logDirectoryCreated) {
-            // Open the folder in VS Code since there isn't an easy way to
-            // open the folder in the platform's file browser
-            await vscode.commands.executeCommand("vscode.openFolder", this.logDirectoryPath, true);
-        } else {
-            void this.writeAndShowError("Cannot open PowerShell log directory as it does not exist!");
-        }
+    public append(message: string): void {
+        const parseResult = this.parser(message);
+        if (parseResult !== undefined) {this.sendLogMessage(...parseResult);}
     }
 
-    private static timestampMessage(message: string, level: LogLevel): string {
-        const now = new Date();
-        return `${now.toLocaleDateString()} ${now.toLocaleTimeString()} [${LogLevel[level].toUpperCase()}] - ${message}${os.EOL}`;
+    /** Converts from Omnisharp logs since middleware for LogMessage does not currently exist **/
+    public static omnisharpLspParser(message: string): [string, LogLevel] {
+        const logLevelMatch = /^\[(?<level>Trace|Debug|Info|Warn|Error) +- \d+:\d+:\d+ [AP]M\] (?<message>.+)/.exec(message);
+        const logLevel: LogLevel = logLevelMatch?.groups?.level
+            ? LogLevel[logLevelMatch.groups.level as keyof typeof LogLevel]
+            : LogLevel.Info;
+        const logMessage = logLevelMatch?.groups?.message ?? message;
+
+        return [logMessage, logLevel];
     }
 
-    // TODO: Should we await this function above?
-    private async writeLine(message: string, level: LogLevel = LogLevel.Normal): Promise<void> {
-        const timestampedMessage = Logger.timestampMessage(message, level);
-        this.logChannel.appendLine(timestampedMessage);
-        if (this.logLevel !== LogLevel.None) {
-            // A simple lock because this function isn't re-entrant.
-            while (this.writingLog) {
-                await utils.sleep(300);
-            }
-            try {
-                this.writingLog = true;
-                if (!this.logDirectoryCreated) {
-                    this.writeVerbose(`Creating log directory at: '${this.logDirectoryPath}'`);
-                    await vscode.workspace.fs.createDirectory(this.logDirectoryPath);
-                    this.logDirectoryCreated = true;
-                }
-                let log = new Uint8Array();
-                if (await utils.checkIfFileExists(this.logFilePath)) {
-                    log = await vscode.workspace.fs.readFile(this.logFilePath);
-                }
-                await vscode.workspace.fs.writeFile(
-                    this.logFilePath,
-                    Buffer.concat([log, Buffer.from(timestampedMessage)]));
-            } catch (err) {
-                console.log(`Error writing to vscode-powershell log file: ${err}`);
-            } finally {
-                this.writingLog = false;
-            }
+    protected sendLogMessage(message: string, level: LogLevel): void {
+        switch (level) {
+        case LogLevel.Trace:
+            this.channel.trace(message);
+            break;
+        case LogLevel.Debug:
+            this.channel.debug(message);
+            break;
+        case LogLevel.Info:
+            this.channel.info(message);
+            break;
+        case LogLevel.Warning:
+            this.channel.warn(message);
+            break;
+        case LogLevel.Error:
+            this.channel.error(message);
+            break;
+        default:
+            this.channel.error("!UNKNOWN LOG LEVEL!: " + message);
+            break;
         }
     }
+
+    // #region Passthru Implementation
+    public get name(): string {
+        // prevents the window from being created unless we get a log request
+        return this.channelName;
+    }
+    public get logLevel(): LogLevel {
+        return this.channel.logLevel;
+    }
+    replace(value: string): void {
+        this.channel.replace(value);
+    }
+    show(_column?: undefined, preserveFocus?: boolean): void {
+        this.channel.show(preserveFocus);
+    }
+    public get onDidChangeLogLevel(): Event<LogLevel> {
+        return this.channel.onDidChangeLogLevel;
+    }
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    public trace(message: string, ...args: any[]): void {
+        this.channel.trace(message, ...args);
+    }
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    public debug(message: string, ...args: any[]): void {
+        this.channel.debug(message, ...args);
+    }
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    public info(message: string, ...args: any[]): void {
+        this.channel.info(message, ...args);
+    }
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    public warn(message: string, ...args: any[]): void {
+        this.channel.warn(message, ...args);
+    }
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    public error(message: string, ...args: any[]): void {
+        this.channel.error(message, ...args);
+    }
+    public clear(): void {
+        this.channel.clear();
+    }
+    public hide(): void {
+        this.channel.hide();
+    }
+    public dispose(): void {
+        this.channel.dispose();
+    }
+    // #endregion
+}
+
+/** Special parsing for PowerShell Editor Services LSP messages since the LogLevel cannot be read due to vscode
+ * LanguageClient Limitations (https://github.com/microsoft/vscode-languageserver-node/issues/1116)
+  */
+export function PsesParser(message: string): [string, LogLevel] {
+    const logLevelMatch = /^<(?<level>Trace|Debug|Info|Warning|Error)>(?<message>.+)/.exec(message);
+    const logLevel: LogLevel = logLevelMatch?.groups?.level
+        ? LogLevel[logLevelMatch.groups.level as keyof typeof LogLevel]
+        : LogLevel.Info;
+    const logMessage = logLevelMatch?.groups?.message ?? message;
+
+    return ["[PSES] " + logMessage, logLevel];
+}
+
+/** Lsp Trace Parser that does some additional parsing and formatting to make it look nicer */
+export function LspTraceParser(message: string): [string, LogLevel] {
+    let [parsedMessage, level] = LanguageClientOutputChannelAdapter.omnisharpLspParser(message);
+    if (parsedMessage.startsWith("Sending ")) {
+        parsedMessage = parsedMessage.replace("Sending", "➡️");
+        level = LogLevel.Debug;
+    }
+    if (parsedMessage.startsWith("Received ")) {
+        parsedMessage = parsedMessage.replace("Received", "⬅️");
+        level = LogLevel.Debug;
+    }
+    if (parsedMessage.startsWith("Params:")
+        || parsedMessage.startsWith("Result:")
+    ) {
+        level = LogLevel.Trace;
+    }
+
+    // These are PSES messages that get logged to the output channel anyways so we drop these to trace for easy noise filtering
+    if (parsedMessage.startsWith("⬅️ notification 'window/logMessage'")) {
+        level = LogLevel.Trace;
+    }
+
+    return [parsedMessage.trimEnd(), level];
 }
diff --git a/src/process.ts b/src/process.ts
index d363c8e2ee..052da2b8bf 100644
--- a/src/process.ts
+++ b/src/process.ts
@@ -30,6 +30,7 @@ export class PowerShellProcess {
         private isTemp: boolean,
         private shellIntegrationEnabled: boolean,
         private logger: ILogger,
+        private logDirectoryPath: vscode.Uri,
         private startPsesArgs: string,
         private sessionFilePath: vscode.Uri,
         private sessionSettings: Settings) {
@@ -51,7 +52,7 @@ export class PowerShellProcess {
                 : "";
 
         this.startPsesArgs +=
-            `-LogPath '${utils.escapeSingleQuotes(this.logger.logDirectoryPath.fsPath)}' ` +
+            `-LogPath '${utils.escapeSingleQuotes(this.logDirectoryPath.fsPath)}' ` +
             `-SessionDetailsPath '${utils.escapeSingleQuotes(this.sessionFilePath.fsPath)}' ` +
             `-FeatureFlags @(${featureFlags}) `;
 
@@ -89,13 +90,13 @@ export class PowerShellProcess {
                 startEditorServices);
         } else {
             // Otherwise use -EncodedCommand for better quote support.
-            this.logger.writeVerbose("Using Base64 -EncodedCommand but logging as -Command equivalent.");
+            this.logger.writeDebug("Using Base64 -EncodedCommand but logging as -Command equivalent.");
             powerShellArgs.push(
                 "-EncodedCommand",
                 Buffer.from(startEditorServices, "utf16le").toString("base64"));
         }
 
-        this.logger.writeVerbose(`Starting process: ${this.exePath} ${powerShellArgs.slice(0, -2).join(" ")} -Command ${startEditorServices}`);
+        this.logger.writeDebug(`Starting process: ${this.exePath} ${powerShellArgs.slice(0, -2).join(" ")} -Command ${startEditorServices}`);
 
         // Make sure no old session file exists
         await this.deleteSessionFile(this.sessionFilePath);
@@ -173,7 +174,7 @@ export class PowerShellProcess {
     }
 
     public dispose(): void {
-        this.logger.writeVerbose(`Disposing PowerShell process with PID: ${this.pid}`);
+        this.logger.writeDebug(`Disposing PowerShell process with PID: ${this.pid}`);
 
         void this.deleteSessionFile(this.sessionFilePath);
 
@@ -226,7 +227,7 @@ export class PowerShellProcess {
         const warnAt = numOfTries - PowerShellProcess.warnUserThreshold;
 
         // Check every second.
-        this.logger.writeVerbose(`Waiting for session file: ${this.sessionFilePath}`);
+        this.logger.writeDebug(`Waiting for session file: ${this.sessionFilePath}`);
         for (let i = numOfTries; i > 0; i--) {
             if (cancellationToken.isCancellationRequested) {
                 this.logger.writeWarning("Canceled while waiting for session file.");
@@ -239,7 +240,7 @@ export class PowerShellProcess {
             }
 
             if (await utils.checkIfFileExists(this.sessionFilePath)) {
-                this.logger.writeVerbose("Session file found.");
+                this.logger.writeDebug("Session file found.");
                 return await this.readSessionFile(this.sessionFilePath);
             }
 
diff --git a/src/session.ts b/src/session.ts
index a5f4314845..0b1037a116 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -6,7 +6,7 @@ import path = require("path");
 import vscode = require("vscode");
 import TelemetryReporter, { TelemetryEventProperties, TelemetryEventMeasurements } from "@vscode/extension-telemetry";
 import { Message } from "vscode-jsonrpc";
-import { ILogger } from "./logging";
+import { ILogger, LanguageClientOutputChannelAdapter, LspTraceParser, PsesParser } from "./logging";
 import { PowerShellProcess } from "./process";
 import { Settings, changeSetting, getSettings, getEffectiveConfigurationTarget, validateCwdSetting } from "./settings";
 import utils = require("./utils");
@@ -14,7 +14,8 @@ import utils = require("./utils");
 import {
     CloseAction, CloseHandlerResult, DocumentSelector, ErrorAction, ErrorHandlerResult,
     LanguageClientOptions, Middleware, NotificationType,
-    RequestType0, ResolveCodeLensSignature, RevealOutputChannelOn
+    RequestType0, ResolveCodeLensSignature,
+    RevealOutputChannelOn,
 } from "vscode-languageclient";
 import { LanguageClient, StreamInfo } from "vscode-languageclient/node";
 
@@ -93,6 +94,7 @@ export class SessionManager implements Middleware {
     private startCancellationTokenSource: vscode.CancellationTokenSource | undefined;
     private suppressRestartPrompt = false;
     private versionDetails: IPowerShellVersionDetails | undefined;
+    private traceLogLevelHandler?: vscode.Disposable;
 
     constructor(
         private extensionContext: vscode.ExtensionContext,
@@ -104,7 +106,6 @@ export class SessionManager implements Middleware {
         hostVersion: string,
         publisher: string,
         private telemetryReporter: TelemetryReporter) {
-
         // Create the language status item
         this.languageStatusItem = this.createStatusBarItem();
         // We have to override the scheme because it defaults to
@@ -161,7 +162,7 @@ export class SessionManager implements Middleware {
             return;
         case SessionStatus.Running:
             // We're started, just return.
-            this.logger.writeVerbose("Already started.");
+            this.logger.writeDebug("Already started.");
             return;
         case SessionStatus.Busy:
             // We're started but busy so notify and return.
@@ -170,12 +171,12 @@ export class SessionManager implements Middleware {
             return;
         case SessionStatus.Stopping:
             // Wait until done stopping, then start.
-            this.logger.writeVerbose("Still stopping.");
+            this.logger.writeDebug("Still stopping.");
             await this.waitWhileStopping();
             break;
         case SessionStatus.Failed:
             // Try to start again.
-            this.logger.writeVerbose("Previously failed, starting again.");
+            this.logger.writeDebug("Previously failed, starting again.");
             break;
         }
 
@@ -277,6 +278,8 @@ export class SessionManager implements Middleware {
         this.startCancellationTokenSource?.dispose();
         this.startCancellationTokenSource = undefined;
         this.sessionDetails = undefined;
+        this.traceLogLevelHandler?.dispose();
+        this.traceLogLevelHandler = undefined;
 
         this.setSessionStatus("Not Started", SessionStatus.NotStarted);
     }
@@ -291,7 +294,7 @@ export class SessionManager implements Middleware {
         if (exeNameOverride) {
             // Reset the version and PowerShell details since we're launching a
             // new executable.
-            this.logger.writeVerbose(`Starting with executable overriden to: ${exeNameOverride}`);
+            this.logger.writeDebug(`Starting with executable overriden to: ${exeNameOverride}`);
             this.sessionSettings.powerShellDefaultVersion = exeNameOverride;
             this.versionDetails = undefined;
             this.PowerShellExeDetails = undefined;
@@ -335,7 +338,6 @@ export class SessionManager implements Middleware {
         // handler when the process is disposed).
         this.debugSessionProcess?.dispose();
         this.debugEventHandler?.dispose();
-
         if (this.PowerShellExeDetails === undefined) {
             return Promise.reject(new Error("Required PowerShellExeDetails undefined!"));
         }
@@ -353,6 +355,7 @@ export class SessionManager implements Middleware {
                 true,
                 false,
                 this.logger,
+                this.extensionContext.logUri,
                 this.getEditorServicesArgs(bundledModulesPath, this.PowerShellExeDetails) + "-DebugServiceOnly ",
                 this.getNewSessionFilePath(),
                 this.sessionSettings);
@@ -451,34 +454,58 @@ export class SessionManager implements Middleware {
         }
     }
 
-    private async onConfigurationUpdated(): Promise<void> {
+    /** There are some changes we cannot "hot" set, so these require a restart of the session */
+    private async restartOnCriticalConfigChange(changeEvent: vscode.ConfigurationChangeEvent): Promise<void> {
+        if (this.suppressRestartPrompt) {return;}
+        if (this.sessionStatus !== SessionStatus.Running) {return;}
+
+        // Restart not needed if shell integration is enabled but the shell is backgrounded.
         const settings = getSettings();
-        const shellIntegrationEnabled = vscode.workspace.getConfiguration("terminal.integrated.shellIntegration").get<boolean>("enabled");
-        this.logger.updateLogLevel(settings.developer.editorServicesLogLevel);
+        if (changeEvent.affectsConfiguration("terminal.integrated.shellIntegration.enabled")) {
+            const shellIntegrationEnabled = vscode.workspace.getConfiguration("terminal.integrated.shellIntegration").get<boolean>("enabled") ?? false;
+            if (shellIntegrationEnabled && !settings.integratedConsole.startInBackground) {
+                return this.restartWithPrompt();
+            }
+        }
+
+        // Early return if the change doesn't affect the PowerShell extension settings from this point forward
+        if (!changeEvent.affectsConfiguration("powershell")) {return;}
+
 
         // Detect any setting changes that would affect the session.
-        if (!this.suppressRestartPrompt
-            && this.sessionStatus === SessionStatus.Running
-            && ((shellIntegrationEnabled !== this.shellIntegrationEnabled
-                && !settings.integratedConsole.startInBackground)
-            || settings.cwd !== this.sessionSettings.cwd
+        const coldRestartSettingNames = [
+            "developer.traceLsp",
+            "developer.traceDap",
+            "developer.editorServicesLogLevel",
+        ];
+        for (const settingName of coldRestartSettingNames) {
+            if (changeEvent.affectsConfiguration("powershell" + "." + settingName)) {
+                return this.restartWithPrompt();
+            }
+        }
+
+        // TODO: Migrate these to affectsConfiguration style above
+        if (settings.cwd !== this.sessionSettings.cwd
             || settings.powerShellDefaultVersion !== this.sessionSettings.powerShellDefaultVersion
-            || settings.developer.editorServicesLogLevel !== this.sessionSettings.developer.editorServicesLogLevel
             || settings.developer.bundledModulesPath !== this.sessionSettings.developer.bundledModulesPath
             || settings.developer.editorServicesWaitForDebugger !== this.sessionSettings.developer.editorServicesWaitForDebugger
             || settings.developer.setExecutionPolicy !== this.sessionSettings.developer.setExecutionPolicy
             || settings.integratedConsole.useLegacyReadLine !== this.sessionSettings.integratedConsole.useLegacyReadLine
             || settings.integratedConsole.startInBackground !== this.sessionSettings.integratedConsole.startInBackground
-            || settings.integratedConsole.startLocation !== this.sessionSettings.integratedConsole.startLocation)) {
+            || settings.integratedConsole.startLocation !== this.sessionSettings.integratedConsole.startLocation
+        ) {
+            return this.restartWithPrompt();
+        }
+    }
 
-            this.logger.writeVerbose("Settings changed, prompting to restart...");
-            const response = await vscode.window.showInformationMessage(
-                "The PowerShell runtime configuration has changed, would you like to start a new session?",
-                "Yes", "No");
+    private async restartWithPrompt(): Promise<void> {
+        this.logger.writeDebug("Settings changed, prompting to restart...");
+        const response = await vscode.window.showInformationMessage(
+            "The PowerShell runtime configuration has changed, would you like to start a new session?",
+            "Yes", "No");
 
-            if (response === "Yes") {
-                await this.restartSession();
-            }
+        if (response === "Yes") {
+            await this.restartSession();
         }
     }
 
@@ -486,14 +513,14 @@ export class SessionManager implements Middleware {
         this.registeredCommands = [
             vscode.commands.registerCommand("PowerShell.RestartSession", async () => { await this.restartSession(); }),
             vscode.commands.registerCommand(this.ShowSessionMenuCommandName, async () => { await this.showSessionMenu(); }),
-            vscode.workspace.onDidChangeConfiguration(async () => { await this.onConfigurationUpdated(); }),
+            vscode.workspace.onDidChangeConfiguration((e) => this.restartOnCriticalConfigChange(e)),
             vscode.commands.registerCommand(
                 "PowerShell.ShowSessionConsole", (isExecute?: boolean) => { this.showSessionTerminal(isExecute); })
         ];
     }
 
     private async findPowerShell(): Promise<IPowerShellExeDetails | undefined> {
-        this.logger.writeVerbose("Finding PowerShell...");
+        this.logger.writeDebug("Finding PowerShell...");
         const powershellExeFinder = new PowerShellExeFinder(
             this.platformDetails,
             this.sessionSettings.powerShellAdditionalExePaths,
@@ -539,6 +566,7 @@ export class SessionManager implements Middleware {
                 false,
                 this.shellIntegrationEnabled,
                 this.logger,
+                this.extensionContext.logUri,
                 this.getEditorServicesArgs(bundledModulesPath, powerShellExeDetails),
                 this.getNewSessionFilePath(),
                 this.sessionSettings);
@@ -591,7 +619,7 @@ export class SessionManager implements Middleware {
     }
 
     private sessionStarted(sessionDetails: IEditorServicesSessionDetails): boolean {
-        this.logger.writeVerbose(`Session details: ${JSON.stringify(sessionDetails, undefined, 2)}`);
+        this.logger.writeDebug(`Session details: ${JSON.stringify(sessionDetails, undefined, 2)}`);
         if (sessionDetails.status === "started") { // Successful server start with a session file
             return true;
         }
@@ -610,7 +638,7 @@ export class SessionManager implements Middleware {
     }
 
     private async startLanguageClient(sessionDetails: IEditorServicesSessionDetails): Promise<LanguageClient> {
-        this.logger.writeVerbose("Connecting to language service...");
+        this.logger.writeDebug("Connecting to language service...");
         const connectFunc = (): Promise<StreamInfo> => {
             return new Promise<StreamInfo>(
                 (resolve, _reject) => {
@@ -618,11 +646,12 @@ export class SessionManager implements Middleware {
                     socket.on(
                         "connect",
                         () => {
-                            this.logger.writeVerbose("Language service connected.");
+                            this.logger.writeDebug("Language service connected.");
                             resolve({ writer: socket, reader: socket });
                         });
                 });
         };
+
         const clientOptions: LanguageClientOptions = {
             documentSelector: this.documentSelector,
             synchronize: {
@@ -646,9 +675,11 @@ export class SessionManager implements Middleware {
                 // hangs up (ECONNRESET errors).
                 error: (_error: Error, _message: Message, _count: number): ErrorHandlerResult => {
                     // TODO: Is there any error worth terminating on?
+                    this.logger.writeError(`${_error.name}: ${_error.message} ${_error.cause}`);
                     return { action: ErrorAction.Continue };
                 },
                 closed: (): CloseHandlerResult => {
+                    this.logger.write("Language service connection closed.");
                     // We have our own restart experience
                     return {
                         action: CloseAction.DoNotRestart,
@@ -656,9 +687,11 @@ export class SessionManager implements Middleware {
                     };
                 },
             },
-            revealOutputChannelOn: RevealOutputChannelOn.Never,
             middleware: this,
-            traceOutputChannel: vscode.window.createOutputChannel("PowerShell Trace - LSP", {log: true}),
+            traceOutputChannel: new LanguageClientOutputChannelAdapter("PowerShell: Trace LSP", LspTraceParser),
+            // This is named the same as the Client log to merge the logs, but will be handled and disposed separately.
+            outputChannel: new LanguageClientOutputChannelAdapter("PowerShell", PsesParser),
+            revealOutputChannelOn: RevealOutputChannelOn.Never
         };
 
         const languageClient = new LanguageClient("powershell", "PowerShell Editor Services Client", connectFunc, clientOptions);
@@ -763,8 +796,8 @@ Type 'help' to get help.
             && this.extensionContext.extensionMode === vscode.ExtensionMode.Development) {
             editorServicesArgs += "-WaitForDebugger ";
         }
-
-        editorServicesArgs += `-LogLevel '${this.sessionSettings.developer.editorServicesLogLevel}' `;
+        const logLevel = vscode.workspace.getConfiguration("powershell.developer").get<string>("editorServicesLogLevel");
+        editorServicesArgs += `-LogLevel '${logLevel}' `;
 
         return editorServicesArgs;
     }
@@ -836,7 +869,7 @@ Type 'help' to get help.
     }
 
     private setSessionStatus(detail: string, status: SessionStatus): void {
-        this.logger.writeVerbose(`Session status changing from '${this.sessionStatus}' to '${status}'.`);
+        this.logger.writeDebug(`Session status changing from '${this.sessionStatus}' to '${status}'.`);
         this.sessionStatus = status;
         this.languageStatusItem.text = "$(terminal-powershell)";
         this.languageStatusItem.detail = "PowerShell";
diff --git a/src/settings.ts b/src/settings.ts
index 9c2ef38452..f29079846a 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -56,15 +56,6 @@ export enum PipelineIndentationStyle {
     None = "None",
 }
 
-export enum LogLevel {
-    Diagnostic = "Diagnostic",
-    Verbose = "Verbose",
-    Normal = "Normal",
-    Warning = "Warning",
-    Error = "Error",
-    None = "None",
-}
-
 export enum CommentType {
     Disabled = "Disabled",
     BlockComment = "BlockComment",
@@ -120,7 +111,6 @@ class DeveloperSettings extends PartialSettings {
     // From `<root>/out/main.js` we go to the directory before <root> and
     // then into the other repo.
     bundledModulesPath = "../../PowerShellEditorServices/module";
-    editorServicesLogLevel = LogLevel.Normal;
     editorServicesWaitForDebugger = false;
     setExecutionPolicy = true;
     waitForSessionFileTimeoutSeconds = 240;
@@ -209,7 +199,7 @@ export async function changeSetting(
     configurationTarget: vscode.ConfigurationTarget | boolean | undefined,
     logger: ILogger | undefined): Promise<void> {
 
-    logger?.writeVerbose(`Changing '${settingName}' at scope '${configurationTarget}' to '${newValue}'.`);
+    logger?.writeDebug(`Changing '${settingName}' at scope '${configurationTarget}' to '${newValue}'.`);
 
     try {
         const configuration = vscode.workspace.getConfiguration(utils.PowerShellLanguageId);
@@ -242,7 +232,7 @@ export async function getChosenWorkspace(logger: ILogger | undefined): Promise<v
 
         chosenWorkspace = await vscode.window.showWorkspaceFolderPick(options);
 
-        logger?.writeVerbose(`User selected workspace: '${chosenWorkspace?.name}'`);
+        logger?.writeDebug(`User selected workspace: '${chosenWorkspace?.name}'`);
         if (chosenWorkspace === undefined) {
             chosenWorkspace = vscode.workspace.workspaceFolders[0];
         } else {
@@ -296,7 +286,7 @@ export async function validateCwdSetting(logger: ILogger | undefined): Promise<s
     // Otherwise get a cwd from the workspace, if possible.
     const workspace = await getChosenWorkspace(logger);
     if (workspace === undefined) {
-        logger?.writeVerbose("Workspace was undefined, using homedir!");
+        logger?.writeDebug("Workspace was undefined, using homedir!");
         return os.homedir();
     }
 
@@ -316,3 +306,84 @@ export async function validateCwdSetting(logger: ILogger | undefined): Promise<s
     // If all else fails, use the home directory.
     return os.homedir();
 }
+
+
+/**
+ * Options for the `onSettingChange` function.
+ * @param scope the scope in which the vscode setting should be evaluated.
+ * @param run Indicates whether the function should be run now in addition to when settings change, or if it should be run only once and stop listening after a single change. If this is undefined, the function will be run only when the setting changes.
+ */
+interface onSettingChangeOptions {
+    scope?: vscode.ConfigurationScope;
+    run?: "now" | "once";
+}
+
+/**
+ * Invokes the specified action when a setting changes
+ * @param section the section of the vscode settings to evaluate. Defaults to `powershell`
+ * @param setting a string representation of the setting you wish to evaluate, e.g. `trace.server`
+ * @param action the action to take when the setting changes
+ * @param scope the scope in which the vscode setting should be evaluated.
+ * @returns a Disposable object that can be used to stop listening for changes with dispose()
+ * @example
+ * onSettingChange("powershell", "settingName", (newValue) => console.log(newValue));
+ */
+
+// Because we actually do use the constraint in the callback
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
+export function onSettingChange<T>(
+    section: string,
+    setting: string,
+    action: (newValue: T | undefined) => void,
+    options?: onSettingChangeOptions,
+): vscode.Disposable {
+    const settingPath = `${section}.${setting}`;
+    const disposable = vscode.workspace.onDidChangeConfiguration(e => {
+        if (!e.affectsConfiguration(settingPath, options?.scope)) { return; }
+
+        doOnSettingsChange(section, setting, action, options?.scope);
+        if (options?.run === "once") {
+            disposable.dispose(); // Javascript black magic, referring to an outer reference before it exists
+        }
+    });
+    if (options?.run === "now") {
+        doOnSettingsChange(section, setting, action, options.scope);
+    }
+    return disposable;
+}
+
+/** Implementation is separate to avoid duplicate code for run now */
+
+// Because we actually do use the constraint in the callback
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
+function doOnSettingsChange<T>(
+    section: string,
+    setting: string,
+    action: (newValue: T | undefined) => void,
+    scope?: vscode.ConfigurationScope,
+): void {
+    const value = vscode.workspace.getConfiguration(section, scope).get<T>(setting);
+    action(value);
+}
+
+/**
+ * Invokes the specified action when a PowerShell setting changes. Convenience function for `onSettingChange`
+ * @param setting a string representation of the setting you wish to evaluate, e.g. `trace.server`
+ * @param action the action to take when the setting changes
+ * @param scope the scope in which the vscode setting should be evaluated.n
+ * @returns a Disposable object that can be used to stop listening for changes
+ * @example
+ * onPowerShellSettingChange("settingName", (newValue) => console.log(newValue));
+ */
+
+// Because we actually do use the constraint in the callback
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
+export function onPowerShellSettingChange<T>(
+    setting: string,
+    action: (newValue: T | undefined) => void,
+    options?: onSettingChangeOptions
+
+): vscode.Disposable {
+    const section = "powershell";
+    return onSettingChange(section, setting, action, options);
+}
diff --git a/test/core/paths.test.ts b/test/core/paths.test.ts
index 15f60f5bd1..703b22a53f 100644
--- a/test/core/paths.test.ts
+++ b/test/core/paths.test.ts
@@ -9,9 +9,11 @@ import { checkIfDirectoryExists, checkIfFileExists, ShellIntegrationScript } fro
 
 describe("Path assumptions", function () {
     let globalStorageUri: vscode.Uri;
+    let logUri: vscode.Uri;
     before(async () => {
         const extension: IPowerShellExtensionClient = await utils.ensureEditorServicesIsConnected();
         globalStorageUri = extension.getStorageUri();
+        logUri = extension.getLogUri();
     });
 
     it("Creates the session folder at the correct path", async function () {
@@ -19,7 +21,7 @@ describe("Path assumptions", function () {
     });
 
     it("Creates the log folder at the correct path", async function () {
-        assert(await checkIfDirectoryExists(vscode.Uri.joinPath(globalStorageUri, "logs")));
+        assert(await checkIfDirectoryExists(logUri));
     });
 
     it("Finds the Terminal Shell Integration Script", async function () {
diff --git a/test/utils.ts b/test/utils.ts
index 7d601aadf2..e62de2d87e 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -25,10 +25,10 @@ export class TestLogger implements ILogger {
     writeAndShowInformation(_message: string, ..._additionalMessages: string[]): Promise<void> {
         return Promise.resolve();
     }
-    writeDiagnostic(_message: string, ..._additionalMessages: string[]): void {
+    writeTrace(_message: string, ..._additionalMessages: string[]): void {
         return;
     }
-    writeVerbose(_message: string, ..._additionalMessages: string[]): void {
+    writeDebug(_message: string, ..._additionalMessages: string[]): void {
         return;
     }
     writeWarning(_message: string, ..._additionalMessages: string[]): void {

From 5488029e036ecbcfcfc179c987aa3fe1b16e6829 Mon Sep 17 00:00:00 2001
From: Justin Grote <JustinGrote@users.noreply.github.com>
Date: Mon, 18 Nov 2024 11:06:43 -0700
Subject: [PATCH 04/21] Clarify and simplify support statements

---
 README.md | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index 3c9398d92c..8d11bef44a 100644
--- a/README.md
+++ b/README.md
@@ -53,23 +53,27 @@ picker][] and select **PowerShell ISE**.
 
 ## Platform Support
 
-The extension should work anywhere VS Code itself and PowerShell Core 7.2 or higher is
-[supported][]. For Windows PowerShell, only version 5.1 is supported and only on a best-effort
-basis. PowerShell Core 6, 7.0, and 7.1 have reached end-of-support. We test the following
-configurations:
+The extension should work everywhere [Visual Studio Code](https://code.visualstudio.com/docs/supporting/requirements) is supported using [PowerShell 7+ currently supported versions][].
 
-- **Windows Server 2022** with Windows PowerShell 5.1 and PowerShell Core 7.2, 7.3 and 7.4
-- **macOS 12** with PowerShell Core 7.2, 7.3 and 7.4
-- **Ubuntu 22.04** with PowerShell Core 7.2, 7.3 and 7.4
+> [!IMPORTANT]
+> For Windows PowerShell, only version 5.1 is supported and only on a best-effort basis. [.NET Framework 4.8][dotnet-framework] or higher is required.
+
+> [!IMPORTANT]
+> [Visual Studio Code for the Web](https://code.visualstudio.com/docs/editor/vscode-web) is only supported for limited functionality such as basic syntax highlighting, as the PowerShell engine cannot run in this environment currently.
+
+[VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) Environments, including [Github Codespaces](https://github.com/features/codespaces) and [VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server) are supported.
+
+We actively test the following configurations [in Github Actions on every commit](https://github.com/PowerShell/vscode-powershell/actions/workflows/ci-test.yml):
+- **Windows Server 2022** with Windows PowerShell 5.1 and PowerShell 7+
+- **macOS 14.7** with PowerShell 7+
+- **Ubuntu 22.04** with PowerShell 7+
 
 On Windows, we also test with and without Constrained Language Mode enabled.
 
 Read the [installation instructions][]
 to get more details on how to use the extension on these platforms.
 
-For Windows PowerShell 5.1, [.NET Framework 4.8][dotnet-framework] or higher is required.
-
-[supported]: https://docs.microsoft.com/en-us/powershell/scripting/powershell-support-lifecycle
+[PowerShell 7+ currently supported versions]: https://docs.microsoft.com/en-us/powershell/scripting/powershell-support-lifecycle
 [installation instructions]: https://docs.microsoft.com/en-us/powershell/scripting/components/vscode/using-vscode
 [dotnet-framework]: https://dotnet.microsoft.com/en-us/download/dotnet-framework
 

From 96ad371a720add1398aafe99d8b87937eb228a50 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Mon, 18 Nov 2024 11:33:16 -0800
Subject: [PATCH 05/21] Bump packages

---
 package-lock.json | 326 ++++++++++++++++++++--------------------------
 package.json      |  14 +-
 2 files changed, 149 insertions(+), 191 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f185d2941a..5ccbb484da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,15 +1,14 @@
 {
   "name": "powershell",
-  "version": "2024.2.2",
+  "version": "2024.4.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "powershell",
-      "version": "2024.2.2",
+      "version": "2024.4.0",
       "license": "SEE LICENSE IN LICENSE.txt",
       "dependencies": {
-        "@types/vscode": "~1.94.0",
         "@vscode/extension-telemetry": "^0.9.7",
         "node-fetch": "^2.7.0",
         "semver": "^7.6.3",
@@ -19,7 +18,7 @@
         "vscode-languageserver-protocol": "^3.17.5"
       },
       "devDependencies": {
-        "@vscode/vsce": "^3.1.1",
+        "@vscode/vsce": "^3.2.1",
         "esbuild": "^0.21.5"
       },
       "engines": {
@@ -28,26 +27,26 @@
       "optionalDependencies": {
         "@types/mocha": "^10.0.9",
         "@types/mock-fs": "^4.13.4",
-        "@types/node": "^20.16.11",
-        "@types/node-fetch": "^2.6.11",
+        "@types/node": "^20.17.6",
+        "@types/node-fetch": "^2.6.12",
         "@types/rewire": "^2.5.30",
         "@types/semver": "^7.5.8",
         "@types/sinon": "^17.0.3",
         "@types/ungap__structured-clone": "^1.2.0",
         "@types/uuid": "^9.0.8",
         "@types/vscode": "~1.94.0",
-        "@typescript-eslint/eslint-plugin": "^8.8.1",
-        "@typescript-eslint/parser": "^8.8.1",
+        "@typescript-eslint/eslint-plugin": "^8.14.0",
+        "@typescript-eslint/parser": "^8.14.0",
         "@ungap/structured-clone": "^1.2.0",
         "@vscode/debugprotocol": "^1.68.0",
         "@vscode/test-electron": "^2.4.1",
         "eslint": "^8.57.0",
         "eslint-plugin-header": "^3.1.1",
         "glob": "^11.0.0",
-        "mocha": "^10.7.3",
+        "mocha": "^10.8.2",
         "mocha-explorer-launcher-scripts": "^0.4.0",
         "mocha-multi-reporters": "^1.5.1",
-        "mock-fs": "^5.3.0",
+        "mock-fs": "^5.4.1",
         "rewire": "^7.0.0",
         "sinon": "^18.0.1",
         "source-map-support": "^0.5.21",
@@ -55,34 +54,23 @@
       }
     },
     "node_modules/@azure/abort-controller": {
-      "version": "1.1.0",
-      "integrity": "sha1-eI7nhFelWvihrTQqyxgjg9IRkkk=",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=12.0.0"
-      }
-    },
-    "node_modules/@azure/core-auth": {
-      "version": "1.8.0",
-      "integrity": "sha1-KBtKbTMJw+exW82WfwHUx5rkodY=",
+      "version": "2.1.2",
+      "integrity": "sha1-Qv4MyrI4QdmQWBLFjxCC0neEVm0=",
       "dev": true,
       "dependencies": {
-        "@azure/abort-controller": "^2.0.0",
-        "@azure/core-util": "^1.1.0",
         "tslib": "^2.6.2"
       },
       "engines": {
         "node": ">=18.0.0"
       }
     },
-    "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": {
-      "version": "2.1.2",
-      "integrity": "sha1-Qv4MyrI4QdmQWBLFjxCC0neEVm0=",
+    "node_modules/@azure/core-auth": {
+      "version": "1.9.0",
+      "integrity": "sha1-rHJbA/q+PIkjcQZe6eIEG+4P0aw=",
       "dev": true,
       "dependencies": {
+        "@azure/abort-controller": "^2.0.0",
+        "@azure/core-util": "^1.11.0",
         "tslib": "^2.6.2"
       },
       "engines": {
@@ -106,26 +94,15 @@
         "node": ">=18.0.0"
       }
     },
-    "node_modules/@azure/core-client/node_modules/@azure/abort-controller": {
-      "version": "2.1.2",
-      "integrity": "sha1-Qv4MyrI4QdmQWBLFjxCC0neEVm0=",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.6.2"
-      },
-      "engines": {
-        "node": ">=18.0.0"
-      }
-    },
     "node_modules/@azure/core-rest-pipeline": {
-      "version": "1.17.0",
-      "integrity": "sha1-Vdr6EJNVPFSe1tjbymmqUFx7OqM=",
+      "version": "1.18.0",
+      "integrity": "sha1-Fl8c2bsQYL47aJV0LbPR8RBicdM=",
       "dev": true,
       "dependencies": {
         "@azure/abort-controller": "^2.0.0",
         "@azure/core-auth": "^1.8.0",
         "@azure/core-tracing": "^1.0.1",
-        "@azure/core-util": "^1.9.0",
+        "@azure/core-util": "^1.11.0",
         "@azure/logger": "^1.0.0",
         "http-proxy-agent": "^7.0.0",
         "https-proxy-agent": "^7.0.0",
@@ -135,17 +112,6 @@
         "node": ">=18.0.0"
       }
     },
-    "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": {
-      "version": "2.1.2",
-      "integrity": "sha1-Qv4MyrI4QdmQWBLFjxCC0neEVm0=",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.6.2"
-      },
-      "engines": {
-        "node": ">=18.0.0"
-      }
-    },
     "node_modules/@azure/core-tracing": {
       "version": "1.2.0",
       "integrity": "sha1-e+XVPDUi1jnPGQQsvNsZ9xvDWrI=",
@@ -158,8 +124,8 @@
       }
     },
     "node_modules/@azure/core-util": {
-      "version": "1.10.0",
-      "integrity": "sha1-zzFjOC1ANDlyhIyRSGmGTfXUS9s=",
+      "version": "1.11.0",
+      "integrity": "sha1-9TD8Z+c4rqhy+90cyEFucCGfrac=",
       "dev": true,
       "dependencies": {
         "@azure/abort-controller": "^2.0.0",
@@ -169,31 +135,20 @@
         "node": ">=18.0.0"
       }
     },
-    "node_modules/@azure/core-util/node_modules/@azure/abort-controller": {
-      "version": "2.1.2",
-      "integrity": "sha1-Qv4MyrI4QdmQWBLFjxCC0neEVm0=",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.6.2"
-      },
-      "engines": {
-        "node": ">=18.0.0"
-      }
-    },
     "node_modules/@azure/identity": {
-      "version": "4.4.1",
-      "integrity": "sha1-SQ+irSZ4Yimvo2QRiSu1Pfo0eNM=",
+      "version": "4.5.0",
+      "integrity": "sha1-k843V792GgjP0F9W7xgUNeBbnhw=",
       "dev": true,
       "dependencies": {
-        "@azure/abort-controller": "^1.0.0",
-        "@azure/core-auth": "^1.5.0",
+        "@azure/abort-controller": "^2.0.0",
+        "@azure/core-auth": "^1.9.0",
         "@azure/core-client": "^1.9.2",
-        "@azure/core-rest-pipeline": "^1.1.0",
+        "@azure/core-rest-pipeline": "^1.17.0",
         "@azure/core-tracing": "^1.0.0",
-        "@azure/core-util": "^1.3.0",
+        "@azure/core-util": "^1.11.0",
         "@azure/logger": "^1.0.0",
-        "@azure/msal-browser": "^3.14.0",
-        "@azure/msal-node": "^2.9.2",
+        "@azure/msal-browser": "^3.26.1",
+        "@azure/msal-node": "^2.15.0",
         "events": "^3.0.0",
         "jws": "^4.0.0",
         "open": "^8.0.0",
@@ -216,30 +171,30 @@
       }
     },
     "node_modules/@azure/msal-browser": {
-      "version": "3.26.1",
-      "integrity": "sha1-L0No15l2gtsw3KUuMvysNj+g760=",
+      "version": "3.27.0",
+      "integrity": "sha1-tvAvc8jhAtPxFQCbRndTn7Fz/is=",
       "dev": true,
       "dependencies": {
-        "@azure/msal-common": "14.15.0"
+        "@azure/msal-common": "14.16.0"
       },
       "engines": {
         "node": ">=0.8.0"
       }
     },
     "node_modules/@azure/msal-common": {
-      "version": "14.15.0",
-      "integrity": "sha1-DiesC7iP4QD0+NFgW2TVwmhjalU=",
+      "version": "14.16.0",
+      "integrity": "sha1-80cPyux4jb5QhZlSzUmTQL2iPXo=",
       "dev": true,
       "engines": {
         "node": ">=0.8.0"
       }
     },
     "node_modules/@azure/msal-node": {
-      "version": "2.15.0",
-      "integrity": "sha1-UL+OaSpmVgJ8Bzp12HeopHiq/f0=",
+      "version": "2.16.1",
+      "integrity": "sha1-iYKIMujmyKiM7MTvbY1OQ1IRa3c=",
       "dev": true,
       "dependencies": {
-        "@azure/msal-common": "14.15.0",
+        "@azure/msal-common": "14.16.0",
         "jsonwebtoken": "^9.0.0",
         "uuid": "^8.3.0"
       },
@@ -601,22 +556,25 @@
       }
     },
     "node_modules/@eslint-community/eslint-utils": {
-      "version": "4.4.0",
-      "integrity": "sha1-ojUU6Pua8SadX3eIqlVnmNYca1k=",
+      "version": "4.4.1",
+      "integrity": "sha1-0RRb8sIBMtZABJXW30v1k2L9nVY=",
       "optional": true,
       "dependencies": {
-        "eslint-visitor-keys": "^3.3.0"
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
       "peerDependencies": {
         "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
       }
     },
     "node_modules/@eslint-community/regexpp": {
-      "version": "4.11.1",
-      "integrity": "sha1-pUe638cZ6z5fS1VjJeVC++nXoY8=",
+      "version": "4.12.1",
+      "integrity": "sha1-z8bP/jnfOQo4Qc3iq8z5Lqp64OA=",
       "optional": true,
       "engines": {
         "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
@@ -764,10 +722,10 @@
       }
     },
     "node_modules/@microsoft/1ds-core-js": {
-      "version": "4.3.3",
-      "integrity": "sha1-+HAkGN3vebFBfwQNlGpJ4XOlBFQ=",
+      "version": "4.3.4",
+      "integrity": "sha1-g2zxPwrNbe6tqEFBN/yJ57RsX8g=",
       "dependencies": {
-        "@microsoft/applicationinsights-core-js": "3.3.3",
+        "@microsoft/applicationinsights-core-js": "3.3.4",
         "@microsoft/applicationinsights-shims": "3.0.1",
         "@microsoft/dynamicproto-js": "^2.0.3",
         "@nevware21/ts-async": ">= 0.5.2 < 2.x",
@@ -775,10 +733,10 @@
       }
     },
     "node_modules/@microsoft/1ds-post-js": {
-      "version": "4.3.3",
-      "integrity": "sha1-FR9adD1ZmOgCkZII7wqcX1Xv+HQ=",
+      "version": "4.3.4",
+      "integrity": "sha1-5O0cNpHHst0z+N14vtlzA6m8VQw=",
       "dependencies": {
-        "@microsoft/1ds-core-js": "4.3.3",
+        "@microsoft/1ds-core-js": "4.3.4",
         "@microsoft/applicationinsights-shims": "3.0.1",
         "@microsoft/dynamicproto-js": "^2.0.3",
         "@nevware21/ts-async": ">= 0.5.2 < 2.x",
@@ -786,11 +744,11 @@
       }
     },
     "node_modules/@microsoft/applicationinsights-channel-js": {
-      "version": "3.3.3",
-      "integrity": "sha1-bukPn7WxMzMgMxNTs/VBOFM0cY4=",
+      "version": "3.3.4",
+      "integrity": "sha1-FC90ky04SOESN/8cT0hMCtPaShU=",
       "dependencies": {
-        "@microsoft/applicationinsights-common": "3.3.3",
-        "@microsoft/applicationinsights-core-js": "3.3.3",
+        "@microsoft/applicationinsights-common": "3.3.4",
+        "@microsoft/applicationinsights-core-js": "3.3.4",
         "@microsoft/applicationinsights-shims": "3.0.1",
         "@microsoft/dynamicproto-js": "^2.0.3",
         "@nevware21/ts-async": ">= 0.5.2 < 2.x",
@@ -801,10 +759,10 @@
       }
     },
     "node_modules/@microsoft/applicationinsights-common": {
-      "version": "3.3.3",
-      "integrity": "sha1-jEcJ7AqYANxwrZJYD9c7HCZOOVQ=",
+      "version": "3.3.4",
+      "integrity": "sha1-STTbpg5sxM2gTGmAQhXVt3BwWbk=",
       "dependencies": {
-        "@microsoft/applicationinsights-core-js": "3.3.3",
+        "@microsoft/applicationinsights-core-js": "3.3.4",
         "@microsoft/applicationinsights-shims": "3.0.1",
         "@microsoft/dynamicproto-js": "^2.0.3",
         "@nevware21/ts-utils": ">= 0.11.3 < 2.x"
@@ -814,8 +772,8 @@
       }
     },
     "node_modules/@microsoft/applicationinsights-core-js": {
-      "version": "3.3.3",
-      "integrity": "sha1-Z+C6y7gwv7dYzEo3BhqC31KkCRQ=",
+      "version": "3.3.4",
+      "integrity": "sha1-r5wrUwhkeKBTn/C0ap9oGZ+RmsI=",
       "dependencies": {
         "@microsoft/applicationinsights-shims": "3.0.1",
         "@microsoft/dynamicproto-js": "^2.0.3",
@@ -834,12 +792,12 @@
       }
     },
     "node_modules/@microsoft/applicationinsights-web-basic": {
-      "version": "3.3.3",
-      "integrity": "sha1-twQmd5FzzT/OdF2k/AYrmdUAFMA=",
+      "version": "3.3.4",
+      "integrity": "sha1-k2K2StVrz7U6gPciLYkZOwq4+2s=",
       "dependencies": {
-        "@microsoft/applicationinsights-channel-js": "3.3.3",
-        "@microsoft/applicationinsights-common": "3.3.3",
-        "@microsoft/applicationinsights-core-js": "3.3.3",
+        "@microsoft/applicationinsights-channel-js": "3.3.4",
+        "@microsoft/applicationinsights-common": "3.3.4",
+        "@microsoft/applicationinsights-core-js": "3.3.4",
         "@microsoft/applicationinsights-shims": "3.0.1",
         "@microsoft/dynamicproto-js": "^2.0.3",
         "@nevware21/ts-async": ">= 0.5.2 < 2.x",
@@ -857,15 +815,15 @@
       }
     },
     "node_modules/@nevware21/ts-async": {
-      "version": "0.5.2",
-      "integrity": "sha1-pBiD3GzMRma98VbpLzXzAD/T9vA=",
+      "version": "0.5.3",
+      "integrity": "sha1-R8BDUUWLBARXl3+0t1xDwn+6MoM=",
       "dependencies": {
-        "@nevware21/ts-utils": ">= 0.11.3 < 2.x"
+        "@nevware21/ts-utils": ">= 0.11.5 < 2.x"
       }
     },
     "node_modules/@nevware21/ts-utils": {
-      "version": "0.11.4",
-      "integrity": "sha1-sLfqRs/xO51lrFMbWebc2N7AGGk="
+      "version": "0.11.5",
+      "integrity": "sha1-uQD10E5lepbglqWNrW4Leu0nPvs="
     },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
@@ -952,16 +910,16 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "20.16.11",
-      "integrity": "sha1-m1RMPnFrFXesEucPkUUZPzJ1CzM=",
+      "version": "20.17.6",
+      "integrity": "sha1-bkBzIwwYDTV56MYBQfme/fXfAIE=",
       "optional": true,
       "dependencies": {
         "undici-types": "~6.19.2"
       }
     },
     "node_modules/@types/node-fetch": {
-      "version": "2.6.11",
-      "integrity": "sha1-mzm3hmXa4OgqCPAvSWfWLGb5XSQ=",
+      "version": "2.6.12",
+      "integrity": "sha1-irXD74Mw8TEAp0eeLNVtM4aDCgM=",
       "optional": true,
       "dependencies": {
         "@types/node": "*",
@@ -1007,15 +965,15 @@
       "optional": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.8.1",
-      "integrity": "sha1-k2S3VtTXi8vfb9PpNF5pJMaK03E=",
+      "version": "8.14.0",
+      "integrity": "sha1-fcDkGch76tyPVUv1pC5QCe03SNw=",
       "optional": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.8.1",
-        "@typescript-eslint/type-utils": "8.8.1",
-        "@typescript-eslint/utils": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1",
+        "@typescript-eslint/scope-manager": "8.14.0",
+        "@typescript-eslint/type-utils": "8.14.0",
+        "@typescript-eslint/utils": "8.14.0",
+        "@typescript-eslint/visitor-keys": "8.14.0",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
@@ -1039,14 +997,14 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.8.1",
-      "integrity": "sha1-WVK6KoO9UgJLhy8/3I7S02Ngc7g=",
+      "version": "8.14.0",
+      "integrity": "sha1-Cn6dvBG8B3FqstexImIX6fa1H8g=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.8.1",
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/typescript-estree": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1",
+        "@typescript-eslint/scope-manager": "8.14.0",
+        "@typescript-eslint/types": "8.14.0",
+        "@typescript-eslint/typescript-estree": "8.14.0",
+        "@typescript-eslint/visitor-keys": "8.14.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1066,12 +1024,12 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.8.1",
-      "integrity": "sha1-tL6hwHharr/jxKsFntrqHEl35/8=",
+      "version": "8.14.0",
+      "integrity": "sha1-AfN8FHpzXNePD/NV4DO5RX2h83M=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1"
+        "@typescript-eslint/types": "8.14.0",
+        "@typescript-eslint/visitor-keys": "8.14.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1082,12 +1040,12 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.8.1",
-      "integrity": "sha1-MfWexG6ToCtAn7TUBqNopZ+tMG4=",
+      "version": "8.14.0",
+      "integrity": "sha1-RVxq8wwzayShryi8T4G43V102U0=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.8.1",
-        "@typescript-eslint/utils": "8.8.1",
+        "@typescript-eslint/typescript-estree": "8.14.0",
+        "@typescript-eslint/utils": "8.14.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -1105,8 +1063,8 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.8.1",
-      "integrity": "sha1-6+heD6So4yokpWra3wYBA77xO9E=",
+      "version": "8.14.0",
+      "integrity": "sha1-DTPY0LCEecQk59ZUhV/d8sceQCE=",
       "optional": true,
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1117,12 +1075,12 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.8.1",
-      "integrity": "sha1-NGSfTijTLuSRUhk7x97cDnjl0ew=",
+      "version": "8.14.0",
+      "integrity": "sha1-p6OlpTpsCTE+EvtFMdT/WC7jwxI=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/visitor-keys": "8.8.1",
+        "@typescript-eslint/types": "8.14.0",
+        "@typescript-eslint/visitor-keys": "8.14.0",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
@@ -1144,14 +1102,14 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.8.1",
-      "integrity": "sha1-nilID7+iZMJpRiU9qnIYH58FPJ0=",
+      "version": "8.14.0",
+      "integrity": "sha1-rCUGh14Dq6JOYCNk5Dst+kVSnb0=",
       "optional": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.8.1",
-        "@typescript-eslint/types": "8.8.1",
-        "@typescript-eslint/typescript-estree": "8.8.1"
+        "@typescript-eslint/scope-manager": "8.14.0",
+        "@typescript-eslint/types": "8.14.0",
+        "@typescript-eslint/typescript-estree": "8.14.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1165,11 +1123,11 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.8.1",
-      "integrity": "sha1-D7EoDzgRSfw0Xf3in3VC/05Yf8U=",
+      "version": "8.14.0",
+      "integrity": "sha1-JBjVpUZpr5ZYmGreTmz7d2fYFa0=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.8.1",
+        "@typescript-eslint/types": "8.14.0",
         "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
@@ -1218,8 +1176,8 @@
       }
     },
     "node_modules/@vscode/vsce": {
-      "version": "3.1.1",
-      "integrity": "sha1-sYtEeW2eW3pIo6Hon5Pe+cOzOLk=",
+      "version": "3.2.1",
+      "integrity": "sha1-e/qGnqQ/59eH8J4WTw8OI534+x0=",
       "dev": true,
       "dependencies": {
         "@azure/identity": "^4.1.0",
@@ -1258,8 +1216,8 @@
       }
     },
     "node_modules/@vscode/vsce-sign": {
-      "version": "2.0.4",
-      "integrity": "sha1-tL8VXRbypLrcBp34UNyG91YSSEI=",
+      "version": "2.0.5",
+      "integrity": "sha1-iFADZHbcDU4IDZwtgyXj6X7/UZM=",
       "dev": true,
       "hasInstallScript": true,
       "optionalDependencies": {
@@ -1403,8 +1361,8 @@
       }
     },
     "node_modules/acorn": {
-      "version": "8.12.1",
-      "integrity": "sha1-cWFr3MviXielRDngBG6JynbfIkg=",
+      "version": "8.14.0",
+      "integrity": "sha1-Bj4scMrF+09kZ/CxEVLgTGgnlbA=",
       "optional": true,
       "bin": {
         "acorn": "bin/acorn"
@@ -1911,8 +1869,8 @@
       "optional": true
     },
     "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "integrity": "sha1-9zqFudXUHQRVUcF34ogtSshXKKY=",
+      "version": "7.0.5",
+      "integrity": "sha1-kQqsiA/1JD2pa3KLxlIaX2wvL4I=",
       "devOptional": true,
       "dependencies": {
         "path-key": "^3.1.0",
@@ -3556,8 +3514,8 @@
       "optional": true
     },
     "node_modules/mocha": {
-      "version": "10.7.3",
-      "integrity": "sha1-rjIAPKu9UrWa7OF4RgVqaOtLB1I=",
+      "version": "10.8.2",
+      "integrity": "sha1-jYNC0BbtQRsSpCnrcxuCX5Ya+5Y=",
       "optional": true,
       "dependencies": {
         "ansi-colors": "^4.1.3",
@@ -3675,8 +3633,8 @@
       }
     },
     "node_modules/mock-fs": {
-      "version": "5.3.0",
-      "integrity": "sha1-ffyVzlUor/jhD6EXFhuR2BKeDp4=",
+      "version": "5.4.1",
+      "integrity": "sha1-sAq8ZYyxnbvygv3i8Fu3Uc0eEqU=",
       "optional": true,
       "engines": {
         "node": ">=12.0.0"
@@ -3716,16 +3674,16 @@
       }
     },
     "node_modules/nise/node_modules/@sinonjs/fake-timers": {
-      "version": "13.0.2",
-      "integrity": "sha1-P/6Iq7BiBnpYD9+6cGrQBDWg8qY=",
+      "version": "13.0.5",
+      "integrity": "sha1-NrnbwhrVVGSG6pFz1r6gY+sXF9U=",
       "optional": true,
       "dependencies": {
         "@sinonjs/commons": "^3.0.1"
       }
     },
     "node_modules/node-abi": {
-      "version": "3.68.0",
-      "integrity": "sha1-jzf7Auz09D6+aUCQ3LUuDEzEuiU=",
+      "version": "3.71.0",
+      "integrity": "sha1-UthLvNhXXvtxRo+6ofmkmywkIDg=",
       "dev": true,
       "optional": true,
       "dependencies": {
@@ -3779,8 +3737,8 @@
       }
     },
     "node_modules/object-inspect": {
-      "version": "1.13.2",
-      "integrity": "sha1-3qAIhGf7mR5nr0BYFHokgkowQ/8=",
+      "version": "1.13.3",
+      "integrity": "sha1-8UwYPeURMCQ9bRiuFJN1/1DqSIo=",
       "dev": true,
       "engines": {
         "node": ">= 0.4"
@@ -4014,22 +3972,22 @@
       }
     },
     "node_modules/parse5": {
-      "version": "7.1.2",
-      "integrity": "sha1-Bza+u/13eTgjJAojt/xeAQt/jjI=",
+      "version": "7.2.1",
+      "integrity": "sha1-iSj1WRXmEl9DDMRDCXZb8XVWozo=",
       "dev": true,
       "dependencies": {
-        "entities": "^4.4.0"
+        "entities": "^4.5.0"
       },
       "funding": {
         "url": "https://github.com/inikulin/parse5?sponsor=1"
       }
     },
     "node_modules/parse5-htmlparser2-tree-adapter": {
-      "version": "7.0.0",
-      "integrity": "sha1-I8LMIzvPCbt766i4pp1GsIxiwvE=",
+      "version": "7.1.0",
+      "integrity": "sha1-tagGVI7Yk6Q+JMy0L7t4BpMR6Bs=",
       "dev": true,
       "dependencies": {
-        "domhandler": "^5.0.2",
+        "domhandler": "^5.0.3",
         "parse5": "^7.0.0"
       },
       "funding": {
@@ -4087,8 +4045,8 @@
       }
     },
     "node_modules/path-scurry/node_modules/lru-cache": {
-      "version": "11.0.1",
-      "integrity": "sha1-OnMvv+24LFunvKZWStP0Kvy24Uc=",
+      "version": "11.0.2",
+      "integrity": "sha1-+9jnz4IR9efl2RkFxBWj9VdVyjk=",
       "devOptional": true,
       "engines": {
         "node": "20 || >=22"
@@ -4184,8 +4142,8 @@
       }
     },
     "node_modules/qs": {
-      "version": "6.13.0",
-      "integrity": "sha1-bKO9WEOffiRWVXmJl3h7DYilGQY=",
+      "version": "6.13.1",
+      "integrity": "sha1-POX8cr06gXG4XJm5PGXdILfRsW4=",
       "dev": true,
       "dependencies": {
         "side-channel": "^1.0.6"
@@ -4893,8 +4851,8 @@
       "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
     },
     "node_modules/ts-api-utils": {
-      "version": "1.3.0",
-      "integrity": "sha1-S0kOJxKfHo5oa0XMSrY3FNxg7qE=",
+      "version": "1.4.0",
+      "integrity": "sha1-cJxvIHblEagVV/PQegy9VmroGVw=",
       "optional": true,
       "engines": {
         "node": ">=16"
@@ -4904,8 +4862,8 @@
       }
     },
     "node_modules/tslib": {
-      "version": "2.7.0",
-      "integrity": "sha1-2bQMXECrWehzjyl98wh78aJpDAE="
+      "version": "2.8.1",
+      "integrity": "sha1-YS7+TtI11Wfoq6Xypfq3AoCt6D8="
     },
     "node_modules/tunnel": {
       "version": "0.0.6",
@@ -4990,8 +4948,8 @@
       "dev": true
     },
     "node_modules/undici": {
-      "version": "6.20.0",
-      "integrity": "sha1-O5TZZ2k3WepiWjt4sglyE/MEBaE=",
+      "version": "6.21.0",
+      "integrity": "sha1-Sz06+u+YTge0jnYgw07YooXtTNQ=",
       "dev": true,
       "engines": {
         "node": ">=18.17"
diff --git a/package.json b/package.json
index 73687b5fae..bf705268fe 100644
--- a/package.json
+++ b/package.json
@@ -69,32 +69,32 @@
     "vscode-languageserver-protocol": "^3.17.5"
   },
   "devDependencies": {
-    "@vscode/vsce": "^3.1.1",
+    "@vscode/vsce": "^3.2.1",
     "esbuild": "^0.21.5"
   },
   "optionalDependencies": {
     "@types/mocha": "^10.0.9",
     "@types/mock-fs": "^4.13.4",
-    "@types/node": "^20.16.11",
-    "@types/node-fetch": "^2.6.11",
+    "@types/node": "^20.17.6",
+    "@types/node-fetch": "^2.6.12",
     "@types/rewire": "^2.5.30",
     "@types/semver": "^7.5.8",
     "@types/sinon": "^17.0.3",
     "@types/ungap__structured-clone": "^1.2.0",
     "@types/uuid": "^9.0.8",
     "@types/vscode": "~1.94.0",
-    "@typescript-eslint/eslint-plugin": "^8.8.1",
-    "@typescript-eslint/parser": "^8.8.1",
+    "@typescript-eslint/eslint-plugin": "^8.14.0",
+    "@typescript-eslint/parser": "^8.14.0",
     "@ungap/structured-clone": "^1.2.0",
     "@vscode/debugprotocol": "^1.68.0",
     "@vscode/test-electron": "^2.4.1",
     "eslint": "^8.57.0",
     "eslint-plugin-header": "^3.1.1",
     "glob": "^11.0.0",
-    "mocha": "^10.7.3",
+    "mocha": "^10.8.2",
     "mocha-explorer-launcher-scripts": "^0.4.0",
     "mocha-multi-reporters": "^1.5.1",
-    "mock-fs": "^5.3.0",
+    "mock-fs": "^5.4.1",
     "rewire": "^7.0.0",
     "sinon": "^18.0.1",
     "source-map-support": "^0.5.21",

From c9533fe4c913ae44051858989cf0c0993564381e Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Mon, 18 Nov 2024 13:07:13 -0800
Subject: [PATCH 06/21] Use correct build of PowerShellEditorServices in
 OneBranch

Since OneBranch is still using PowerShell 7.3, the PowerShellEditorServices
is only partially succeeding as its "assert release configuration" step
fails (I've verified it manually) with a continueOnError flag set to true.
So the correct build is from a pipeline that's only partially succeeded,
and the default setting of the download tasks silently skips those.
Now we explicitly allow them and print out the version so this is easier.
---
 .pipelines/vscode-powershell-Official.yml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/.pipelines/vscode-powershell-Official.yml b/.pipelines/vscode-powershell-Official.yml
index cea5d0b6d3..1bfcd22fa8 100644
--- a/.pipelines/vscode-powershell-Official.yml
+++ b/.pipelines/vscode-powershell-Official.yml
@@ -94,6 +94,8 @@ extends:
               project: PowerShellCore
               definition: 2905
               specificBuildWithTriggering: true
+              allowPartiallySucceededBuilds: true
+              buildVersionToDownload: latestFromBranch
               branchName: refs/heads/main
               artifact: drop_build_main
           - task: ExtractFiles@1
@@ -101,6 +103,10 @@ extends:
             inputs:
               archiveFilePatterns: $(Pipeline.Workspace)/PowerShellEditorServices.zip
               destinationFolder: $(Build.SourcesDirectory)/modules
+          - pwsh: |
+              $manifest = Test-ModuleManifest $(Build.SourcesDirectory)/modules/PowerShellEditorServices/PowerShellEditorServices.psd1
+              Write-Host Using PowerShellEditorServices v$($manifest.Version)
+            displayName: PowerShellEditorServices version
           - pwsh: |
               Register-PSRepository -Name CFS -SourceLocation "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v2" -InstallationPolicy Trusted
               Install-Module -Repository CFS -Name Microsoft.PowerShell.PSResourceGet
@@ -156,6 +162,8 @@ extends:
               project: PowerShellCore
               definition: 2905
               specificBuildWithTriggering: true
+              allowPartiallySucceededBuilds: true
+              buildVersionToDownload: latestFromBranch
               branchName: refs/heads/main
               artifact: drop_build_main
           - task: ExtractFiles@1
@@ -163,6 +171,10 @@ extends:
             inputs:
               archiveFilePatterns: $(Pipeline.Workspace)/PowerShellEditorServices.zip
               destinationFolder: $(Build.SourcesDirectory)/modules
+          - pwsh: |
+              $manifest = Test-ModuleManifest $(Build.SourcesDirectory)/modules/PowerShellEditorServices/PowerShellEditorServices.psd1
+              Write-Host Using PowerShellEditorServices v$($manifest.Version)
+            displayName: PowerShellEditorServices version
           - pwsh: |
               Register-PSRepository -Name CFS -SourceLocation "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v2" -InstallationPolicy Trusted
               Install-Module -Repository CFS -Name Microsoft.PowerShell.PSResourceGet

From 0b612535f1353a7a6273c4197fd69d17cbd88588 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Mon, 18 Nov 2024 11:47:59 -0800
Subject: [PATCH 07/21] v2024.5.1-preview: Drop support for PowerShell <7.4 and
 logging overhaul

PowerShell 7.2 LTS and 7.3 are now past end-of-support and are now unsupported.
This is an incompatible API change so we're bumping the major version
of PowerShell Editor Services.
Please update to PowerShell 7.4 LTS going forward.

This release contains a logging overhaul which purposely removes our
dependency on Serilog and should lead to improved stability with
PowerShell 5.1 (by avoiding a major GAC assembly conflict).
---
 CHANGELOG.md | 18 ++++++++++++++++++
 package.json |  2 +-
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2e4a57b76..dd6941264e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
 # PowerShell Extension Release History
 
+## v2024.5.1-preview
+### Monday, November 18, 2024
+
+With PowerShell Editor Services [v4.0.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.0.0)!
+
+Drop support for PowerShell <7.4 and logging overhaul
+
+PowerShell 7.2 LTS and 7.3 are now past end-of-support and are now unsupported.
+This is an incompatible API change so we're bumping the major version
+of PowerShell Editor Services.
+Please update to PowerShell 7.4 LTS going forward.
+
+This release contains a logging overhaul which purposely removes our
+dependency on Serilog and should lead to improved stability with
+PowerShell 5.1 (by avoiding a major GAC assembly conflict).
+
+See more details at the GitHub Release for [v2024.5.1-preview](https://github.com/PowerShell/vscode-powershell/releases/tag/v2024.5.1-preview).
+
 ## v2024.4.0
 ### Monday, November 04, 2024
 
diff --git a/package.json b/package.json
index bf705268fe..bb448383fb 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "powershell",
   "displayName": "PowerShell",
-  "version": "2024.4.0",
+  "version": "2024.5.1",
   "preview": false,
   "publisher": "ms-vscode",
   "description": "Develop PowerShell modules, commands and scripts in Visual Studio Code!",

From 7a42c84eacc5be863b281d818ce38242abc5f258 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 20 Nov 2024 14:01:11 -0800
Subject: [PATCH 08/21] Improve warning when additional PowerShell isn't found

Since we added logic which searches for possible intended permutations
of the given additional PowerShell path, we needed to make the warning
show only if none of the permutations were found. This was accomplished
by suppressing it in the first iterator and then yielding it again after
the permutations were exhausted with the warning unsuppressed.
---
 src/platform.ts            | 18 +++++----
 test/core/platform.test.ts | 77 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/src/platform.ts b/src/platform.ts
index 5e63dca6bd..706c9f56e2 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -239,17 +239,17 @@ export class PowerShellExeFinder {
                 }
 
                 exePath = untildify(exePath);
-
-                // Always search for what the user gave us first
-                yield new PossiblePowerShellExe(exePath, versionName);
-
-                // Also search for `pwsh[.exe]` and `powershell[.exe]` if missing
                 const args: [string, undefined, boolean, boolean]
                     // Must be a tuple type and is suppressing the warning
                     = [versionName, undefined, true, true];
 
-                // Handle Windows where '.exe' and 'powershell' are things
+                // Always search for what the user gave us first, but with the warning
+                // suppressed so we can display it after all possibilities are exhausted
+                yield new PossiblePowerShellExe(exePath, ...args);
+
+                // Also search for `pwsh[.exe]` and `powershell[.exe]` if missing
                 if (this.platformDetails.operatingSystem === OperatingSystem.Windows) {
+                    // Handle Windows where '.exe' and 'powershell' are things
                     if (!exePath.endsWith("pwsh.exe") && !exePath.endsWith("powershell.exe")) {
                         if (exePath.endsWith("pwsh") || exePath.endsWith("powershell")) {
                             // Add extension if that was missing
@@ -260,9 +260,13 @@ export class PowerShellExeFinder {
                         yield new PossiblePowerShellExe(path.join(exePath, "pwsh.exe"), ...args);
                         yield new PossiblePowerShellExe(path.join(exePath, "powershell.exe"), ...args);
                     }
-                } else if (!exePath.endsWith("pwsh")) { // Always just 'pwsh' on non-Windows
+                } else if (!exePath.endsWith("pwsh")) {
+                    // Always just 'pwsh' on non-Windows
                     yield new PossiblePowerShellExe(path.join(exePath, "pwsh"), ...args);
                 }
+
+                // If we're still being iterated over, no permutation of the given path existed so yield an object with the warning unsuppressed
+                yield new PossiblePowerShellExe(exePath, versionName, false, undefined, false);
             }
         }
     }
diff --git a/test/core/platform.test.ts b/test/core/platform.test.ts
index 853b550685..299e125626 100644
--- a/test/core/platform.test.ts
+++ b/test/core/platform.test.ts
@@ -468,7 +468,17 @@ if (process.platform === "win32") {
                 isProcess64Bit: true,
             },
             environmentVars: {},
+            // Note that for each given path, we expect:
+            // 1. The path as-is.
+            // 2. Any expected permutations of the path (for example, with a tilde or folder expanded, and/or '.exe' added).
+            // 3. The path as-is again (in order for a warning to be displayed at the correct time).
+            // An improvement here would be to check the suppressWarning field, but it's not currently exposed.
             expectedPowerShellSequence: [
+                {
+                    exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
+                    displayName: "pwsh",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh",
@@ -479,6 +489,11 @@ if (process.platform === "win32") {
                     displayName: "pwsh-tilde",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: path.join(os.homedir(), "pwsh", "pwsh.exe"),
+                    displayName: "pwsh-tilde",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh",
                     displayName: "pwsh-no-exe",
@@ -499,6 +514,11 @@ if (process.platform === "win32") {
                     displayName: "pwsh-no-exe",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "C:\\Users\\test\\pwsh\\pwsh",
+                    displayName: "pwsh-no-exe",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\",
                     displayName: "pwsh-folder",
@@ -514,6 +534,11 @@ if (process.platform === "win32") {
                     displayName: "pwsh-folder",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "C:\\Users\\test\\pwsh\\",
+                    displayName: "pwsh-folder",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "C:\\Users\\test\\pwsh",
                     displayName: "pwsh-folder-no-slash",
@@ -534,6 +559,16 @@ if (process.platform === "win32") {
                     displayName: "pwsh-folder-no-slash",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "C:\\Users\\test\\pwsh",
+                    displayName: "pwsh-folder-no-slash",
+                    supportsProperArguments: true
+                },
+                {
+                    exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
+                    displayName: "pwsh-single-quotes",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh-single-quotes",
@@ -544,6 +579,11 @@ if (process.platform === "win32") {
                     displayName: "pwsh-double-quotes",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
+                    displayName: "pwsh-double-quotes",
+                    supportsProperArguments: true
+                },
             ],
             filesystem: {},
         }
@@ -760,19 +800,34 @@ if (process.platform === "win32") {
 
     successAdditionalTestCases = [
         {   // Also sufficient for macOS as the behavior is the same
-            name: "Linux (Additional PowerShell Executables)",
+            name: "Linux/macOS (Additional PowerShell Executables)",
             platformDetails: {
                 operatingSystem: platform.OperatingSystem.Linux,
                 isOS64Bit: true,
                 isProcess64Bit: true,
             },
             environmentVars: {},
+            // Note that for each given path, we expect:
+            // 1. The path as-is.
+            // 2. Any expected permutations of the path (for example, with a tilde or folder expanded).
+            // 3. The path as-is again (in order for a warning to be displayed at the correct time).
+            // An improvement here would be to check the suppressWarning field, but it's not currently exposed.
             expectedPowerShellSequence: [
                 {
                     exePath: "/home/bin/pwsh",
                     displayName: "pwsh",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "/home/bin/pwsh",
+                    displayName: "pwsh",
+                    supportsProperArguments: true
+                },
+                {
+                    exePath: path.join(os.homedir(), "bin", "pwsh"),
+                    displayName: "pwsh-tilde",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: path.join(os.homedir(), "bin", "pwsh"),
                     displayName: "pwsh-tilde",
@@ -788,6 +843,11 @@ if (process.platform === "win32") {
                     displayName: "pwsh-folder",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "/home/bin/",
+                    displayName: "pwsh-folder",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "/home/bin",
                     displayName: "pwsh-folder-no-slash",
@@ -798,6 +858,16 @@ if (process.platform === "win32") {
                     displayName: "pwsh-folder-no-slash",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "/home/bin",
+                    displayName: "pwsh-folder-no-slash",
+                    supportsProperArguments: true
+                },
+                {
+                    exePath: "/home/bin/pwsh",
+                    displayName: "pwsh-single-quotes",
+                    supportsProperArguments: true
+                },
                 {
                     exePath: "/home/bin/pwsh",
                     displayName: "pwsh-single-quotes",
@@ -808,6 +878,11 @@ if (process.platform === "win32") {
                     displayName: "pwsh-double-quotes",
                     supportsProperArguments: true
                 },
+                {
+                    exePath: "/home/bin/pwsh",
+                    displayName: "pwsh-double-quotes",
+                    supportsProperArguments: true
+                },
             ],
             filesystem: {},
         }

From 3c71fcf08ee2b99d18cc6b44a0f835fc066d121d Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Thu, 21 Nov 2024 12:12:06 -0800
Subject: [PATCH 09/21] Fix additional PowerShell warning (take two)

Since Show Session Menu always fully enumerates the iterator we needed
to move the existence check into the generator. Fortunately the 'exists'
method was already idempotent. I'd like this to be cleaner, but at least
the tests now make sense (and required a stub fix).
---
 src/platform.ts            |  34 ++++--
 test/core/platform.test.ts | 210 ++++++++++---------------------------
 2 files changed, 85 insertions(+), 159 deletions(-)

diff --git a/src/platform.ts b/src/platform.ts
index 706c9f56e2..360fea1613 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -148,7 +148,7 @@ export class PowerShellExeFinder {
 
         // Also show any additionally configured PowerShells
         // These may be duplicates of the default installations, but given a different name.
-        for (const additionalPwsh of this.enumerateAdditionalPowerShellInstallations()) {
+        for await (const additionalPwsh of this.enumerateAdditionalPowerShellInstallations()) {
             if (await additionalPwsh.exists()) {
                 yield additionalPwsh;
             } else if (!additionalPwsh.suppressWarning) {
@@ -230,7 +230,7 @@ export class PowerShellExeFinder {
      * Iterates through the configured additional PowerShell executable locations,
      * without checking for their existence.
      */
-    private *enumerateAdditionalPowerShellInstallations(): Iterable<IPossiblePowerShellExe> {
+    private async *enumerateAdditionalPowerShellInstallations(): AsyncIterable<IPossiblePowerShellExe> {
         for (const versionName in this.additionalPowerShellExes) {
             if (Object.prototype.hasOwnProperty.call(this.additionalPowerShellExes, versionName)) {
                 let exePath: string | undefined = utils.stripQuotePair(this.additionalPowerShellExes[versionName]);
@@ -245,7 +245,11 @@ export class PowerShellExeFinder {
 
                 // Always search for what the user gave us first, but with the warning
                 // suppressed so we can display it after all possibilities are exhausted
-                yield new PossiblePowerShellExe(exePath, ...args);
+                let pwsh = new PossiblePowerShellExe(exePath, ...args);
+                if (await pwsh.exists()) {
+                    yield pwsh;
+                    continue;
+                }
 
                 // Also search for `pwsh[.exe]` and `powershell[.exe]` if missing
                 if (this.platformDetails.operatingSystem === OperatingSystem.Windows) {
@@ -253,16 +257,32 @@ export class PowerShellExeFinder {
                     if (!exePath.endsWith("pwsh.exe") && !exePath.endsWith("powershell.exe")) {
                         if (exePath.endsWith("pwsh") || exePath.endsWith("powershell")) {
                             // Add extension if that was missing
-                            yield new PossiblePowerShellExe(exePath + ".exe", ...args);
+                            pwsh = new PossiblePowerShellExe(exePath + ".exe", ...args);
+                            if (await pwsh.exists()) {
+                                yield pwsh;
+                                continue;
+                            }
                         }
                         // Also add full exe names (this isn't an else just in case
                         // the folder was named "pwsh" or "powershell")
-                        yield new PossiblePowerShellExe(path.join(exePath, "pwsh.exe"), ...args);
-                        yield new PossiblePowerShellExe(path.join(exePath, "powershell.exe"), ...args);
+                        pwsh = new PossiblePowerShellExe(path.join(exePath, "pwsh.exe"), ...args);
+                        if (await pwsh.exists()) {
+                            yield pwsh;
+                            continue;
+                        }
+                        pwsh = new PossiblePowerShellExe(path.join(exePath, "powershell.exe"), ...args);
+                        if (await pwsh.exists()) {
+                            yield pwsh;
+                            continue;
+                        }
                     }
                 } else if (!exePath.endsWith("pwsh")) {
                     // Always just 'pwsh' on non-Windows
-                    yield new PossiblePowerShellExe(path.join(exePath, "pwsh"), ...args);
+                    pwsh = new PossiblePowerShellExe(path.join(exePath, "pwsh"), ...args);
+                    if (await pwsh.exists()) {
+                        yield pwsh;
+                        continue;
+                    }
                 }
 
                 // If we're still being iterated over, no permutation of the given path existed so yield an object with the warning unsuppressed
diff --git a/test/core/platform.test.ts b/test/core/platform.test.ts
index 299e125626..0953379639 100644
--- a/test/core/platform.test.ts
+++ b/test/core/platform.test.ts
@@ -18,10 +18,20 @@ import { stripQuotePair } from "../../src/utils";
 const platformMock = rewire("../../src/platform");
 
 // eslint-disable-next-line @typescript-eslint/require-await
-async function fakeCheckIfFileOrDirectoryExists(targetPath: string | vscode.Uri): Promise<boolean> {
+async function fakeCheckIfFileExists(targetPath: string | vscode.Uri): Promise<boolean> {
     try {
-        fs.lstatSync(targetPath instanceof vscode.Uri ? targetPath.fsPath : targetPath);
-        return true;
+        const stat = fs.lstatSync(targetPath instanceof vscode.Uri ? targetPath.fsPath : targetPath);
+        return stat.isFile();
+    } catch {
+        return false;
+    }
+}
+
+// eslint-disable-next-line @typescript-eslint/require-await
+async function fakeCheckIfDirectoryExists(targetPath: string | vscode.Uri): Promise<boolean> {
+    try {
+        const stat = fs.lstatSync(targetPath instanceof vscode.Uri ? targetPath.fsPath : targetPath);
+        return stat.isDirectory();
     } catch {
         return false;
     }
@@ -33,8 +43,8 @@ async function fakeReadDirectory(targetPath: string | vscode.Uri): Promise<strin
 }
 
 const utilsMock = {
-    checkIfFileExists: fakeCheckIfFileOrDirectoryExists,
-    checkIfDirectoryExists: fakeCheckIfFileOrDirectoryExists,
+    checkIfFileExists: fakeCheckIfFileExists,
+    checkIfDirectoryExists: fakeCheckIfDirectoryExists,
     readDirectory: fakeReadDirectory,
     stripQuotePair: stripQuotePair
 };
@@ -467,18 +477,11 @@ if (process.platform === "win32") {
                 isOS64Bit: true,
                 isProcess64Bit: true,
             },
-            environmentVars: {},
-            // Note that for each given path, we expect:
-            // 1. The path as-is.
-            // 2. Any expected permutations of the path (for example, with a tilde or folder expanded, and/or '.exe' added).
-            // 3. The path as-is again (in order for a warning to be displayed at the correct time).
-            // An improvement here would be to check the suppressWarning field, but it's not currently exposed.
+            environmentVars: {
+                "USERNAME": "test",
+                "USERPROFILE": "C:\\Users\\test",
+            },
             expectedPowerShellSequence: [
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
-                    displayName: "pwsh",
-                    supportsProperArguments: true
-                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh",
@@ -489,86 +492,21 @@ if (process.platform === "win32") {
                     displayName: "pwsh-tilde",
                     supportsProperArguments: true
                 },
-                {
-                    exePath: path.join(os.homedir(), "pwsh", "pwsh.exe"),
-                    displayName: "pwsh-tilde",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh",
-                    displayName: "pwsh-no-exe",
-                    supportsProperArguments: true
-                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh-no-exe",
                     supportsProperArguments: true
                 },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh\\pwsh.exe",
-                    displayName: "pwsh-no-exe",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh\\powershell.exe",
-                    displayName: "pwsh-no-exe",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh",
-                    displayName: "pwsh-no-exe",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\",
-                    displayName: "pwsh-folder",
-                    supportsProperArguments: true
-                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh-folder",
                     supportsProperArguments: true
                 },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\powershell.exe",
-                    displayName: "pwsh-folder",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\",
-                    displayName: "pwsh-folder",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh",
-                    displayName: "pwsh-folder-no-slash",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh.exe",
-                    displayName: "pwsh-folder-no-slash",
-                    supportsProperArguments: true
-                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh-folder-no-slash",
                     supportsProperArguments: true
                 },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\powershell.exe",
-                    displayName: "pwsh-folder-no-slash",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh",
-                    displayName: "pwsh-folder-no-slash",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
-                    displayName: "pwsh-single-quotes",
-                    supportsProperArguments: true
-                },
                 {
                     exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
                     displayName: "pwsh-single-quotes",
@@ -579,13 +517,15 @@ if (process.platform === "win32") {
                     displayName: "pwsh-double-quotes",
                     supportsProperArguments: true
                 },
-                {
-                    exePath: "C:\\Users\\test\\pwsh\\pwsh.exe",
-                    displayName: "pwsh-double-quotes",
-                    supportsProperArguments: true
-                },
             ],
-            filesystem: {},
+            filesystem: {
+                "C:\\Users\\test\\pwsh": {
+                    "pwsh.exe": "",
+                },
+                [path.join(os.homedir(), "pwsh")]: {
+                    "pwsh.exe": "",
+                }
+            },
         }
     ];
 } else {
@@ -693,7 +633,7 @@ if (process.platform === "win32") {
             ],
             filesystem: {
                 "/usr/bin": {
-                    pwsh: "",
+                    "pwsh": "",
                 },
             },
         },
@@ -714,7 +654,7 @@ if (process.platform === "win32") {
             ],
             filesystem: {
                 "/snap/bin": {
-                    pwsh: "",
+                    "pwsh": "",
                 },
             },
         },
@@ -735,7 +675,7 @@ if (process.platform === "win32") {
             ],
             filesystem: {
                 "/usr/local/bin": {
-                    pwsh: "",
+                    "pwsh": "",
                 },
             },
         },
@@ -759,7 +699,7 @@ if (process.platform === "win32") {
             ],
             filesystem: {
                 "/Users/test/.dotnet/tools": {
-                    pwsh: "",
+                    "pwsh": "",
                 },
             },
         },
@@ -783,108 +723,74 @@ if (process.platform === "win32") {
             ],
             filesystem: {
                 "/home/test/.dotnet/tools": {
-                    pwsh: "",
+                    "pwsh": "",
                 },
             },
         },
     ];
 
     additionalPowerShellExes = {
-        "pwsh": "/home/bin/pwsh",
+        "pwsh": "/home/test/bin/pwsh",
         "pwsh-tilde": "~/bin/pwsh",
-        "pwsh-folder": "/home/bin/",
-        "pwsh-folder-no-slash": "/home/bin",
-        "pwsh-single-quotes": "'/home/bin/pwsh'",
-        "pwsh-double-quotes": "\"/home/bin/pwsh\"",
+        "pwsh-folder": "/home/test/bin/",
+        "pwsh-folder-no-slash": "/home/test/bin",
+        "pwsh-single-quotes": "'/home/test/bin/pwsh'",
+        "pwsh-double-quotes": "\"/home/test/bin/pwsh\"",
     };
 
     successAdditionalTestCases = [
-        {   // Also sufficient for macOS as the behavior is the same
+        {
             name: "Linux/macOS (Additional PowerShell Executables)",
             platformDetails: {
                 operatingSystem: platform.OperatingSystem.Linux,
                 isOS64Bit: true,
                 isProcess64Bit: true,
             },
-            environmentVars: {},
-            // Note that for each given path, we expect:
-            // 1. The path as-is.
-            // 2. Any expected permutations of the path (for example, with a tilde or folder expanded).
-            // 3. The path as-is again (in order for a warning to be displayed at the correct time).
-            // An improvement here would be to check the suppressWarning field, but it's not currently exposed.
+            environmentVars: {
+                "USER": "test",
+                "HOME": "/home/test",
+            },
             expectedPowerShellSequence: [
                 {
-                    exePath: "/home/bin/pwsh",
-                    displayName: "pwsh",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/pwsh",
+                    exePath: "/home/test/bin/pwsh",
                     displayName: "pwsh",
                     supportsProperArguments: true
                 },
                 {
+                    // untildify ignores the HOME mock so this is platform-dependent
                     exePath: path.join(os.homedir(), "bin", "pwsh"),
                     displayName: "pwsh-tilde",
                     supportsProperArguments: true
                 },
                 {
-                    exePath: path.join(os.homedir(), "bin", "pwsh"),
-                    displayName: "pwsh-tilde",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/",
+                    exePath: "/home/test/bin/pwsh",
                     displayName: "pwsh-folder",
                     supportsProperArguments: true
                 },
                 {
-                    exePath: "/home/bin/pwsh",
-                    displayName: "pwsh-folder",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/",
-                    displayName: "pwsh-folder",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin",
-                    displayName: "pwsh-folder-no-slash",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/pwsh",
+                    exePath: "/home/test/bin/pwsh",
                     displayName: "pwsh-folder-no-slash",
                     supportsProperArguments: true
                 },
                 {
-                    exePath: "/home/bin",
-                    displayName: "pwsh-folder-no-slash",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/pwsh",
-                    displayName: "pwsh-single-quotes",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/pwsh",
+                    exePath: "/home/test/bin/pwsh",
                     displayName: "pwsh-single-quotes",
                     supportsProperArguments: true
                 },
                 {
-                    exePath: "/home/bin/pwsh",
-                    displayName: "pwsh-double-quotes",
-                    supportsProperArguments: true
-                },
-                {
-                    exePath: "/home/bin/pwsh",
+                    exePath: "/home/test/bin/pwsh",
                     displayName: "pwsh-double-quotes",
                     supportsProperArguments: true
                 },
             ],
-            filesystem: {},
+            filesystem: {
+                "/home/test/bin": {
+                    "pwsh": "",
+                },
+                [path.join(os.homedir(), "bin")]: {
+                    "pwsh": "",
+                }
+            },
         }
     ];
 }
@@ -1099,13 +1005,13 @@ describe("Platform module", function () {
         });
 
         for (const testPlatform of successAdditionalTestCases) {
-            it(`Guesses for ${testPlatform.name}`, function () {
+            it(`Guesses for ${testPlatform.name}`, async function () {
                 setupTestEnvironment(testPlatform);
 
                 const powerShellExeFinder = new platformMock.PowerShellExeFinder(testPlatform.platformDetails, additionalPowerShellExes);
 
                 let i = 0;
-                for (const additionalPwsh of powerShellExeFinder.enumerateAdditionalPowerShellInstallations()) {
+                for await (const additionalPwsh of powerShellExeFinder.enumerateAdditionalPowerShellInstallations()) {
                     const expectedPowerShell = testPlatform.expectedPowerShellSequence[i];
                     i++;
 

From 7aae792fc3516e26ba8152f35fb39b8cb21c58c9 Mon Sep 17 00:00:00 2001
From: Justin Grote <JustinGrote@users.noreply.github.com>
Date: Sat, 16 Nov 2024 17:59:24 -0700
Subject: [PATCH 10/21] Optimize Launch Configs and Enable Hot Reload

---
 .vscode/launch.json               | 127 --------
 .vscode/tasks.json                |  30 --
 docs/development.md               |  15 +-
 extension-dev.code-workspace      | 231 ---------------
 package.json                      |  12 +
 pwsh-extension-dev.code-workspace | 477 ++++++++++++++++++++++++++++++
 src/extension.ts                  |  60 +++-
 src/process.ts                    |  18 +-
 src/session.ts                    |  25 +-
 9 files changed, 599 insertions(+), 396 deletions(-)
 delete mode 100644 .vscode/launch.json
 delete mode 100644 .vscode/tasks.json
 delete mode 100644 extension-dev.code-workspace
 create mode 100644 pwsh-extension-dev.code-workspace

diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index c8375dfe52..0000000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,127 +0,0 @@
-{
-  "version": "0.2.0",
-  "configurations": [
-    // NOTE: These are not in the code-workspace file because StartDebugging cannot current resolve configs stored there so they have to be here for the Mocha Test Explorer feature.
-    // Ref: https://github.com/microsoft/vscode/issues/150663
-    {
-      "name": "Launch Extension",
-      "type": "extensionHost",
-      "request": "launch",
-      "runtimeExecutable": "${execPath}",
-      "args": [
-        "--extensionDevelopmentPath=${workspaceFolder}"
-      ],
-      "env": {
-        "__TEST_WORKSPACE_PATH": "${workspaceFolder}/examples",
-      },
-      "sourceMaps": true,
-      // This speeds up source map detection and makes smartStep work correctly
-      "outFiles": [
-        "${workspaceFolder}/**/*.js",
-        "!**/node_modules/**",
-        "!**/.vscode-test/**"
-      ],
-      "skipFiles": [
-        "<node_internals>/**",
-        "**/node_modules/**",
-        "**/.vscode-test/**"
-      ],
-      "presentation": {
-        "hidden": false,
-        "group": "test",
-        "order": 2
-      }
-    },
-    {
-      // Runs the extension in an empty temp profile that is automatically cleaned up after use
-      // Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
-      "name": "Launch Extension - Temp Profile",
-      "type": "extensionHost",
-      "request": "launch",
-      "runtimeExecutable": "${execPath}",
-      "args": [
-        "--profile-temp",
-        "--extensionDevelopmentPath=${workspaceFolder}",
-        "${workspaceFolder}/examples"
-      ],
-      "sourceMaps": true,
-      // This speeds up source map detection and makes smartStep work correctly
-      "outFiles": [
-        "${workspaceFolder}/**/*.js",
-        "!**/node_modules/**",
-        "!**/.vscode-test/**"
-      ],
-      "skipFiles": [
-        "<node_internals>/**",
-        "**/node_modules/**",
-        "**/.vscode-test/**"
-      ],
-      "presentation": {
-        "hidden": false,
-        "group": "test",
-        "order": 2
-      }
-    },
-    {
-      // Runs the extension in an isolated but persistent profile separate from the user settings
-      // Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
-      "name": "Launch Extension - Isolated Profile",
-      "type": "extensionHost",
-      "request": "launch",
-      "runtimeExecutable": "${execPath}",
-      "args": [
-        "--profile=debug",
-        "--extensionDevelopmentPath=${workspaceFolder}",
-        "${workspaceFolder}/examples"
-      ],
-      "sourceMaps": true,
-      // This speeds up source map detection and makes smartStep work correctly
-      "outFiles": [
-        "${workspaceFolder}/**/*.js",
-        "!**/node_modules/**",
-        "!**/.vscode-test/**"
-      ],
-      "skipFiles": [
-        "<node_internals>/**",
-        "**/node_modules/**",
-        "**/.vscode-test/**"
-      ],
-      "presentation": {
-        "hidden": false,
-        "group": "test",
-        "order": 2
-      }
-    },
-    {
-      "name": "Test Extension",
-      "type": "node",
-      "request": "launch",
-      "program": "${workspaceFolder}/test/runTests.js",
-      "cascadeTerminateToConfigurations": [
-        "ExtensionTests",
-      ],
-      // This speeds up source map detection and makes smartStep work correctly
-      "outFiles": [
-        "${workspaceFolder}/**/*.js",
-        "!**/node_modules/**",
-        "!**/.vscode-test/**"
-      ],
-      "skipFiles": [
-        "<node_internals>/**",
-        "**/node_modules/**",
-        "**/.vscode-test/**"
-      ],
-      "attachSimplePort": 59229, // The default is 9229 but we want to avoid conflicts because we will have two Code instances running.
-      "env": {
-        "__TEST_DEBUG_INSPECT_PORT": "59229" // Needs to match attachSimplePort
-      },
-      "presentation": {
-        "hidden": false,
-      },
-      "internalConsoleOptions": "neverOpen",
-      "console": "integratedTerminal",
-      "autoAttachChildProcesses": false,
-      "preLaunchTask": "watch-tests"
-    }
-  ]
-}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
deleted file mode 100644
index fa53736684..0000000000
--- a/.vscode/tasks.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "version": "2.0.0",
-  "tasks": [
-    {
-      "label": "watch-tests",
-      "icon": {
-        "color": "terminal.ansiCyan",
-        "id": "sync"
-      },
-      "type": "npm",
-      "script": "watch-tests",
-      "group": "test",
-      "problemMatcher": "$tsc-watch",
-      "isBackground": true,
-      "dependsOn": "watch"
-    },
-    {
-      "label": "watch",
-      "icon": {
-        "color": "terminal.ansiCyan",
-        "id": "sync"
-      },
-      "type": "npm",
-      "script": "watch",
-      "group": "build",
-      "problemMatcher": "$esbuild-watch",
-      "isBackground": true
-    }
-  ]
-}
diff --git a/docs/development.md b/docs/development.md
index 28b55750ab..c8e7cae79a 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -56,10 +56,19 @@ Invoke-Build Build
 Explore the `vscode-powershell.build.ps1` file for other build targets.
 
 ### Launching the extension
+First, ensure you have completed a build as instructed above, as the launch templates do not check some prerequisites for performance reasons.
 
-To debug the extension use one of the provided `Launch Extension` debug configurations (remember to rebuild first).
-You can simultaneously use the `Attach to Editor Services` configuration to attach the .NET debugger to the PowerShell process running the server.
-Try the `powershell.developer.editorServicesWaitForDebugger` setting to attach before startup.
+To debug the extension use one of the provided `Launch Extension` debug configurations.
+1. `Launch Extension`: Launches the debugger using your personal profile settings.
+2. `Temp Profile`: Launches VS Code with a temp profile that resets on every launch. Useful for "out of the box" environment testing.
+3. `Isolated Profile`: Launches the debugger with a persistent debug profile specific to the extension, so you can preserve some settings or test certain prerequisites.
+
+All three templates use pre-launch tasks to build the code, and support automatic restart of the extension host on changes to the Extension source code. [Hot Reload](https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/) is also enabled for PowerShell Editor Services.
+
+> [!WARNING]  
+> There is a current limitation that, if you restart the extension/extension host or it is restarted due to a extension code change, the editor services attachment will be disconnected due to the PSES terminal being terminated, and you will either need to restart the debug session completely, or do a manual build of PSES and run the `Attach to Editor Services` debug launch manually.
+
+Try the `powershell.developer.editorServicesWaitForDebugger` setting to ensure that you are fully attached before the extension startup process continues.
 
 ## Contributing Snippets
 
diff --git a/extension-dev.code-workspace b/extension-dev.code-workspace
deleted file mode 100644
index d4e7a6aa4a..0000000000
--- a/extension-dev.code-workspace
+++ /dev/null
@@ -1,231 +0,0 @@
-{
-  "folders": [
-    {
-      "name": "Client",
-      "path": "."
-    },
-    {
-      "name": "Server",
-      "path": "../PowerShellEditorServices"
-    }
-  ],
-  "extensions": {
-    "recommendations": [
-      "davidanson.vscode-markdownlint",
-      "dbaeumer.vscode-eslint",
-      "editorconfig.editorconfig",
-      "josefpihrt-vscode.roslynator",
-      "ms-azure-devops.azure-pipelines",
-      "ms-dotnettools.csharp",
-      "ms-vscode.powershell",
-      "hbenl.vscode-mocha-test-adapter",
-      "connor4312.esbuild-problem-matchers"
-    ]
-  },
-  "settings": {
-    "window.title": "PowerShell VS Code Extension Development",
-    "debug.onTaskErrors": "prompt",
-    "editor.tabSize": 4,
-    "editor.insertSpaces": true,
-    "files.trimTrailingWhitespace": true,
-    "files.insertFinalNewline": true,
-    "files.associations": {
-      "**/snippets/*.json": "jsonc", // Use JSONC instead of JSON because that's how VS Code interprets snippet files, and it enables better source documentation.
-      "**/.vsts-ci/**/*.yml": "azure-pipelines",
-    },
-    // Ignore the Markdown rule:
-    "markdownlint.config": {
-      "MD024": false // no-duplicate-header
-    },
-    "powershell.cwd": "Client",
-    "powershell.codeFormatting.autoCorrectAliases": true,
-    "powershell.codeFormatting.avoidSemicolonsAsLineTerminators": true,
-    "powershell.codeFormatting.newLineAfterCloseBrace": false,
-    "powershell.codeFormatting.trimWhitespaceAroundPipe": true,
-    "powershell.codeFormatting.useCorrectCasing": true,
-    "powershell.codeFormatting.whitespaceBeforeOpenBrace": false,
-    "powershell.codeFormatting.whitespaceBetweenParameters": true,
-    "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline",
-    "typescript.tsdk": "Client/node_modules/typescript/lib", // Lock the TypeScript SDK path to the version we use
-    "typescript.format.semicolons": "insert", // Code actions like "organize imports" ignore ESLint, so we need this here
-    "eslint.format.enable": true, // Enable ESLint as defaut formatter so quick fixes can be applied directly
-    "[typescript]": {
-      "editor.defaultFormatter": "dbaeumer.vscode-eslint",
-      "editor.formatOnPaste": true,
-      "editor.formatOnSave": true,
-      "editor.formatOnSaveMode": "modificationsIfAvailable"
-    },
-    "mochaExplorer.configFile": ".mocharc.json",
-    "mochaExplorer.launcherScript": "test/runTests",
-    "mochaExplorer.autoload": false, // The test instance pops up every time discovery or run is done, this could be annoying on startup.
-    "mochaExplorer.debuggerPort": 59229, // Matches the launch config, we dont want to use the default port as we are launching a duplicate instance of vscode and it might conflict.
-    "mochaExplorer.ipcRole": "server",
-    "mochaExplorer.ipcTimeout": 30000, // 30 seconds
-    "testExplorer.useNativeTesting": true,
-    "mochaExplorer.env": {
-      "VSCODE_VERSION": "insiders",
-      "ELECTRON_RUN_AS_NODE": null
-    }
-  },
-  "tasks": {
-    "version": "2.0.0",
-    "windows": {
-      "options": {
-        "shell": {
-          "executable": "pwsh.exe",
-          "args": [
-            "-NoProfile",
-            "-ExecutionPolicy",
-            "Bypass",
-            "-Command"
-          ]
-        }
-      }
-    },
-    "linux": {
-      "options": {
-        "shell": {
-          "executable": "pwsh",
-          "args": [
-            "-NoProfile",
-            "-Command"
-          ]
-        }
-      }
-    },
-    "osx": {
-      "options": {
-        "shell": {
-          "executable": "/usr/local/bin/pwsh",
-          "args": [
-            "-NoProfile",
-            "-Command"
-          ]
-        }
-      }
-    },
-    "tasks": [
-      {
-        "label": "Build",
-        "type": "shell",
-        "options": {
-          "cwd": "${workspaceFolder:Client}"
-        },
-        "command": "Invoke-Build Build",
-        "problemMatcher": [
-          "$msCompile",
-          "$tsc"
-        ],
-        "group": {
-          "kind": "build",
-          "isDefault": true
-        }
-      },
-      {
-        "label": "Test Client",
-        "type": "shell",
-        "options": {
-          "cwd": "${workspaceFolder:Client}"
-        },
-        "command": "Invoke-Build Test",
-        "problemMatcher": [
-          "$msCompile",
-          "$tsc"
-        ],
-        "group": {
-          "kind": "test",
-          "isDefault": true
-        }
-      },
-      {
-        "label": "Test Server",
-        "type": "shell",
-        "options": {
-          "cwd": "${workspaceFolder:Server}"
-        },
-        "problemMatcher": [
-          "$msCompile"
-        ],
-        "command": "Invoke-Build TestPS74",
-        "group": {
-          "kind": "test",
-          "isDefault": true
-        }
-      },
-      {
-        "label": "Invoke-Build Client",
-        "type": "shell",
-        "options": {
-          "cwd": "${workspaceFolder:Client}"
-        },
-        "command": "Invoke-Build ${input:clientBuildCommand}",
-        "group": "build"
-      },
-      {
-        "label": "Invoke-Build Server",
-        "type": "shell",
-        "options": {
-          "cwd": "${workspaceFolder:Server}"
-        },
-        "command": "Invoke-Build ${input:serverBuildCommand}",
-        "group": "build"
-      }
-    ],
-    "inputs": [
-      {
-        "type": "pickString",
-        "id": "clientBuildCommand",
-        "description": "Which Invoke-Build Client Task?",
-        "options": [
-          "Restore",
-          "Clean",
-          "Build",
-          "Test",
-          "Package"
-        ],
-        "default": "Clean"
-      },
-      {
-        "type": "pickString",
-        "id": "serverBuildCommand",
-        "description": "Which Invoke-Build Server Task?",
-        "options": [
-          "SetupDotNet",
-          "BinClean",
-          "Clean",
-          "Build",
-          "Test",
-          "TestPS74",
-          "TestE2EPwsh",
-          "TestPS51",
-          "TestE2EPowerShell",
-        ],
-        "default": "Clean"
-      }
-    ]
-  },
-  "launch": {
-    "version": "0.2.0",
-    "configurations": [
-      {
-        // https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
-        "name": "Attach to Editor Services",
-        "type": "coreclr",
-        "request": "attach",
-        "processId": "${command:pickProcess}",
-        "justMyCode": false,
-        "suppressJITOptimizations": true,
-        "symbolOptions": {
-          "searchPaths": [],
-          "searchMicrosoftSymbolServer": true,
-          "searchNuGetOrgSymbolServer": true
-        },
-        "presentation": {
-          "hidden": false,
-          "group": "test",
-          "order": 3
-        }
-      }
-    ]
-  }
-}
diff --git a/package.json b/package.json
index bb448383fb..45ef2b467c 100644
--- a/package.json
+++ b/package.json
@@ -300,7 +300,19 @@
         "title": "Move panel to bottom",
         "category": "PowerShell",
         "icon": "$(layout-panel-right)"
+      },
+      {
+        "title": "[PowerShell Debug Input Task] Wait for and return PSES startup PID",
+        "command": "PowerShell.WaitForPsesActivationAndReturnProcessId",
+        "enablement": "false"
+      },
+      {
+        "title": "[PowerShell Debug Input Task] Get the VS Code Session ID for writing the PID file",
+        "command": "GetVsCodeSessionId",
+        "enablement": "false"
       }
+
+
     ],
     "menus": {
       "commandPalette": [
diff --git a/pwsh-extension-dev.code-workspace b/pwsh-extension-dev.code-workspace
new file mode 100644
index 0000000000..9f243c5754
--- /dev/null
+++ b/pwsh-extension-dev.code-workspace
@@ -0,0 +1,477 @@
+{
+  "folders": [
+    {
+      "name": "Client",
+      "path": "."
+    },
+    {
+      "name": "Server",
+      "path": "../PowerShellEditorServices"
+    }
+  ],
+
+  "extensions": {
+    "recommendations": [
+      "davidanson.vscode-markdownlint",
+      "dbaeumer.vscode-eslint",
+      "editorconfig.editorconfig",
+      "ms-dotnettools.csharp",
+      "ms-vscode.powershell",
+      "hbenl.vscode-mocha-test-adapter",
+      "connor4312.esbuild-problem-matchers"
+    ]
+  },
+  "settings": {
+    "window.title": "PowerShell VS Code Extension Development",
+    "debug.onTaskErrors": "prompt",
+    "editor.tabSize": 4,
+    "editor.insertSpaces": true,
+    "files.trimTrailingWhitespace": true,
+    "files.insertFinalNewline": true,
+    "files.associations": {
+      "**/snippets/*.json": "jsonc", // Use JSONC instead of JSON because that's how VS Code interprets snippet files, and it enables better source documentation.
+    },
+    // Ignore the Markdown rule:
+    "markdownlint.config": {
+      "MD024": false // no-duplicate-header
+    },
+    "powershell.cwd": "Client",
+    "powershell.codeFormatting.autoCorrectAliases": true,
+    "powershell.codeFormatting.avoidSemicolonsAsLineTerminators": true,
+    "powershell.codeFormatting.newLineAfterCloseBrace": false,
+    "powershell.codeFormatting.trimWhitespaceAroundPipe": true,
+    "powershell.codeFormatting.useCorrectCasing": true,
+    "powershell.codeFormatting.whitespaceBeforeOpenBrace": false,
+    "powershell.codeFormatting.whitespaceBetweenParameters": true,
+    "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline",
+    "typescript.tsdk": "Client/node_modules/typescript/lib", // Lock the TypeScript SDK path to the version we use
+    "typescript.format.semicolons": "insert", // Code actions like "organize imports" ignore ESLint, so we need this here
+    "eslint.format.enable": true, // Enable ESLint as defaut formatter so quick fixes can be applied directly
+    "[typescript]": {
+      "editor.defaultFormatter": "esbenp.prettier-vscode",
+      "editor.formatOnPaste": true,
+      "editor.formatOnSave": true,
+      "editor.formatOnSaveMode": "modificationsIfAvailable"
+    },
+    "mochaExplorer.configFile": ".mocharc.json",
+    "mochaExplorer.launcherScript": "test/runTests",
+    "mochaExplorer.autoload": false, // The test instance pops up every time discovery or run is done, this could be annoying on startup.
+    "mochaExplorer.debuggerPort": 59229, // Matches the launch config, we dont want to use the default port as we are launching a duplicate instance of vscode and it might conflict.
+    "mochaExplorer.ipcRole": "server",
+    "mochaExplorer.ipcTimeout": 30000, // 30 seconds
+    "testExplorer.useNativeTesting": true,
+    "mochaExplorer.env": {
+      "VSCODE_VERSION": "insiders",
+      "ELECTRON_RUN_AS_NODE": null
+    }
+  },
+  "tasks": {
+    "version": "2.0.0",
+    "windows": {
+      "options": {
+        "shell": {
+          "executable": "pwsh.exe",
+          "args": [
+            "-NoProfile",
+            "-ExecutionPolicy",
+            "Bypass",
+            "-Command"
+          ]
+        }
+      }
+    },
+    "linux": {
+      "options": {
+        "shell": {
+          "executable": "pwsh",
+          "args": [
+            "-NoProfile",
+            "-Command"
+          ]
+        }
+      }
+    },
+    "osx": {
+      "options": {
+        "shell": {
+          "executable": "/usr/local/bin/pwsh",
+          "args": [
+            "-NoProfile",
+            "-Command"
+          ]
+        }
+      }
+    },
+    "tasks": [
+      {
+        "label": "Build",
+        "icon": {
+          "id": "tools",
+        },
+        "type": "shell",
+        "options": {
+          "cwd": "${workspaceFolder:Client}"
+        },
+        "command": "Invoke-Build Build",
+        "problemMatcher": [
+          "$msCompile",
+          "$tsc"
+        ],
+        "group": {
+          "kind": "build",
+          "isDefault": true
+        }
+      },
+      {
+        "label": "Extension: Watch",
+        "type": "shell",
+        "isBackground": true,
+        "options": {
+          "cwd": "${workspaceFolder:Client}"
+        },
+        "command": "npm run watch",
+        "problemMatcher": {
+          "owner": "esbuild-watch",
+          "pattern": {
+            "regexp": "^\\[ERROR\\] (.+)\\n\\n\\s+([^:]+):(\\d+):(\\d+):",
+            "message": 1,
+            "file": 2,
+            "line": 3,
+            "column": 4,
+          },
+          "background": {
+            "activeOnStart": true,
+            "beginsPattern": "^\\[watch\\] build started",
+            "endsPattern": "^\\[watch\\] build finished"
+          }
+        },
+        "icon": {
+          "id": "sync",
+          "color": "terminal.ansiCyan"
+        },
+        "group": {
+          "kind": "build",
+        }
+      },
+      {
+        "label": "PSES: BuildIfChanged",
+        "type": "shell",
+        "options": {
+          "cwd": "${workspaceFolder:Server}"
+        },
+        "command": "Invoke-Build BuildIfChanged",
+        "problemMatcher": "$msCompile",
+        "icon": {
+          "id": "tools",
+          "color": "terminal.ansiCyan"
+        },
+        "group": {
+          "kind": "build",
+        }
+      },
+      {
+        "label": "PreLaunch",
+        "dependsOn": [
+          "Extension: Watch",
+          "PSES: BuildIfChanged"
+        ],
+        "dependsOrder": "parallel"
+      },
+      {
+        "label": "Test Client",
+        "type": "shell",
+        "options": {
+          "cwd": "${workspaceFolder:Client}"
+        },
+        "command": "Invoke-Build Test",
+        "problemMatcher": [
+          "$msCompile",
+          "$tsc"
+        ],
+        "group": {
+          "kind": "test",
+          "isDefault": true
+        }
+      },
+      {
+        "label": "Test Server",
+        "type": "shell",
+        "options": {
+          "cwd": "${workspaceFolder:Server}"
+        },
+        "problemMatcher": [
+          "$msCompile"
+        ],
+        "command": "Invoke-Build TestPS74",
+        "group": {
+          "kind": "test",
+          "isDefault": true
+        }
+      },
+      {
+        "label": "Invoke-Build Client",
+        "type": "shell",
+        "options": {
+          "cwd": "${workspaceFolder:Client}"
+        },
+        "command": "Invoke-Build ${input:clientBuildCommand}",
+        "group": "build"
+      },
+      {
+        "label": "Invoke-Build Server",
+        "type": "shell",
+        "options": {
+          "cwd": "${workspaceFolder:Server}"
+        },
+        "command": "Invoke-Build ${input:serverBuildCommand}",
+        "group": "build"
+      }
+    ],
+    "inputs": [
+      {
+        "type": "pickString",
+        "id": "clientBuildCommand",
+        "description": "Which Invoke-Build Client Task?",
+        "options": [
+          "Restore",
+          "Clean",
+          "Build",
+          "Test",
+          "Package"
+        ],
+        "default": "Clean"
+      },
+      {
+        "type": "pickString",
+        "id": "serverBuildCommand",
+        "description": "Which Invoke-Build Server Task?",
+        "options": [
+          "SetupDotNet",
+          "BinClean",
+          "Clean",
+          "Build",
+          "Test",
+          "TestPS74",
+          "TestE2EPwsh",
+          "TestPS51",
+          "TestE2EPowerShell",
+        ],
+        "default": "Clean"
+      }
+    ],
+
+  },
+  "launch": {
+    "version": "0.2.0",
+    "compounds": [
+      {
+        "name": "Launch Extension",
+        "configurations": [
+          "Launch",
+          "PowerShell Editor Services"
+        ],
+        "preLaunchTask": "Build",
+        "stopAll": true,
+        "presentation": {
+          "hidden": false,
+          "group": "Test",
+          "order": 1
+        }
+      },
+      {
+        "name": "Launch Extension - Temp Profile",
+        "configurations": [
+          "Launch-Temp",
+          "PowerShell Editor Services"
+        ],
+        "preLaunchTask": "PreLaunch",
+        "stopAll": true,
+        "presentation": {
+          "hidden": false,
+          "group": "Test",
+          "order": 2
+        }
+      },
+      {
+        "name": "Launch Extension - Isolated Profile",
+        "configurations": [
+          "Launch-Isolated",
+          "PowerShell Editor Services"
+        ],
+        "preLaunchTask": "Build",
+        "stopAll": true,
+        "presentation": {
+          "hidden": false,
+          "group": "Test",
+          "order": 3
+        }
+      }
+    ],
+    "configurations": [
+      {
+        "name": "Launch",
+        "type": "extensionHost",
+        "request": "launch",
+        "env": {
+          "VSCODE_PARENT_SESSION_ID": "${command:GetVsCodeSessionId}",
+        },
+        "runtimeExecutable": "${execPath}",
+        "args": [
+          "--extensionDevelopmentPath=${workspaceFolder:Client}",
+          "${workspaceFolder:Client}/examples"
+        ],
+        "sourceMaps": true,
+        // This speeds up source map detection and makes smartStep work correctly
+        "outFiles": [
+          "${workspaceFolder:Client}/**/*.js",
+          "!**/node_modules/**",
+          "!**/.vscode-test/**"
+        ],
+        "skipFiles": [
+          "<node_internals>/**",
+          "**/node_modules/**",
+          "**/.vscode-test/**",
+          "**/app/out/vs/**" //Skips Extension Host internals
+        ],
+        "presentation": {
+          "hidden": true
+        }
+      },
+      {
+        "name": "Launch-Temp",
+        "type": "extensionHost",
+        "request": "launch",
+        "env": {
+          "VSCODE_PARENT_SESSION_ID": "${command:GetVsCodeSessionId}",
+        },
+        "runtimeExecutable": "${execPath}",
+        "args": [
+          // Runs the extension in an empty temp profile that is automatically cleaned up after use
+          // Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
+          "--profile-temp",
+          "--extensionDevelopmentPath=${workspaceFolder:Client}",
+          "${workspaceFolder:Client}/examples"
+        ],
+        "sourceMaps": true,
+        // This speeds up source map detection and makes smartStep work correctly
+        "outFiles": [
+          "${workspaceFolder:Client}/**/*.js",
+          "!**/node_modules/**",
+          "!**/.vscode-test/**"
+        ],
+        "skipFiles": [
+          "<node_internals>/**",
+          "**/node_modules/**",
+          "**/.vscode-test/**",
+          "**/app/out/vs/**", // Skips Extension Host internals
+        ],
+        "presentation": {
+          "hidden": true
+        }
+      },
+      {
+        "name": "Launch-Isolated",
+        "type": "extensionHost",
+        "request": "launch",
+        "env": {
+          "VSCODE_PARENT_SESSION_ID": "${command:GetVsCodeSessionId}",
+        },
+        "runtimeExecutable": "${execPath}",
+        "args": [
+          // Runs the extension in an empty temp profile that is automatically cleaned up after use
+          // Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
+          "--profile=pwsh-debug",
+          "--extensionDevelopmentPath=${workspaceFolder:Client}",
+          "${workspaceFolder:Client}/examples"
+        ],
+        "sourceMaps": true,
+        // This speeds up source map detection and makes smartStep work correctly
+        "outFiles": [
+          "${workspaceFolder:Client}/**/*.js",
+          "!**/node_modules/**",
+          "!**/.vscode-test/**",
+        ],
+        "skipFiles": [
+          "<node_internals>/**",
+          "**/node_modules/**",
+          "**/.vscode-test/**",
+          "**/app/out/vs/**" //Skips Extension Host internals
+        ],
+        "presentation": {
+          "hidden": true
+        }
+      },
+      {
+        // https://code.visualstudio.com/docs/csharp/debugger-settings
+        "name": "Attach to Editor Services",
+        "type": "coreclr",
+        "request": "attach",
+        "processId": "${command:PowerShell.PickPSHostProcess}",
+        "justMyCode": true,
+        "suppressJITOptimizations": true,
+        "symbolOptions": {
+          "searchPaths": [],
+          "searchMicrosoftSymbolServer": false,
+          "searchNuGetOrgSymbolServer": false
+        },
+        "presentation": {
+          "hidden": false,
+          "group": "Test",
+          "order": 5
+        },
+        "logging": {
+          "moduleLoad": false
+        },
+      },
+      {
+        // https://code.visualstudio.com/docs/csharp/debugger-settings
+        "name": "PowerShell Editor Services",
+        "type": "coreclr",
+        "request": "attach",
+        // Waits for the extension terminal to become available and gets the PID, saves having to enter it manually.
+        "processId": "${command:PowerShell.WaitForPsesActivationAndReturnProcessId}",
+        "justMyCode": true,
+        "suppressJITOptimizations": true,
+        "symbolOptions": {
+          "searchPaths": [],
+          "searchMicrosoftSymbolServer": false,
+          "searchNuGetOrgSymbolServer": false
+        },
+        "presentation": {
+          "hidden": true
+        },
+        "logging": {
+          "moduleLoad": false
+        }
+      },
+      {
+        // Runs the extension in an isolated but persistent profile separate from the user settings
+        // Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
+        "name": "Launch Extension - Rename Test Cases",
+        "type": "extensionHost",
+        "request": "launch",
+        "runtimeExecutable": "${execPath}",
+        "args": [
+          "--profile=debug",
+          "--extensionDevelopmentPath=${workspaceFolder:Client}",
+          "${workspaceFolder:Server}/test/PowerShellEditorServices.Test.Shared/Refactoring"
+        ],
+        "sourceMaps": true,
+        // This speeds up source map detection and makes smartStep work correctly
+        "outFiles": [
+          "${workspaceFolder:Client}/**/*.js",
+          "!**/node_modules/**",
+          "!**/.vscode-test/**"
+        ],
+        "skipFiles": [
+          "<node_internals>/**",
+          "**/node_modules/**",
+          "**/.vscode-test/**" // Skips Extension Host internals
+        ],
+        "presentation": {
+          "hidden": true,
+        }
+      },
+    ]
+  }
+}
diff --git a/src/extension.ts b/src/extension.ts
index 8676724ab1..80a13657a2 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -25,7 +25,6 @@ import { SessionManager } from "./session";
 import { getSettings } from "./settings";
 import { PowerShellLanguageId } from "./utils";
 import { LanguageClientConsumer } from "./languageClientConsumer";
-
 // The 1DS telemetry key, which is just shared among all Microsoft extensions
 // (and isn't sensitive).
 const TELEMETRY_KEY = "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255";
@@ -44,6 +43,9 @@ const documentSelector: DocumentSelector = [
 
 export async function activate(context: vscode.ExtensionContext): Promise<IPowerShellExtensionClient> {
     logger = new Logger();
+    if (context.extensionMode === vscode.ExtensionMode.Development) {
+        restartOnExtensionFileChanges(context);
+    }
 
     telemetryReporter = new TelemetryReporter(TELEMETRY_KEY);
 
@@ -151,7 +153,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<IPower
         vscode.commands.registerCommand(
             "PowerShell.ShowLogs",
             () => {logger.showLogPanel();}
-        )
+        ),
+        vscode.commands.registerCommand(
+            "GetVsCodeSessionId",
+            () => vscode.env.sessionId
+        ),
+        // Register a command that waits for the Extension Terminal to be active. Can be used by .NET Attach Tasks.
+        registerWaitForPsesActivationCommand(context)
     ];
 
     const externalApi = new ExternalApiFeature(context, sessionManager, logger);
@@ -184,6 +192,54 @@ export async function activate(context: vscode.ExtensionContext): Promise<IPower
     };
 }
 
+/** Registers a command that waits for PSES Activation and returns the PID, used to auto-attach the PSES debugger */
+function registerWaitForPsesActivationCommand(context: vscode.ExtensionContext): vscode.Disposable {
+    return vscode.commands.registerCommand(
+        "PowerShell.WaitForPsesActivationAndReturnProcessId",
+        async () => {
+            const pidFileName = `PSES-${vscode.env.sessionId}.pid`;
+            const pidFile = vscode.Uri.joinPath(context.globalStorageUri, "sessions", pidFileName);
+            const fs = vscode.workspace.fs;
+            // Wait for the file to be created
+            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, no-constant-condition
+            while (true) {
+                try {
+                    const pidContent = await fs.readFile(pidFile);
+                    const pid = parseInt(pidContent.toString(), 10);
+                    try {
+                        // Check if the process is still alive, delete the PID file if not and continue waiting.
+                        // https://nodejs.org/api/process.html#process_process_kill_pid_signal
+                        // "As a special case, a signal of 0 can be used to test for the existence of a process. "
+                        const NODE_TEST_PROCESS_EXISTENCE = 0;
+                        process.kill(pid, NODE_TEST_PROCESS_EXISTENCE);
+                    } catch {
+                        await fs.delete(pidFile);
+                        continue;
+                    }
+                    // VSCode command returns for launch configurations *must* be string type explicitly, will error on number or otherwise.
+                    return pidContent.toString();
+                } catch {
+                    // File doesn't exist yet, wait and try again
+                    await new Promise(resolve => setTimeout(resolve, 1000));
+                }
+            }
+        }
+    );
+}
+
+/** Restarts the extension host when extension file changes are detected. Useful for development. */
+function restartOnExtensionFileChanges(context: vscode.ExtensionContext): void {
+    const watcher = vscode.workspace.createFileSystemWatcher(
+        new vscode.RelativePattern(context.extensionPath, "dist/*.js")
+    );
+
+    context.subscriptions.push(watcher);
+    watcher.onDidChange(({ fsPath }) => {
+        vscode.window.showInformationMessage(`${fsPath.split(context.extensionPath, 2)[1]} changed. Reloading Extension Host...`);
+        vscode.commands.executeCommand("workbench.action.restartExtensionHost");
+    });
+}
+
 export async function deactivate(): Promise<void> {
     // Clean up all extension features
     for (const commandRegistration of commandRegistrations) {
diff --git a/src/process.ts b/src/process.ts
index 052da2b8bf..f99b0cfa1b 100644
--- a/src/process.ts
+++ b/src/process.ts
@@ -23,6 +23,7 @@ export class PowerShellProcess {
     private consoleCloseSubscription?: vscode.Disposable;
 
     private pid?: number;
+    private pidUpdateEmitter?: vscode.EventEmitter<number | undefined>;
 
     constructor(
         public exePath: string,
@@ -33,10 +34,13 @@ export class PowerShellProcess {
         private logDirectoryPath: vscode.Uri,
         private startPsesArgs: string,
         private sessionFilePath: vscode.Uri,
-        private sessionSettings: Settings) {
+        private sessionSettings: Settings,
+        private devMode = false
+    ) {
 
         this.onExitedEmitter = new vscode.EventEmitter<void>();
         this.onExited = this.onExitedEmitter.event;
+        this.pidUpdateEmitter = new vscode.EventEmitter<number | undefined>();
     }
 
     public async start(cancellationToken: vscode.CancellationToken): Promise<IEditorServicesSessionDetails | undefined> {
@@ -103,7 +107,7 @@ export class PowerShellProcess {
 
         // When VS Code shell integration is enabled, the script expects certain
         // variables to be added to the environment.
-        let envMixin = undefined;
+        let envMixin = {};
         if (this.shellIntegrationEnabled) {
             envMixin = {
                 "VSCODE_INJECTION": "1",
@@ -115,6 +119,12 @@ export class PowerShellProcess {
             };
         }
 
+        // Enables Hot Reload in .NET for the attached process
+        // https://devblogs.microsoft.com/devops/net-enc-support-for-lambdas-and-other-improvements-in-visual-studio-2015/
+        if (this.devMode) {
+            (envMixin as Record<string,string>).COMPLUS_FORCEENC = "1";
+        }
+
         // Launch PowerShell in the integrated terminal
         const terminalOptions: vscode.TerminalOptions = {
             name: this.isTemp ? `${PowerShellProcess.title} (TEMP)` : PowerShellProcess.title,
@@ -136,6 +146,7 @@ export class PowerShellProcess {
         this.consoleTerminal = vscode.window.createTerminal(terminalOptions);
         this.pid = await this.getPid();
         this.logger.write(`PowerShell process started with PID: ${this.pid}`);
+        this.pidUpdateEmitter?.fire(this.pid);
 
         if (this.sessionSettings.integratedConsole.showOnStartup
             && !this.sessionSettings.integratedConsole.startInBackground) {
@@ -186,6 +197,9 @@ export class PowerShellProcess {
 
         this.consoleCloseSubscription?.dispose();
         this.consoleCloseSubscription = undefined;
+
+        this.pidUpdateEmitter?.dispose();
+        this.pidUpdateEmitter = undefined;
     }
 
     public sendKeyPress(): void {
diff --git a/src/session.ts b/src/session.ts
index 0b1037a116..34794a5d3e 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -240,6 +240,8 @@ export class SessionManager implements Middleware {
             this.logger.write(`Started PowerShell v${this.versionDetails.version}.`);
             this.setSessionRunningStatus(); // Yay, we made it!
 
+            await this.writePidIfInDevMode(this.languageServerProcess);
+
             // Fire and forget the updater.
             const updater = new UpdatePowerShell(this.sessionSettings, this.logger, this.versionDetails);
             void updater.checkForUpdate();
@@ -303,6 +305,26 @@ export class SessionManager implements Middleware {
         await this.start();
     }
 
+    /** In Development mode, write the PID to a file where the parent session can find it, to attach the dotnet debugger. */
+    private async writePidIfInDevMode(pwshProcess: PowerShellProcess): Promise<void> {
+        if (this.extensionContext.extensionMode !== vscode.ExtensionMode.Development) { return; }
+        const parentSessionId = process.env.VSCODE_PARENT_SESSION_ID;
+        const pidFilePath = vscode.Uri.joinPath(this.sessionsFolder, `PSES-${parentSessionId}.pid`);
+
+        if (parentSessionId === undefined) { return; }
+
+        const fs = vscode.workspace.fs;
+        const pid = (await pwshProcess.getPid())!.toString();
+        await fs.writeFile(pidFilePath, Buffer.from(pid));
+        const deletePidOnExit = pwshProcess.onExited(() => {
+            deletePidOnExit.dispose();
+            fs.delete(pidFilePath, {useTrash: false});
+            console.log(`Deleted PID file: ${pidFilePath}`);
+        });
+        this.registeredCommands.push(deletePidOnExit);
+        this.extensionContext.subscriptions.push(deletePidOnExit);
+    }
+
     public getSessionDetails(): IEditorServicesSessionDetails | undefined {
         // This is used by the debugger which should have already called `start`.
         if (this.sessionDetails === undefined) {
@@ -569,7 +591,8 @@ export class SessionManager implements Middleware {
                 this.extensionContext.logUri,
                 this.getEditorServicesArgs(bundledModulesPath, powerShellExeDetails),
                 this.getNewSessionFilePath(),
-                this.sessionSettings);
+                this.sessionSettings,
+                this.extensionContext.extensionMode == vscode.ExtensionMode.Development);
 
         languageServerProcess.onExited(
             () => {

From 23619a9a4358905a4668b0d6bcacada44fee25a4 Mon Sep 17 00:00:00 2001
From: Justin Grote <JustinGrote@users.noreply.github.com>
Date: Tue, 26 Nov 2024 11:50:28 -0800
Subject: [PATCH 11/21] Implement Extension Setting Categories

---
 package.json | 877 ++++++++++++++++++++++++++-------------------------
 1 file changed, 452 insertions(+), 425 deletions(-)

diff --git a/package.json b/package.json
index 45ef2b467c..7e797ff226 100644
--- a/package.json
+++ b/package.json
@@ -601,437 +601,464 @@
         "initialConfigurations": []
       }
     ],
-    "configuration": {
-      "title": "PowerShell",
-      "properties": {
-        "powershell.sideBar.CommandExplorerVisibility": {
+    "configuration": [
+      {
+        "title": "Interface",
+        "properties": {
+          "powershell.buttons.showRunButtons": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Show the `Run` and `Run Selection` buttons in the editor's title bar."
+          },
+          "powershell.buttons.showPanelMovementButtons": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Show buttons in the editor's title bar for moving the terminals pane (with the PowerShell Extension Terminal) around."
+          },
+          "powershell.enableReferencesCodeLens": {
           "type": "boolean",
-          "default": false,
-          "markdownDescription": "Specifies the visibility of the Command Explorer in the side bar."
-        },
-        "powershell.sideBar.CommandExplorerExcludeFilter": {
-          "type": "array",
-          "items": {
-            "type": "string"
+            "default": true,
+            "markdownDescription": "Specifies if Code Lenses are displayed above function definitions, used to show the number of times the function is referenced in the workspace and navigate to those references. Large workspaces may want to disable this setting if performance is compromised. See also `#powershell.analyzeOpenDocumentsOnly#`."
           },
-          "default": [],
-          "markdownDescription": "Specifies an array of modules to exclude from Command Explorer listing."
-        },
-        "powershell.powerShellAdditionalExePaths": {
-          "type": "object",
-          "default": {},
-          "markdownDescription": "Specifies a list of Item / Value pairs where the **Item** is a user-chosen name and the **Value** is an absolute path to a PowerShell executable. The name appears in the [Session Menu Command](command:PowerShell.ShowSessionMenu) and is used to reference this executable in the `#powershell.powerShellDefaultVersion#` setting.",
-          "additionalProperties": {
-            "type": "string"
+          "powershell.codeFolding.enable": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Enables syntax based code folding. When disabled, the default indentation based code folding is used."
+          },
+          "powershell.codeFolding.showLastLine": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Shows the last line of a folded section similar to the default VS Code folding style. When disabled, the entire folded region is hidden."
+          },
+          "powershell.helpCompletion": {
+            "type": "string",
+            "default": "BlockComment",
+            "enum": [
+              "Disabled",
+              "BlockComment",
+              "LineComment"
+            ],
+            "markdownEnumDescriptions": [
+              "Disables the feature.",
+              "Inserts a block style help comment, for example:\n\n`<#`\n\n`.<help keyword>`\n\n`<help content>`\n\n`#>`",
+              "Inserts a line style help comment, for example:\n\n`# .<help keyword>`\n\n`# <help content>`"
+            ],
+            "markdownDescription": "Specifies the [comment based help](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help) completion style triggered by typing ` ##`."
+          },
+          "powershell.sideBar.CommandExplorerVisibility": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Specifies the visibility of the Command Explorer in the side bar."
+          },
+          "powershell.sideBar.CommandExplorerExcludeFilter": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "default": [],
+            "markdownDescription": "Specifies an array of modules to exclude from Command Explorer listing."
+          },
+          "powershell.promptToUpdatePowerShell": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Specifies whether you may be prompted to update your version of PowerShell."
+          },
+          "powershell.promptToUpdatePackageManagement": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "**Deprecated:** Specifies whether you should be prompted to update your version of `PackageManagement` if it's under 1.4.6.",
+            "markdownDeprecationMessage": "**Deprecated:** This prompt has been removed as it's no longer strictly necessary to upgrade the `PackageManagement` module."
+          },
+          "powershell.suppressAdditionalExeNotFoundWarning": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Suppresses the warning message when any of `#powershell.powerShellAdditionalExePaths#` is not found."
           }
-        },
-        "powershell.powerShellDefaultVersion": {
-          "type": "string",
-          "default": "",
-          "markdownDescription": "Specifies the default PowerShell version started by the extension. The name must match what is displayed in the [Session Menu command](command:PowerShell.ShowSessionMenu), for example, `Windows PowerShell (x86)`. You can specify additional PowerShell executables with the `#powershell.powerShellAdditionalExePaths#` setting."
-        },
-        "powershell.powerShellExePath": {
-          "type": "string",
-          "default": "",
-          "scope": "machine",
-          "markdownDescription": "**Deprecated:** Specifies the path to the PowerShell executable.",
-          "markdownDeprecationMessage": "**Deprecated:** Please use the `#powershell.powerShellAdditionalExePaths#` setting instead."
-        },
-        "powershell.promptToUpdatePowerShell": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Specifies whether you may be prompted to update your version of PowerShell."
-        },
-        "powershell.promptToUpdatePackageManagement": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "**Deprecated:** Specifies whether you should be prompted to update your version of `PackageManagement` if it's under 1.4.6.",
-          "markdownDeprecationMessage": "**Deprecated:** This prompt has been removed as it's no longer strictly necessary to upgrade the `PackageManagement` module."
-        },
-        "powershell.suppressAdditionalExeNotFoundWarning": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Suppresses the warning message when any of `#powershell.powerShellAdditionalExePaths#` is not found."
-        },
-        "powershell.startAsLoginShell.osx": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Starts the PowerShell extension's underlying PowerShell process as a login shell, if applicable."
-        },
-        "powershell.startAsLoginShell.linux": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Starts the PowerShell extension's underlying PowerShell process as a login shell, if applicable."
-        },
-        "powershell.startAutomatically": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Starts the PowerShell extension automatically when a PowerShell file is opened. If `false`, to start the extension use the [Restart Session command](command:PowerShell.RestartSession). **IntelliSense, code navigation, the Extension Terminal, code formatting, and other features are not enabled until the extension starts.**"
-        },
-        "powershell.useX86Host": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "**Deprecated:** Uses the 32-bit language service on 64-bit Windows. This setting has no effect on 32-bit Windows or on the PowerShell extension debugger, which has its own architecture configuration.",
-          "markdownDeprecationMessage": "**Deprecated:** This setting was removed when the PowerShell installation searcher was added. Please use the `#powershell.powerShellAdditionalExePaths#` setting instead."
-        },
-        "powershell.enableProfileLoading": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Specifies whether the extension loads [PowerShell profiles](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles). Note that the extension's \"Current Host\" profile is `Microsoft.VSCode_profile.ps1`, which will be loaded instead of the default \"Current Host\" profile of `Microsoft.PowerShell_profile.ps1`. Use the \"All Hosts\" profile `profile.ps1` for common configuration."
-        },
-        "powershell.enableReferencesCodeLens": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Specifies if Code Lenses are displayed above function definitions, used to show the number of times the function is referenced in the workspace and navigate to those references. Large workspaces may want to disable this setting if performance is compromised. See also `#powershell.analyzeOpenDocumentsOnly#`."
-        },
-        "powershell.analyzeOpenDocumentsOnly": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Specifies to search for references only within open documents instead of all workspace files. An alternative to `#powershell.enableReferencesCodeLens#` that allows large workspaces to support some references without the performance impact."
-        },
-        "powershell.bugReporting.project": {
-          "type": "string",
-          "default": "https://github.com/PowerShell/vscode-powershell",
-          "markdownDescription": "**Deprecated:** Specifies the URL of the GitHub project in which to generate bug reports.",
-          "markdownDeprecationMessage": "**Deprecated:** This setting was never meant to be changed!"
-        },
-        "powershell.helpCompletion": {
-          "type": "string",
-          "default": "BlockComment",
-          "enum": [
-            "Disabled",
-            "BlockComment",
-            "LineComment"
-          ],
-          "markdownEnumDescriptions": [
-            "Disables the feature.",
-            "Inserts a block style help comment, for example:\n\n`<#`\n\n`.<help keyword>`\n\n`<help content>`\n\n`#>`",
-            "Inserts a line style help comment, for example:\n\n`# .<help keyword>`\n\n`# <help content>`"
-          ],
-          "markdownDescription": "Specifies the [comment based help](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help) completion style triggered by typing ` ##`."
-        },
-        "powershell.cwd": {
-          "type": "string",
-          "default": "",
-          "markdownDescription": "A path where the Extension Terminal will be launched. Both the PowerShell process's and the shell's location will be set to this directory. Does not support variables, but does support the use of '~' and paths relative to a single workspace. **For multi-root workspaces, use the name of the folder you wish to have as the cwd.**"
-        },
-        "powershell.scriptAnalysis.enable": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Enables real-time script analysis using [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer) that populates the [Problems view](command:workbench.panel.markers.view.focus)."
-        },
-        "powershell.scriptAnalysis.settingsPath": {
-          "type": "string",
-          "default": "PSScriptAnalyzerSettings.psd1",
-          "markdownDescription": "Specifies the path to a [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer) settings file. **This setting may not work as expected currently!**"
-        },
-        "powershell.codeFolding.enable": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Enables syntax based code folding. When disabled, the default indentation based code folding is used."
-        },
-        "powershell.codeFolding.showLastLine": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Shows the last line of a folded section similar to the default VS Code folding style. When disabled, the entire folded region is hidden."
-        },
-        "powershell.codeFormatting.autoCorrectAliases": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Replaces aliases with their aliased name."
-        },
-        "powershell.codeFormatting.avoidSemicolonsAsLineTerminators": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Removes redundant semicolon(s) at the end of a line where a line terminator is sufficient."
-        },
-        "powershell.codeFormatting.preset": {
-          "type": "string",
-          "default": "Custom",
-          "enum": [
-            "Custom",
-            "Allman",
-            "OTBS",
-            "Stroustrup"
-          ],
-          "markdownEnumDescriptions": [
-            "The three brace settings are respected as-is.",
-            "Sets `#powershell.codeFormatting.openBraceOnSameLine#` to `false`, `#powershell.codeFormatting.newLineAfterOpenBrace#` to `true`, and `#powershell.codeFormatting.newLineAfterCloseBrace#` to `true`.",
-            "Sets `#powershell.codeFormatting.openBraceOnSameLine#` to `true`, `#powershell.codeFormatting.newLineAfterOpenBrace#` to `true`, and `#powershell.codeFormatting.newLineAfterCloseBrace#` to `false`.",
-            "Sets `#powershell.codeFormatting.openBraceOnSameLine#` to `true`, `#powershell.codeFormatting.newLineAfterOpenBrace#` to `true`, and `#powershell.codeFormatting.newLineAfterCloseBrace#` to `true`."
-          ],
-          "markdownDescription": "Sets the code formatting options to follow the given indent style in a way that is compatible with PowerShell syntax. Any setting other than `Custom` will configure (and override) the settings:\n\n* `#powershell.codeFormatting.openBraceOnSameLine#`\n\n* `#powershell.codeFormatting.newLineAfterOpenBrace#`\n\n* `#powershell.codeFormatting.newLineAfterCloseBrace#`\n\nFor more information about the brace styles, please see [PoshCode's discussion](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81)."
-        },
-        "powershell.codeFormatting.openBraceOnSameLine": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Places open brace on the same line as its associated statement."
-        },
-        "powershell.codeFormatting.newLineAfterOpenBrace": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a newline (line break) after an open brace."
-        },
-        "powershell.codeFormatting.newLineAfterCloseBrace": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a newline (line break) after a closing brace."
-        },
-        "powershell.codeFormatting.pipelineIndentationStyle": {
-          "type": "string",
-          "default": "NoIndentation",
-          "enum": [
-            "IncreaseIndentationForFirstPipeline",
-            "IncreaseIndentationAfterEveryPipeline",
-            "NoIndentation",
-            "None"
-          ],
-          "markdownEnumDescriptions": [
-            "Indent once after the first pipeline and keep this indentation.",
-            "Indent more after the first pipeline and keep this indentation.",
-            "Do not increase indentation.",
-            "Do not change any existing pipeline indentation (disables feature)."
-          ],
-          "markdownDescription": "Whether to increase indentation after a pipeline for multi-line statements. See [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer/blob/a94d9f5666bba9f569cdf9c1bc99556934f2b8f4/docs/Rules/UseConsistentIndentation.md#pipelineindentation-string-default-value-is-increaseindentationforfirstpipeline) for examples. It is suggested to use `IncreaseIndentationForFirstPipeline` instead of the default `NoIndentation`. **This default may change in the future,** please see the [Request For Comment](https://github.com/PowerShell/vscode-powershell/issues/4296)."
-        },
-        "powershell.codeFormatting.whitespaceBeforeOpenBrace": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a space between a keyword and its associated script-block expression."
-        },
-        "powershell.codeFormatting.whitespaceBeforeOpenParen": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a space between a keyword (`if`, `elseif`, `while`, `switch`, etc.) and its associated conditional expression."
-        },
-        "powershell.codeFormatting.whitespaceAroundOperator": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds spaces before and after an operator (`=`, `+`, `-`, etc.)."
-        },
-        "powershell.codeFormatting.whitespaceAfterSeparator": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a space after a separator (`,` and `;`)."
-        },
-        "powershell.codeFormatting.whitespaceInsideBrace": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a space after an opening brace (`{`) and before a closing brace (`}`)."
-        },
-        "powershell.codeFormatting.whitespaceBetweenParameters": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Removes redundant whitespace between parameters."
-        },
-        "powershell.codeFormatting.whitespaceAroundPipe": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "**Deprecated:** Please use the `#powershell.codeFormatting.addWhitespaceAroundPipe#` setting instead. If you've used this setting before, we have moved it for you automatically.",
-          "markdownDeprecationMessage": "**Deprecated:** Please use the `#powershell.codeFormatting.addWhitespaceAroundPipe#` setting instead. If you've used this setting before, we have moved it for you automatically."
-        },
-        "powershell.codeFormatting.addWhitespaceAroundPipe": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Adds a space before and after the pipeline operator (`|`) if it is missing."
-        },
-        "powershell.codeFormatting.trimWhitespaceAroundPipe": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Trims extraneous whitespace (more than one character) before and after the pipeline operator (`|`)."
-        },
-        "powershell.codeFormatting.ignoreOneLineBlock": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Does not reformat one-line code blocks, such as: `if (...) {...} else {...}`."
-        },
-        "powershell.codeFormatting.alignPropertyValuePairs": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Align assignment statements in a hashtable or a DSC Configuration."
-        },
-        "powershell.codeFormatting.useConstantStrings": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Use single quotes if a string is not interpolated and its value does not contain a single quote."
-        },
-        "powershell.codeFormatting.useCorrectCasing": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Use correct casing for cmdlets."
-        },
-        "powershell.integratedConsole.showOnStartup": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Shows the Extension Terminal when the PowerShell extension is initialized. When disabled, the pane is not opened on startup, but the Extension Terminal is still created in order to power the extension's features."
-        },
-        "powershell.integratedConsole.startInBackground": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Starts the Extension Terminal in the background. **If this is enabled, to access the terminal you must run the [Show Extension Terminal command](command:PowerShell.ShowSessionConsole), and once shown it cannot be put back into the background.** This option completely hides the Extension Terminal from the terminals view. You are probably looking for the `#powershell.integratedConsole.showOnStartup#` option instead."
-        },
-        "powershell.integratedConsole.startLocation": {
-          "type": "string",
-          "default": "Panel",
-          "enum": [
-            "Editor",
-            "Panel"
-          ],
-          "markdownEnumDescriptions": [
-            "Creates the Extension Terminal in Editor area",
-            "Creates the Extension Terminal in Panel area"
-          ],
-          "markdownDescription": "Sets the startup location for Extension Terminal."
-        },
-        "powershell.integratedConsole.focusConsoleOnExecute": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Switches focus to the console when a script selection is run or a script file is debugged."
-        },
-        "powershell.integratedConsole.useLegacyReadLine": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "This will disable the use of PSReadLine in the PowerShell Extension Terminal and use a legacy implementation. **This setting is not recommended and likely to be deprecated!**"
-        },
-        "powershell.integratedConsole.forceClearScrollbackBuffer": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Use the VS Code API to clear the terminal since that's the only reliable way to clear the scrollback buffer. Turn this on if you're used to `Clear-Host` clearing scroll history. **This setting is not recommended and likely to be deprecated!**"
-        },
-        "powershell.integratedConsole.suppressStartupBanner": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Do not show the startup banner in the PowerShell Extension Terminal."
-        },
-        "powershell.debugging.createTemporaryIntegratedConsole": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Creates a temporary PowerShell Extension Terminal for each debugging session. This is useful for debugging PowerShell classes and binary modules."
-        },
-        "powershell.debugging.executeMode": {
-          "type": "string",
-          "enum": [
-            "DotSource",
-            "Call"
-          ],
-          "default": "DotSource",
-          "markdownEnumDescriptions": [
-            "Use the Dot-Source operator `.` to launch the script, for example, `. 'C:\\Data\\MyScript.ps1'`",
-            "Use the Call operator `&` to launch the script, for example, `& 'C:\\Data\\MyScript.ps1'`"
-          ],
-          "markdownDescription": "Sets the operator used to launch scripts."
-        },
-        "powershell.developer.bundledModulesPath": {
-          "type": "string",
-          "default": "../../PowerShellEditorServices/module",
-          "markdownDescription": "Specifies an alternative path to the folder containing modules that are bundled with the PowerShell extension, that is: PowerShell Editor Services, PSScriptAnalyzer and PSReadLine. **This setting is only meant for extension developers and requires the extension to be run in development mode!**"
-        },
-        "powershell.developer.editorServicesLogLevel": {
-          "type": "string",
-          "default": "Warning",
-          "enum": [
-            "Trace",
-            "Debug",
-            "Information",
-            "Warning",
-            "Error",
-            "None"
-          ],
-          "markdownEnumDescriptions": [
-            "Enables all logging possible, please use this setting when submitting logs for bug reports!",
-            "Enables more detailed logging of the extension",
-            "Logs high-level information about what the extension is doing.",
-            "Only log warnings and errors. This is the default setting",
-            "Only log errors.",
-            "Disable all logging possible. No log files will be written!"
-          ],
-          "markdownDescription": "Sets the log verbosity for both the extension and its LSP server, PowerShell Editor Services. **Please set to `Trace` when recording logs for a bug report!**"
-        },
-        "powershell.developer.editorServicesWaitForDebugger": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Launches the LSP server with the `/waitForDebugger` flag to force it to wait for a .NET debugger to attach before proceeding, and emit its PID until then. **This setting is only meant for extension developers and requires the extension to be run in development mode!**"
-        },
-        "powershell.developer.setExecutionPolicy": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "On Windows we launch the PowerShell executable with `-ExecutionPolicy Bypass` so that the LSP server (PowerShell Editor Services module) will launch without issue. Some anti-virus programs disallow this command-line argument and this flag can be used to remove it. **Using this setting may require trusting the script manually in order for it to launch!**"
-        },
-        "powershell.developer.featureFlags": {
-          "type": "array",
-          "items": {
-            "type": "string"
+        }
+      },
+            {
+        "title": "Formatting",
+        "properties": {
+          "powershell.codeFormatting.preset": {
+            "type": "string",
+            "default": "Custom",
+            "enum": [
+              "Custom",
+              "Allman",
+              "OTBS",
+              "Stroustrup"
+            ],
+            "markdownEnumDescriptions": [
+              "The three brace settings are respected as-is.",
+              "Sets `#powershell.codeFormatting.openBraceOnSameLine#` to `false`, `#powershell.codeFormatting.newLineAfterOpenBrace#` to `true`, and `#powershell.codeFormatting.newLineAfterCloseBrace#` to `true`.",
+              "Sets `#powershell.codeFormatting.openBraceOnSameLine#` to `true`, `#powershell.codeFormatting.newLineAfterOpenBrace#` to `true`, and `#powershell.codeFormatting.newLineAfterCloseBrace#` to `false`.",
+              "Sets `#powershell.codeFormatting.openBraceOnSameLine#` to `true`, `#powershell.codeFormatting.newLineAfterOpenBrace#` to `true`, and `#powershell.codeFormatting.newLineAfterCloseBrace#` to `true`."
+            ],
+            "markdownDescription": "Sets the code formatting options to follow the given indent style in a way that is compatible with PowerShell syntax. Any setting other than `Custom` will configure (and override) the settings:\n\n* `#powershell.codeFormatting.openBraceOnSameLine#`\n\n* `#powershell.codeFormatting.newLineAfterOpenBrace#`\n\n* `#powershell.codeFormatting.newLineAfterCloseBrace#`\n\nFor more information about the brace styles, please see [PoshCode's discussion](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81)."
           },
-          "default": [],
-          "markdownDescription": "An array of strings that enable experimental features in the PowerShell extension. **No flags are currently available!**"
-        },
-        "powershell.developer.traceDap": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Traces the DAP communication between VS Code and the PowerShell Editor Services [DAP Server](https://microsoft.github.io/debug-adapter-protocol/). The output will be logged and also visible in the Output pane, where the verbosity is configurable. **For extension developers and issue troubleshooting only!**"
-        },
-        "powershell.trace.server": {
-          "type": "string",
-          "enum": [
-            "off",
-            "messages",
-            "verbose"
-          ],
-          "default": "off",
-          "markdownDescription": "Traces the communication between VS Code and the PowerShell Editor Services [LSP Server](https://microsoft.github.io/language-server-protocol/). The output will be logged and also visible in the Output pane, where the verbosity is configurable. **For extension developers and issue troubleshooting only!**"
-        },
-        "powershell.developer.waitForSessionFileTimeoutSeconds": {
-          "type": "number",
-          "default": 240,
-          "markdownDescription": "Specifies how many seconds the extension will wait for the LSP server, PowerShell Editor Services, to connect. The default is four minutes; try increasing this value if your computer is particularly slow (often caused by overactive anti-malware programs)."
-        },
-        "powershell.pester.useLegacyCodeLens": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Use a CodeLens that is compatible with Pester 4. Disabling this will show `Run Tests` on all `It`, `Describe` and `Context` blocks, and will correctly work only with Pester 5 and newer."
-        },
-        "powershell.pester.codeLens": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "This setting controls the appearance of the `Run Tests` and `Debug Tests` CodeLenses that appears above Pester tests."
-        },
-        "powershell.pester.outputVerbosity": {
-          "type": "string",
-          "default": "FromPreference",
-          "enum": [
-            "FromPreference",
-            "None",
-            "Minimal",
-            "Normal",
-            "Detailed",
-            "Diagnostic"
-          ],
-          "markdownDescription": "Defines the verbosity of output to be used. For Pester 5 and newer the default value `FromPreference` will use the `Output` settings from the `$PesterPreference` defined in the caller's context, and will default to `Normal` if there is none. For Pester 4 the `FromPreference` and `Normal` options map to `All`, and `Minimal` option maps to `Fails`."
-        },
-        "powershell.pester.debugOutputVerbosity": {
-          "type": "string",
-          "enum": [
-            "None",
-            "Minimal",
-            "Normal",
-            "Detailed",
-            "Diagnostic"
-          ],
-          "default": "Diagnostic",
-          "markdownDescription": "Defines the verbosity of output to be used when debugging a test or a block. For Pester 5 and newer the default value `Diagnostic` will print additional information about discovery, skipped and filtered tests, mocking and more."
-        },
-        "powershell.buttons.showRunButtons": {
-          "type": "boolean",
-          "default": true,
-          "markdownDescription": "Show the `Run` and `Run Selection` buttons in the editor's title bar."
-        },
-        "powershell.buttons.showPanelMovementButtons": {
-          "type": "boolean",
-          "default": false,
-          "markdownDescription": "Show buttons in the editor's title bar for moving the terminals pane (with the PowerShell Extension Terminal) around."
+          "powershell.codeFormatting.autoCorrectAliases": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Replaces aliases with their aliased name."
+          },
+          "powershell.codeFormatting.avoidSemicolonsAsLineTerminators": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Removes redundant semicolon(s) at the end of a line where a line terminator is sufficient."
+          },
+          "powershell.codeFormatting.openBraceOnSameLine": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Places open brace on the same line as its associated statement."
+          },
+          "powershell.codeFormatting.newLineAfterOpenBrace": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a newline (line break) after an open brace."
+          },
+          "powershell.codeFormatting.newLineAfterCloseBrace": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a newline (line break) after a closing brace."
+          },
+          "powershell.codeFormatting.pipelineIndentationStyle": {
+            "type": "string",
+            "default": "NoIndentation",
+            "enum": [
+              "IncreaseIndentationForFirstPipeline",
+              "IncreaseIndentationAfterEveryPipeline",
+              "NoIndentation",
+              "None"
+            ],
+            "markdownEnumDescriptions": [
+              "Indent once after the first pipeline and keep this indentation.",
+              "Indent more after the first pipeline and keep this indentation.",
+              "Do not increase indentation.",
+              "Do not change any existing pipeline indentation (disables feature)."
+            ],
+            "markdownDescription": "Whether to increase indentation after a pipeline for multi-line statements. See [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer/blob/a94d9f5666bba9f569cdf9c1bc99556934f2b8f4/docs/Rules/UseConsistentIndentation.md#pipelineindentation-string-default-value-is-increaseindentationforfirstpipeline) for examples. It is suggested to use `IncreaseIndentationForFirstPipeline` instead of the default `NoIndentation`. **This default may change in the future,** please see the [Request For Comment](https://github.com/PowerShell/vscode-powershell/issues/4296)."
+          },
+          "powershell.codeFormatting.whitespaceBeforeOpenBrace": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a space between a keyword and its associated script-block expression."
+          },
+          "powershell.codeFormatting.whitespaceBeforeOpenParen": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a space between a keyword (`if`, `elseif`, `while`, `switch`, etc.) and its associated conditional expression."
+          },
+          "powershell.codeFormatting.whitespaceAroundOperator": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds spaces before and after an operator (`=`, `+`, `-`, etc.)."
+          },
+          "powershell.codeFormatting.whitespaceAfterSeparator": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a space after a separator (`,` and `;`)."
+          },
+          "powershell.codeFormatting.whitespaceInsideBrace": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a space after an opening brace (`{`) and before a closing brace (`}`)."
+          },
+          "powershell.codeFormatting.whitespaceBetweenParameters": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Removes redundant whitespace between parameters."
+          },
+          "powershell.codeFormatting.whitespaceAroundPipe": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "**Deprecated:** Please use the `#powershell.codeFormatting.addWhitespaceAroundPipe#` setting instead. If you've used this setting before, we have moved it for you automatically.",
+            "markdownDeprecationMessage": "**Deprecated:** Please use the `#powershell.codeFormatting.addWhitespaceAroundPipe#` setting instead. If you've used this setting before, we have moved it for you automatically."
+          },
+          "powershell.codeFormatting.addWhitespaceAroundPipe": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Adds a space before and after the pipeline operator (`|`) if it is missing."
+          },
+          "powershell.codeFormatting.trimWhitespaceAroundPipe": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Trims extraneous whitespace (more than one character) before and after the pipeline operator (`|`)."
+          },
+          "powershell.codeFormatting.ignoreOneLineBlock": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Does not reformat one-line code blocks, such as: `if (...) {...} else {...}`."
+          },
+          "powershell.codeFormatting.alignPropertyValuePairs": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Align assignment statements in a hashtable or a DSC Configuration."
+          },
+          "powershell.codeFormatting.useConstantStrings": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Use single quotes if a string is not interpolated and its value does not contain a single quote."
+          },
+          "powershell.codeFormatting.useCorrectCasing": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Use correct casing for cmdlets."
+          }
+        }
+      },
+      {
+        "title": "Editor Services",
+        "properties": {
+          "powershell.powerShellDefaultVersion": {
+            "type": "string",
+            "default": "",
+            "markdownDescription": "Specifies the default PowerShell version started by the extension. The name must match what is displayed in the [Session Menu command](command:PowerShell.ShowSessionMenu), for example, `Windows PowerShell (x86)`. You can specify additional PowerShell executables with the `#powershell.powerShellAdditionalExePaths#` setting."
+          },
+          "powershell.enableProfileLoading": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Specifies whether the extension loads [PowerShell profiles](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles). Note that the extension's \"Current Host\" profile is `Microsoft.VSCode_profile.ps1`, which will be loaded instead of the default \"Current Host\" profile of `Microsoft.PowerShell_profile.ps1`. Use the \"All Hosts\" profile `profile.ps1` for common configuration."
+          },
+          "powershell.startAutomatically": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Starts the PowerShell extension automatically when a PowerShell file is opened. If `false`, to start the extension use the [Restart Session command](command:PowerShell.RestartSession). **IntelliSense, code navigation, the Extension Terminal, code formatting, and other features are not enabled until the extension starts.**"
+          },
+          "powershell.scriptAnalysis.enable": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Enables real-time script analysis using [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer) that populates the [Problems view](command:workbench.panel.markers.view.focus)."
+          },
+          "powershell.scriptAnalysis.settingsPath": {
+            "type": "string",
+            "default": "PSScriptAnalyzerSettings.psd1",
+            "markdownDescription": "Specifies the path to a [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer) settings file. **This setting may not work as expected currently!**"
+          },
+          "powershell.analyzeOpenDocumentsOnly": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Specifies to search for references only within open documents instead of all workspace files. An alternative to `#powershell.enableReferencesCodeLens#` that allows large workspaces to support some references without the performance impact."
+          },
+          "powershell.debugging.createTemporaryIntegratedConsole": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Creates a temporary PowerShell Extension Terminal for each debugging session. This is useful for debugging PowerShell classes and binary modules."
+          },
+          "powershell.debugging.executeMode": {
+            "type": "string",
+            "enum": [
+              "DotSource",
+              "Call"
+            ],
+            "default": "DotSource",
+            "markdownEnumDescriptions": [
+              "Use the Dot-Source operator `.` to launch the script, for example, `. 'C:\\Data\\MyScript.ps1'`",
+              "Use the Call operator `&` to launch the script, for example, `& 'C:\\Data\\MyScript.ps1'`"
+            ],
+            "markdownDescription": "Sets the operator used to launch scripts."
+          },
+          "powershell.powerShellExePath": {
+            "type": "string",
+            "default": "",
+            "scope": "machine",
+            "markdownDescription": "**Deprecated:** Specifies the path to the PowerShell executable.",
+            "markdownDeprecationMessage": "**Deprecated:** Please use the `#powershell.powerShellAdditionalExePaths#` setting instead."
+          },
+          "powershell.powerShellAdditionalExePaths": {
+            "type": "object",
+            "default": {},
+            "markdownDescription": "Specifies a list of Item / Value pairs where the **Item** is a user-chosen name and the **Value** is an absolute path to a PowerShell executable. The name appears in the [Session Menu Command](command:PowerShell.ShowSessionMenu) and is used to reference this executable in the `#powershell.powerShellDefaultVersion#` setting.",
+            "additionalProperties": {
+              "type": "string"
+            }
+          },
+          "powershell.cwd": {
+            "type": "string",
+            "default": "",
+            "markdownDescription": "A path where the Extension Terminal will be launched. Both the PowerShell process's and the shell's location will be set to this directory. Does not support variables, but does support the use of '~' and paths relative to a single workspace. **For multi-root workspaces, use the name of the folder you wish to have as the cwd.**"
+          },
+          "powershell.startAsLoginShell.osx": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Starts the PowerShell extension's underlying PowerShell process as a login shell, if applicable."
+          },
+          "powershell.startAsLoginShell.linux": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Starts the PowerShell extension's underlying PowerShell process as a login shell, if applicable."
+          },
+          "powershell.useX86Host": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "**Deprecated:** Uses the 32-bit language service on 64-bit Windows. This setting has no effect on 32-bit Windows or on the PowerShell extension debugger, which has its own architecture configuration.",
+            "markdownDeprecationMessage": "**Deprecated:** This setting was removed when the PowerShell installation searcher was added. Please use the `#powershell.powerShellAdditionalExePaths#` setting instead."
+          }
+        }
+      },
+            {
+        "title": "Pester",
+        "properties": {
+          "powershell.pester.useLegacyCodeLens": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Use a CodeLens that is compatible with Pester 4. Disabling this will show `Run Tests` on all `It`, `Describe` and `Context` blocks, and will correctly work only with Pester 5 and newer."
+          },
+          "powershell.pester.codeLens": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "This setting controls the appearance of the `Run Tests` and `Debug Tests` CodeLenses that appears above Pester tests."
+          },
+          "powershell.pester.outputVerbosity": {
+            "type": "string",
+            "default": "FromPreference",
+            "enum": [
+              "FromPreference",
+              "None",
+              "Minimal",
+              "Normal",
+              "Detailed",
+              "Diagnostic"
+            ],
+            "markdownDescription": "Defines the verbosity of output to be used. For Pester 5 and newer the default value `FromPreference` will use the `Output` settings from the `$PesterPreference` defined in the caller's context, and will default to `Normal` if there is none. For Pester 4 the `FromPreference` and `Normal` options map to `All`, and `Minimal` option maps to `Fails`."
+          },
+          "powershell.pester.debugOutputVerbosity": {
+            "type": "string",
+            "enum": [
+              "None",
+              "Minimal",
+              "Normal",
+              "Detailed",
+              "Diagnostic"
+            ],
+            "default": "Diagnostic",
+            "markdownDescription": "Defines the verbosity of output to be used when debugging a test or a block. For Pester 5 and newer the default value `Diagnostic` will print additional information about discovery, skipped and filtered tests, mocking and more."
+          }
+        }
+      },
+      {
+        "title": "Terminal",
+        "properties": {
+          "powershell.integratedConsole.suppressStartupBanner": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Do not show the startup banner in the PowerShell Extension Terminal."
+          },
+          "powershell.integratedConsole.showOnStartup": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Shows the Extension Terminal when the PowerShell extension is initialized. When disabled, the pane is not opened on startup, but the Extension Terminal is still created in order to power the extension's features."
+          },
+          "powershell.integratedConsole.startInBackground": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Starts the Extension Terminal in the background. **If this is enabled, to access the terminal you must run the [Show Extension Terminal command](command:PowerShell.ShowSessionConsole), and once shown it cannot be put back into the background.** This option completely hides the Extension Terminal from the terminals view. You are probably looking for the `#powershell.integratedConsole.showOnStartup#` option instead."
+          },
+          "powershell.integratedConsole.startLocation": {
+            "type": "string",
+            "default": "Panel",
+            "enum": [
+              "Editor",
+              "Panel"
+            ],
+            "markdownEnumDescriptions": [
+              "Creates the Extension Terminal in Editor area",
+              "Creates the Extension Terminal in Panel area"
+            ],
+            "markdownDescription": "Sets the startup location for Extension Terminal."
+          },
+          "powershell.integratedConsole.focusConsoleOnExecute": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "Switches focus to the console when a script selection is run or a script file is debugged."
+          },
+          "powershell.integratedConsole.useLegacyReadLine": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "This will disable the use of PSReadLine in the PowerShell Extension Terminal and use a legacy implementation. **This setting is not recommended and likely to be deprecated!**"
+          },
+          "powershell.integratedConsole.forceClearScrollbackBuffer": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Use the VS Code API to clear the terminal since that's the only reliable way to clear the scrollback buffer. Turn this on if you're used to `Clear-Host` clearing scroll history. **This setting is not recommended and likely to be deprecated!**"
+          }
+        }
+      },
+      {
+        "title": "Developer",
+        "properties": {
+          "powershell.developer.editorServicesLogLevel": {
+            "type": "string",
+            "default": "Warning",
+            "enum": [
+              "Trace",
+              "Debug",
+              "Information",
+              "Warning",
+              "Error",
+              "None"
+            ],
+            "markdownEnumDescriptions": [
+              "Enables all logging possible, please use this setting when submitting logs for bug reports!",
+              "Enables more detailed logging of the extension",
+              "Logs high-level information about what the extension is doing.",
+              "Only log warnings and errors. This is the default setting",
+              "Only log errors.",
+              "Disable all logging possible. No log files will be written!"
+            ],
+            "markdownDescription": "Sets the log verbosity for both the extension and its LSP server, PowerShell Editor Services. **Please set to `Trace` when recording logs for a bug report!**"
+          },
+          "powershell.trace.server": {
+            "type": "string",
+            "enum": [
+              "off",
+              "messages",
+              "verbose"
+            ],
+            "default": "off",
+            "markdownDescription": "Traces the communication between VS Code and the PowerShell Editor Services [LSP Server](https://microsoft.github.io/language-server-protocol/). The output will be logged and also visible in the Output pane, where the verbosity is configurable. **For extension developers and issue troubleshooting only!**"
+          },
+          "powershell.developer.traceDap": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Traces the DAP communication between VS Code and the PowerShell Editor Services [DAP Server](https://microsoft.github.io/debug-adapter-protocol/). The output will be logged and also visible in the Output pane, where the verbosity is configurable. **For extension developers and issue troubleshooting only!**"
+          },
+          "powershell.developer.editorServicesWaitForDebugger": {
+            "type": "boolean",
+            "default": false,
+            "markdownDescription": "Launches the LSP server with the `/waitForDebugger` flag to force it to wait for a .NET debugger to attach before proceeding, and emit its PID until then. **This setting is only meant for extension developers and requires the extension to be run in development mode!**"
+          },
+          "powershell.developer.setExecutionPolicy": {
+            "type": "boolean",
+            "default": true,
+            "markdownDescription": "On Windows we launch the PowerShell executable with `-ExecutionPolicy Bypass` so that the LSP server (PowerShell Editor Services module) will launch without issue. Some anti-virus programs disallow this command-line argument and this flag can be used to remove it. **Using this setting may require trusting the script manually in order for it to launch!**"
+          },
+          "powershell.developer.bundledModulesPath": {
+            "type": "string",
+            "default": "../../PowerShellEditorServices/module",
+            "markdownDescription": "Specifies an alternative path to the folder containing modules that are bundled with the PowerShell extension, that is: PowerShell Editor Services, PSScriptAnalyzer and PSReadLine. **This setting is only meant for extension developers and requires the extension to be run in development mode!**"
+          },
+          "powershell.developer.featureFlags": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "default": [],
+            "markdownDescription": "An array of strings that enable experimental features in the PowerShell extension. **No flags are currently available!**"
+          },
+          "powershell.developer.waitForSessionFileTimeoutSeconds": {
+            "type": "number",
+            "default": 240,
+            "markdownDescription": "Specifies how many seconds the extension will wait for the LSP server, PowerShell Editor Services, to connect. The default is four minutes; try increasing this value if your computer is particularly slow (often caused by overactive anti-malware programs)."
+          },
+          "powershell.bugReporting.project": {
+            "type": "string",
+            "default": "https://github.com/PowerShell/vscode-powershell",
+            "markdownDescription": "**Deprecated:** Specifies the URL of the GitHub project in which to generate bug reports.",
+            "markdownDeprecationMessage": "**Deprecated:** This setting was never meant to be changed!"
+          }
         }
       }
-    },
+    ],
     "capabilities": {
       "untrustedWorkspaces": {
         "supported": false

From d1a89fab21ea85b08ab19a73cbb3643aec1241f9 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 27 Nov 2024 15:01:42 -0800
Subject: [PATCH 12/21] Add Prettier extension to recommendations

Since it's set as the TypeScript formatter.
---
 pwsh-extension-dev.code-workspace | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pwsh-extension-dev.code-workspace b/pwsh-extension-dev.code-workspace
index 9f243c5754..51083c21ad 100644
--- a/pwsh-extension-dev.code-workspace
+++ b/pwsh-extension-dev.code-workspace
@@ -15,6 +15,7 @@
       "davidanson.vscode-markdownlint",
       "dbaeumer.vscode-eslint",
       "editorconfig.editorconfig",
+      "esbenp.prettier-vscode",
       "ms-dotnettools.csharp",
       "ms-vscode.powershell",
       "hbenl.vscode-mocha-test-adapter",

From 1aa99c29a86047b7f54bd1e1422d0bdbdd6fd662 Mon Sep 17 00:00:00 2001
From: "Jon D." <dunham.jon@gmail.com>
Date: Tue, 3 Dec 2024 16:42:16 -0600
Subject: [PATCH 13/21] Fix for "suppress psscriptanalyzer rule" snippets
 (#5110)

Fix for issue #5108

- Corrects a missing close comment in the Function rule snippet
- Corrects the tab stop numbering in the Parameter rule snippet as well as duplicate use of `$TM_SELECTED_TEXT`
- Adds a missing comma in The Scope rule snippet
- Minor formatting
---
 snippets/PowerShell.json | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/snippets/PowerShell.json b/snippets/PowerShell.json
index 5e25f10286..e5b23be643 100644
--- a/snippets/PowerShell.json
+++ b/snippets/PowerShell.json
@@ -268,8 +268,9 @@
     "description": "Suppress a PSScriptAnalyzer rule for a function. More: https://docs.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/overview?view=ps-modules#suppressing-rules",
     "body": [
       "[Diagnostics.CodeAnalysis.SuppressMessageAttribute(",
-      "\t<#Category#>'${1:PSProvideDefaultParameterValue}', <#CheckId>\\$null, Scope='Function',",
-      "\tJustification = '${0:${TM_SELECTED_TEXT:Reason for suppressing}}'",
+      "\t<#Category#>'${1:PSProvideDefaultParameterValue}', <#CheckId#>\\$null,",
+      "\tScope='Function',",
+      "\tJustification='${0:${TM_SELECTED_TEXT:Reason for suppressing}}'",
       ")]"
     ]
   },
@@ -522,9 +523,10 @@
     ],
     "description": "Suppress a PSScriptAnalyzer rule on a parameter. More: https://docs.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/overview?view=ps-modules#suppressing-rules",
     "body": [
-      "[Diagnostics.CodeAnalysis.SuppressMessageAttribute(<#Category#>'${1:PSUseDeclaredVarsMoreThanAssignments}',",
-      "\t<#ParameterName#>'${0:${TM_SELECTED_TEXT:ParamName}}',",
-      "\tJustification = '${0:${TM_SELECTED_TEXT:Reason for suppressing}}'",
+      "[Diagnostics.CodeAnalysis.SuppressMessageAttribute(",
+      "\t<#Category#>'${1:PSUseDeclaredVarsMoreThanAssignments}',",
+      "\t<#ParameterName#>'${2:${TM_SELECTED_TEXT:ParamName}}',",
+      "\tJustification='${0:Reason for suppressing}'",
       ")]"
     ]
   },
@@ -566,13 +568,17 @@
     ]
   },
   "Scope: Suppress PSScriptAnalyzer Rule": {
-    "prefix": "suppress-message-rule-scope",
+    "prefix": [
+      "suppress-message-rule-scope",
+      "[SuppressMessageAttribute]"
+    ],
     "description": "Suppress a PSScriptAnalyzer rule based on a function/parameter/class/variable/object's name by setting the SuppressMessageAttribute's Target property to a regular expression or a glob pattern. More: https://docs.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/overview?view=ps-modules#suppressing-rules",
     "body": [
       "[Diagnostics.CodeAnalysis.SuppressMessageAttribute(",
-      "\t<#Category#>'${1:PSUseDeclaredVarsMoreThanAssignments}', <#CheckId#>\\$null, Scope='Function',",
-      "\tTarget='${1:${TM_SELECTED_TEXT:RegexOrGlobPatternToMatchName}}'",
-      "\tJustification = '${0:Reason for suppressing}}'",
+      "\t<#Category#>'${1:PSUseDeclaredVarsMoreThanAssignments}', <#CheckId#>\\$null,",
+      "\tScope='${2|Function,Parameter,Class,Variable,Object|}',",
+      "\tTarget='${3:${TM_SELECTED_TEXT:RegexOrGlobPatternToMatchName}}',",
+      "\tJustification='${0:Reason for suppressing}'",
       ")]"
     ]
   },

From c101d9797b1ad5ad17d35b717e53acf0ce4daadd Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 4 Dec 2024 15:15:19 -0800
Subject: [PATCH 14/21] Bump packages

---
 package-lock.json | 167 ++++++++++++++++++++++++++--------------------
 package.json      |  20 +++---
 2 files changed, 105 insertions(+), 82 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 5ccbb484da..82c2bd2da9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,15 +1,15 @@
 {
   "name": "powershell",
-  "version": "2024.4.0",
+  "version": "2024.5.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "powershell",
-      "version": "2024.4.0",
+      "version": "2024.5.1",
       "license": "SEE LICENSE IN LICENSE.txt",
       "dependencies": {
-        "@vscode/extension-telemetry": "^0.9.7",
+        "@vscode/extension-telemetry": "^0.9.8",
         "node-fetch": "^2.7.0",
         "semver": "^7.6.3",
         "untildify": "^4.0.0",
@@ -25,9 +25,9 @@
         "vscode": "^1.94.0"
       },
       "optionalDependencies": {
-        "@types/mocha": "^10.0.9",
+        "@types/mocha": "^10.0.10",
         "@types/mock-fs": "^4.13.4",
-        "@types/node": "^20.17.6",
+        "@types/node": "^20.17.9",
         "@types/node-fetch": "^2.6.12",
         "@types/rewire": "^2.5.30",
         "@types/semver": "^7.5.8",
@@ -35,8 +35,8 @@
         "@types/ungap__structured-clone": "^1.2.0",
         "@types/uuid": "^9.0.8",
         "@types/vscode": "~1.94.0",
-        "@typescript-eslint/eslint-plugin": "^8.14.0",
-        "@typescript-eslint/parser": "^8.14.0",
+        "@typescript-eslint/eslint-plugin": "^8.17.0",
+        "@typescript-eslint/parser": "^8.17.0",
         "@ungap/structured-clone": "^1.2.0",
         "@vscode/debugprotocol": "^1.68.0",
         "@vscode/test-electron": "^2.4.1",
@@ -50,7 +50,7 @@
         "rewire": "^7.0.0",
         "sinon": "^18.0.1",
         "source-map-support": "^0.5.21",
-        "typescript": "^5.6.3"
+        "typescript": "^5.7.2"
       }
     },
     "node_modules/@azure/abort-controller": {
@@ -95,8 +95,8 @@
       }
     },
     "node_modules/@azure/core-rest-pipeline": {
-      "version": "1.18.0",
-      "integrity": "sha1-Fl8c2bsQYL47aJV0LbPR8RBicdM=",
+      "version": "1.18.1",
+      "integrity": "sha1-OA59PxW+gN6D7kFBdq2zKCRALzg=",
       "dev": true,
       "dependencies": {
         "@azure/abort-controller": "^2.0.0",
@@ -190,8 +190,8 @@
       }
     },
     "node_modules/@azure/msal-node": {
-      "version": "2.16.1",
-      "integrity": "sha1-iYKIMujmyKiM7MTvbY1OQ1IRa3c=",
+      "version": "2.16.2",
+      "integrity": "sha1-Prdo02iD6m+ak5wLW0Z7UY54//w=",
       "dev": true,
       "dependencies": {
         "@azure/msal-common": "14.16.0",
@@ -897,8 +897,8 @@
       "optional": true
     },
     "node_modules/@types/mocha": {
-      "version": "10.0.9",
-      "integrity": "sha1-EB6dqI0sAuWsiVKYLCOyJFJNZio=",
+      "version": "10.0.10",
+      "integrity": "sha1-kfYpBejSPL1mIlMS8jlFSiO+v6A=",
       "optional": true
     },
     "node_modules/@types/mock-fs": {
@@ -910,8 +910,8 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "20.17.6",
-      "integrity": "sha1-bkBzIwwYDTV56MYBQfme/fXfAIE=",
+      "version": "20.17.9",
+      "integrity": "sha1-XxQdS37hJc3uX67+KN4JU5iGW6s=",
       "optional": true,
       "dependencies": {
         "undici-types": "~6.19.2"
@@ -965,15 +965,15 @@
       "optional": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.14.0",
-      "integrity": "sha1-fcDkGch76tyPVUv1pC5QCe03SNw=",
+      "version": "8.17.0",
+      "integrity": "sha1-LuBzxCH06B4C0Q5zEkFmS2JTsjw=",
       "optional": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.14.0",
-        "@typescript-eslint/type-utils": "8.14.0",
-        "@typescript-eslint/utils": "8.14.0",
-        "@typescript-eslint/visitor-keys": "8.14.0",
+        "@typescript-eslint/scope-manager": "8.17.0",
+        "@typescript-eslint/type-utils": "8.17.0",
+        "@typescript-eslint/utils": "8.17.0",
+        "@typescript-eslint/visitor-keys": "8.17.0",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
@@ -997,14 +997,14 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.14.0",
-      "integrity": "sha1-Cn6dvBG8B3FqstexImIX6fa1H8g=",
+      "version": "8.17.0",
+      "integrity": "sha1-LulyuxL6aaxiW4WBPcjZpaBT/1I=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.14.0",
-        "@typescript-eslint/types": "8.14.0",
-        "@typescript-eslint/typescript-estree": "8.14.0",
-        "@typescript-eslint/visitor-keys": "8.14.0",
+        "@typescript-eslint/scope-manager": "8.17.0",
+        "@typescript-eslint/types": "8.17.0",
+        "@typescript-eslint/typescript-estree": "8.17.0",
+        "@typescript-eslint/visitor-keys": "8.17.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1024,12 +1024,12 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.14.0",
-      "integrity": "sha1-AfN8FHpzXNePD/NV4DO5RX2h83M=",
+      "version": "8.17.0",
+      "integrity": "sha1-o/Sb89TSf/jWsuoJm6Rl7028qjo=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.14.0",
-        "@typescript-eslint/visitor-keys": "8.14.0"
+        "@typescript-eslint/types": "8.17.0",
+        "@typescript-eslint/visitor-keys": "8.17.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1040,12 +1040,12 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.14.0",
-      "integrity": "sha1-RVxq8wwzayShryi8T4G43V102U0=",
+      "version": "8.17.0",
+      "integrity": "sha1-0yZWn0mM3Q7fWNW7YDC0rZFOY9M=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.14.0",
-        "@typescript-eslint/utils": "8.14.0",
+        "@typescript-eslint/typescript-estree": "8.17.0",
+        "@typescript-eslint/utils": "8.17.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -1056,6 +1056,9 @@
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0"
+      },
       "peerDependenciesMeta": {
         "typescript": {
           "optional": true
@@ -1063,8 +1066,8 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.14.0",
-      "integrity": "sha1-DTPY0LCEecQk59ZUhV/d8sceQCE=",
+      "version": "8.17.0",
+      "integrity": "sha1-74THCe+DJOdmh4g0lwvqmn47cs8=",
       "optional": true,
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1075,12 +1078,12 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.14.0",
-      "integrity": "sha1-p6OlpTpsCTE+EvtFMdT/WC7jwxI=",
+      "version": "8.17.0",
+      "integrity": "sha1-QLWQO8kpsejdnHfbPLUs+xmaKjQ=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.14.0",
-        "@typescript-eslint/visitor-keys": "8.14.0",
+        "@typescript-eslint/types": "8.17.0",
+        "@typescript-eslint/visitor-keys": "8.17.0",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
@@ -1102,14 +1105,14 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.14.0",
-      "integrity": "sha1-rCUGh14Dq6JOYCNk5Dst+kVSnb0=",
+      "version": "8.17.0",
+      "integrity": "sha1-QcBRBaK2q3WS9RPS7rLCwCNtiQg=",
       "optional": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.14.0",
-        "@typescript-eslint/types": "8.14.0",
-        "@typescript-eslint/typescript-estree": "8.14.0"
+        "@typescript-eslint/scope-manager": "8.17.0",
+        "@typescript-eslint/types": "8.17.0",
+        "@typescript-eslint/typescript-estree": "8.17.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1120,15 +1123,20 @@
       },
       "peerDependencies": {
         "eslint": "^8.57.0 || ^9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.14.0",
-      "integrity": "sha1-JBjVpUZpr5ZYmGreTmz7d2fYFa0=",
+      "version": "8.17.0",
+      "integrity": "sha1-TbzQ4oub+VH0KTgFvzT5jfReGqg=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.14.0",
-        "eslint-visitor-keys": "^3.4.3"
+        "@typescript-eslint/types": "8.17.0",
+        "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1138,6 +1146,17 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "4.2.0",
+      "integrity": "sha1-aHussq+IT83aim59ZcYG9GoUzUU=",
+      "optional": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/@ungap/structured-clone": {
       "version": "1.2.0",
       "integrity": "sha1-dWZBrbWHhRtcyz4JXa8nrlgchAY=",
@@ -1149,12 +1168,12 @@
       "optional": true
     },
     "node_modules/@vscode/extension-telemetry": {
-      "version": "0.9.7",
-      "integrity": "sha1-OG4IwfmDUL1aNozPJ5pQGgzW3Wc=",
+      "version": "0.9.8",
+      "integrity": "sha1-EJqdteCdWwX5QDo/72DVljtmj8M=",
       "dependencies": {
-        "@microsoft/1ds-core-js": "^4.3.0",
-        "@microsoft/1ds-post-js": "^4.3.0",
-        "@microsoft/applicationinsights-web-basic": "^3.3.0"
+        "@microsoft/1ds-core-js": "^4.3.4",
+        "@microsoft/1ds-post-js": "^4.3.4",
+        "@microsoft/applicationinsights-web-basic": "^3.3.4"
       },
       "engines": {
         "vscode": "^1.75.0"
@@ -1869,8 +1888,8 @@
       "optional": true
     },
     "node_modules/cross-spawn": {
-      "version": "7.0.5",
-      "integrity": "sha1-kQqsiA/1JD2pa3KLxlIaX2wvL4I=",
+      "version": "7.0.6",
+      "integrity": "sha1-ilj+ePANzXDDcEUXWd+/rwPo7p8=",
       "devOptional": true,
       "dependencies": {
         "path-key": "^3.1.0",
@@ -2566,8 +2585,8 @@
       }
     },
     "node_modules/flatted": {
-      "version": "3.3.1",
-      "integrity": "sha1-IdtHBymmc01JlwAvQ5yzCJh/Vno=",
+      "version": "3.3.2",
+      "integrity": "sha1-rboUSKmEG+xytCxTLqI9u+3vGic=",
       "optional": true
     },
     "node_modules/foreground-child": {
@@ -2723,11 +2742,14 @@
       }
     },
     "node_modules/gopd": {
-      "version": "1.0.1",
-      "integrity": "sha1-Kf923mnax0ibfAkYpXiOVkd8Myw=",
+      "version": "1.1.0",
+      "integrity": "sha1-348IOcLUjK78MqAlpJKU05YGyRI=",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.1.3"
+        "get-intrinsic": "^1.2.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -2758,9 +2780,12 @@
       }
     },
     "node_modules/has-proto": {
-      "version": "1.0.3",
-      "integrity": "sha1-sx3f6bDm6ZFFNqarKGQm0CFPd/0=",
+      "version": "1.1.0",
+      "integrity": "sha1-3rEElMu+iAm84WijuWH0KWn17UM=",
       "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.7"
+      },
       "engines": {
         "node": ">= 0.4"
       },
@@ -2769,8 +2794,8 @@
       }
     },
     "node_modules/has-symbols": {
-      "version": "1.0.3",
-      "integrity": "sha1-u3ssQ0klHc6HsSX3vfh0qnyLOfg=",
+      "version": "1.1.0",
+      "integrity": "sha1-/JxqeDoISVHQuXH+EBjegTcHozg=",
       "dev": true,
       "engines": {
         "node": ">= 0.4"
@@ -4851,8 +4876,8 @@
       "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
     },
     "node_modules/ts-api-utils": {
-      "version": "1.4.0",
-      "integrity": "sha1-cJxvIHblEagVV/PQegy9VmroGVw=",
+      "version": "1.4.3",
+      "integrity": "sha1-v8IhX+ZSj+yrKw+6VwouikJjsGQ=",
       "optional": true,
       "engines": {
         "node": ">=16"
@@ -4926,8 +4951,8 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.6.3",
-      "integrity": "sha1-XzRJ4xydlP67F94DzAgd1W2B21s=",
+      "version": "5.7.2",
+      "integrity": "sha1-MWnPjEyKgozeU7qeyz0rHV3We+Y=",
       "optional": true,
       "bin": {
         "tsc": "bin/tsc",
diff --git a/package.json b/package.json
index 7e797ff226..d86be154fd 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
     "onCommand:PowerShell.SpecifyScriptArgs"
   ],
   "dependencies": {
-    "@vscode/extension-telemetry": "^0.9.7",
+    "@vscode/extension-telemetry": "^0.9.8",
     "node-fetch": "^2.7.0",
     "semver": "^7.6.3",
     "untildify": "^4.0.0",
@@ -73,9 +73,9 @@
     "esbuild": "^0.21.5"
   },
   "optionalDependencies": {
-    "@types/mocha": "^10.0.9",
+    "@types/mocha": "^10.0.10",
     "@types/mock-fs": "^4.13.4",
-    "@types/node": "^20.17.6",
+    "@types/node": "^20.17.9",
     "@types/node-fetch": "^2.6.12",
     "@types/rewire": "^2.5.30",
     "@types/semver": "^7.5.8",
@@ -83,8 +83,8 @@
     "@types/ungap__structured-clone": "^1.2.0",
     "@types/uuid": "^9.0.8",
     "@types/vscode": "~1.94.0",
-    "@typescript-eslint/eslint-plugin": "^8.14.0",
-    "@typescript-eslint/parser": "^8.14.0",
+    "@typescript-eslint/eslint-plugin": "^8.17.0",
+    "@typescript-eslint/parser": "^8.17.0",
     "@ungap/structured-clone": "^1.2.0",
     "@vscode/debugprotocol": "^1.68.0",
     "@vscode/test-electron": "^2.4.1",
@@ -98,7 +98,7 @@
     "rewire": "^7.0.0",
     "sinon": "^18.0.1",
     "source-map-support": "^0.5.21",
-    "typescript": "^5.6.3"
+    "typescript": "^5.7.2"
   },
   "extensionDependencies": [
     "vscode.powershell"
@@ -311,8 +311,6 @@
         "command": "GetVsCodeSessionId",
         "enablement": "false"
       }
-
-
     ],
     "menus": {
       "commandPalette": [
@@ -616,7 +614,7 @@
             "markdownDescription": "Show buttons in the editor's title bar for moving the terminals pane (with the PowerShell Extension Terminal) around."
           },
           "powershell.enableReferencesCodeLens": {
-          "type": "boolean",
+            "type": "boolean",
             "default": true,
             "markdownDescription": "Specifies if Code Lenses are displayed above function definitions, used to show the number of times the function is referenced in the workspace and navigate to those references. Large workspaces may want to disable this setting if performance is compromised. See also `#powershell.analyzeOpenDocumentsOnly#`."
           },
@@ -676,7 +674,7 @@
           }
         }
       },
-            {
+      {
         "title": "Formatting",
         "properties": {
           "powershell.codeFormatting.preset": {
@@ -895,7 +893,7 @@
           }
         }
       },
-            {
+      {
         "title": "Pester",
         "properties": {
           "powershell.pester.useLegacyCodeLens": {

From d75312ab20ef30aa395436c27d177fa4cad3d29f Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 4 Dec 2024 15:16:43 -0800
Subject: [PATCH 15/21] v2024.5.2-preview: Bug fixes and build improvements.

---
 CHANGELOG.md | 9 +++++++++
 package.json | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd6941264e..261b13fc84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # PowerShell Extension Release History
 
+## v2024.5.2-preview
+### Wednesday, December 04, 2024
+
+With PowerShell Editor Services [v4.1.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.1.0)!
+
+Bug fixes and build improvements.
+
+See more details at the GitHub Release for [v2024.5.2-preview](https://github.com/PowerShell/vscode-powershell/releases/tag/v2024.5.2-preview).
+
 ## v2024.5.1-preview
 ### Monday, November 18, 2024
 
diff --git a/package.json b/package.json
index d86be154fd..208a191649 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "powershell",
   "displayName": "PowerShell",
-  "version": "2024.5.1",
+  "version": "2024.5.2",
   "preview": false,
   "publisher": "ms-vscode",
   "description": "Develop PowerShell modules, commands and scripts in Visual Studio Code!",

From 2e8fdc9ead4910ec0a76d7a9266bd3051147b9c5 Mon Sep 17 00:00:00 2001
From: Cydroz <46122593+Cydroz@users.noreply.github.com>
Date: Mon, 6 Jan 2025 23:36:30 +1100
Subject: [PATCH 16/21] Define and use $total variable

---
 snippets/PowerShell.json | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/snippets/PowerShell.json b/snippets/PowerShell.json
index e5b23be643..9371047850 100644
--- a/snippets/PowerShell.json
+++ b/snippets/PowerShell.json
@@ -165,8 +165,9 @@
     "prefix": "foreach-progress",
     "description": "Insert a foreach loop with Write-Progress initialized",
     "body": [
-      "\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$array.count,4) * 100)",
-      "Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$array.count - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
+      "\\$total = \\$${1:array}.count",
+      "\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
+      "Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
       "\\$i = 1",
       "foreach ($${2:item} in $${1:array}) {",
       "    \\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
@@ -670,4 +671,4 @@
       "}"
     ]
   }
-}
+}
\ No newline at end of file

From 01cc9fed878524f72f40df26b243bd5141d808e9 Mon Sep 17 00:00:00 2001
From: Cydroz <46122593+Cydroz@users.noreply.github.com>
Date: Mon, 6 Jan 2025 23:36:55 +1100
Subject: [PATCH 17/21] Define $i before use

---
 snippets/PowerShell.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/snippets/PowerShell.json b/snippets/PowerShell.json
index 9371047850..5ae01e3082 100644
--- a/snippets/PowerShell.json
+++ b/snippets/PowerShell.json
@@ -166,9 +166,9 @@
     "description": "Insert a foreach loop with Write-Progress initialized",
     "body": [
       "\\$total = \\$${1:array}.count",
+      "\\$i = 1",
       "\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
       "Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
-      "\\$i = 1",
       "foreach ($${2:item} in $${1:array}) {",
       "    \\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
       "    Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",

From 0c5f2f8c23add289b3909c352f0c28213ac38537 Mon Sep 17 00:00:00 2001
From: Cydroz <46122593+Cydroz@users.noreply.github.com>
Date: Mon, 6 Jan 2025 23:48:04 +1100
Subject: [PATCH 18/21] Add tab step to customise increment varname

---
 snippets/PowerShell.json | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/snippets/PowerShell.json b/snippets/PowerShell.json
index 5ae01e3082..d0a94ba30b 100644
--- a/snippets/PowerShell.json
+++ b/snippets/PowerShell.json
@@ -166,16 +166,16 @@
     "description": "Insert a foreach loop with Write-Progress initialized",
     "body": [
       "\\$total = \\$${1:array}.count",
-      "\\$i = 1",
-      "\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
-      "Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
+      "\\$${4:i} = 1",
+      "\\$progPercent = \"{0:n2}\" -f ([math]::round(\\$${4:i}/\\$total,4) * 100)",
+      "Write-Progress -Activity \"${3:activityName}\" -Status \"\\$${4:i} of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
       "foreach ($${2:item} in $${1:array}) {",
-      "    \\$progPercent = \"{0:n2}\" -f ([math]::round(\\$i/\\$total,4) * 100)",
-      "    Write-Progress -Activity \"${3:activityName}\" -Status \"\\$i of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
+      "    \\$progPercent = \"{0:n2}\" -f ([math]::round(\\$${4:i}/\\$total,4) * 100)",
+      "    Write-Progress -Activity \"${3:activityName}\" -Status \"\\$${4:i} of \\$total - \\$progPercent% Complete:\" -PercentComplete \\$progPercent",
       "    # Insert Code Here",
       "    ${0}",
       "    ",
-      "    \\$i++",
+      "    \\$${4:i}++",
       "}",
       ""
     ]

From a1e279ba5461a78bfd78280ada6f914dea082303 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 13:33:20 -0800
Subject: [PATCH 19/21] Update packages with a VS Code engine bump to 1.96

With new string casts to quiet new warnings.
---
 docs/development.md |  11 +-
 package-lock.json   | 501 +++++++++++++++++++++++---------------------
 package.json        |  17 +-
 tsconfig.json       |  13 +-
 4 files changed, 276 insertions(+), 266 deletions(-)

diff --git a/docs/development.md b/docs/development.md
index c8e7cae79a..548e37fa57 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -34,11 +34,12 @@ of VS Code's own `package.json` file for their [`electron`][] dependency. The
 major version of [Electron][] will tell us which [Node.js][] is included, which
 dictates which version of Node.js the extension is eventually run with. This
 lets us finally update our `@types/node` development dependency to match, our
-developer machines if necessary, and the CI and OneBranch pipeline tasks.
+developer machines if necessary, the CI and OneBranch pipeline tasks, and the
+`.tsconfig` file. Note that the version of `@types/node` will not necessarily
+exactly match the version of Node.js, but the major version should.
 
-[`vscodeVersion`]: https://github.com/microsoft/azuredatastudio/blob/4970733324ef8254b7c22a5dc55af7f8a1dea93f/product.json#L50
-[`electron`]: https://github.com/microsoft/vscode/blob/384ff7382de624fb94dbaf6da11977bba1ecd427/package.json#L159
-[Electron]: https://www.electronjs.org/blog/electron-30-0
+[`electron`]: https://github.com/microsoft/vscode/blob/138f619c86f1199955d53b4166bef66ef252935c/package.json#L156
+[Electron]: https://releases.electronjs.org/release/v32.2.6
 [Node.js]: https://nodejs.org/en/download/package-manager
 
 ### Building the Code
@@ -65,7 +66,7 @@ To debug the extension use one of the provided `Launch Extension` debug configur
 
 All three templates use pre-launch tasks to build the code, and support automatic restart of the extension host on changes to the Extension source code. [Hot Reload](https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/) is also enabled for PowerShell Editor Services.
 
-> [!WARNING]  
+> [!WARNING]
 > There is a current limitation that, if you restart the extension/extension host or it is restarted due to a extension code change, the editor services attachment will be disconnected due to the PSES terminal being terminated, and you will either need to restart the debug session completely, or do a manual build of PSES and run the `Attach to Editor Services` debug launch manually.
 
 Try the `powershell.developer.editorServicesWaitForDebugger` setting to ensure that you are fully attached before the extension startup process continues.
diff --git a/package-lock.json b/package-lock.json
index 82c2bd2da9..89e85d48e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "powershell",
-  "version": "2024.5.1",
+  "version": "2024.5.2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "powershell",
-      "version": "2024.5.1",
+      "version": "2024.5.2",
       "license": "SEE LICENSE IN LICENSE.txt",
       "dependencies": {
         "@vscode/extension-telemetry": "^0.9.8",
@@ -22,27 +22,28 @@
         "esbuild": "^0.21.5"
       },
       "engines": {
-        "vscode": "^1.94.0"
+        "vscode": "^1.96.0"
       },
       "optionalDependencies": {
+        "@tsconfig/node20": "^20.1.4",
         "@types/mocha": "^10.0.10",
         "@types/mock-fs": "^4.13.4",
-        "@types/node": "^20.17.9",
+        "@types/node": "^20.17.14",
         "@types/node-fetch": "^2.6.12",
         "@types/rewire": "^2.5.30",
         "@types/semver": "^7.5.8",
         "@types/sinon": "^17.0.3",
         "@types/ungap__structured-clone": "^1.2.0",
         "@types/uuid": "^9.0.8",
-        "@types/vscode": "~1.94.0",
-        "@typescript-eslint/eslint-plugin": "^8.17.0",
-        "@typescript-eslint/parser": "^8.17.0",
-        "@ungap/structured-clone": "^1.2.0",
+        "@types/vscode": "~1.96.0",
+        "@typescript-eslint/eslint-plugin": "^8.20.0",
+        "@typescript-eslint/parser": "^8.20.0",
+        "@ungap/structured-clone": "^1.2.1",
         "@vscode/debugprotocol": "^1.68.0",
         "@vscode/test-electron": "^2.4.1",
         "eslint": "^8.57.0",
         "eslint-plugin-header": "^3.1.1",
-        "glob": "^11.0.0",
+        "glob": "^11.0.1",
         "mocha": "^10.8.2",
         "mocha-explorer-launcher-scripts": "^0.4.0",
         "mocha-multi-reporters": "^1.5.1",
@@ -50,7 +51,7 @@
         "rewire": "^7.0.0",
         "sinon": "^18.0.1",
         "source-map-support": "^0.5.21",
-        "typescript": "^5.7.2"
+        "typescript": "^5.7.3"
       }
     },
     "node_modules/@azure/abort-controller": {
@@ -95,8 +96,8 @@
       }
     },
     "node_modules/@azure/core-rest-pipeline": {
-      "version": "1.18.1",
-      "integrity": "sha1-OA59PxW+gN6D7kFBdq2zKCRALzg=",
+      "version": "1.18.2",
+      "integrity": "sha1-+jqDtBLUs+M+3KMKcbHVg4MGwHU=",
       "dev": true,
       "dependencies": {
         "@azure/abort-controller": "^2.0.0",
@@ -136,8 +137,8 @@
       }
     },
     "node_modules/@azure/identity": {
-      "version": "4.5.0",
-      "integrity": "sha1-k843V792GgjP0F9W7xgUNeBbnhw=",
+      "version": "4.6.0",
+      "integrity": "sha1-J2lXtZ/tls9I1eUPxyjDwibk8QU=",
       "dev": true,
       "dependencies": {
         "@azure/abort-controller": "^2.0.0",
@@ -147,7 +148,7 @@
         "@azure/core-tracing": "^1.0.0",
         "@azure/core-util": "^1.11.0",
         "@azure/logger": "^1.0.0",
-        "@azure/msal-browser": "^3.26.1",
+        "@azure/msal-browser": "^4.0.1",
         "@azure/msal-node": "^2.15.0",
         "events": "^3.0.0",
         "jws": "^4.0.0",
@@ -171,19 +172,19 @@
       }
     },
     "node_modules/@azure/msal-browser": {
-      "version": "3.27.0",
-      "integrity": "sha1-tvAvc8jhAtPxFQCbRndTn7Fz/is=",
+      "version": "4.0.1",
+      "integrity": "sha1-YNyEqAPBPGKT33tkL+oLCe/oMnY=",
       "dev": true,
       "dependencies": {
-        "@azure/msal-common": "14.16.0"
+        "@azure/msal-common": "15.0.1"
       },
       "engines": {
         "node": ">=0.8.0"
       }
     },
     "node_modules/@azure/msal-common": {
-      "version": "14.16.0",
-      "integrity": "sha1-80cPyux4jb5QhZlSzUmTQL2iPXo=",
+      "version": "15.0.1",
+      "integrity": "sha1-6cAZ909HXs0h3meRksujMeVLEVY=",
       "dev": true,
       "engines": {
         "node": ">=0.8.0"
@@ -202,6 +203,14 @@
         "node": ">=16"
       }
     },
+    "node_modules/@azure/msal-node/node_modules/@azure/msal-common": {
+      "version": "14.16.0",
+      "integrity": "sha1-80cPyux4jb5QhZlSzUmTQL2iPXo=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/@azure/msal-node/node_modules/uuid": {
       "version": "8.3.2",
       "integrity": "sha1-gNW1ztJxu5r2xEXyGhoExgbO++I=",
@@ -815,15 +824,15 @@
       }
     },
     "node_modules/@nevware21/ts-async": {
-      "version": "0.5.3",
-      "integrity": "sha1-R8BDUUWLBARXl3+0t1xDwn+6MoM=",
+      "version": "0.5.4",
+      "integrity": "sha1-UvhEndCzsWqjF6GLRmL2+xOhNfE=",
       "dependencies": {
-        "@nevware21/ts-utils": ">= 0.11.5 < 2.x"
+        "@nevware21/ts-utils": ">= 0.11.6 < 2.x"
       }
     },
     "node_modules/@nevware21/ts-utils": {
-      "version": "0.11.5",
-      "integrity": "sha1-uQD10E5lepbglqWNrW4Leu0nPvs="
+      "version": "0.11.6",
+      "integrity": "sha1-SfQ9DEiPzxJ+9L3RNWY3CS7Qx+I="
     },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
@@ -896,6 +905,11 @@
       "integrity": "sha1-KCBG8D6IbjUrLV9dpet1XgFFfz8=",
       "optional": true
     },
+    "node_modules/@tsconfig/node20": {
+      "version": "20.1.4",
+      "integrity": "sha1-NFfULt3xLTveOXYYarDNIrhd+Sg=",
+      "optional": true
+    },
     "node_modules/@types/mocha": {
       "version": "10.0.10",
       "integrity": "sha1-kfYpBejSPL1mIlMS8jlFSiO+v6A=",
@@ -910,8 +924,8 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "20.17.9",
-      "integrity": "sha1-XxQdS37hJc3uX67+KN4JU5iGW6s=",
+      "version": "20.17.14",
+      "integrity": "sha1-V559de611Gt1xzyYghY55ktolgg=",
       "optional": true,
       "dependencies": {
         "undici-types": "~6.19.2"
@@ -960,24 +974,24 @@
       "optional": true
     },
     "node_modules/@types/vscode": {
-      "version": "1.94.0",
-      "integrity": "sha1-zNIRG27KumrU2hnC1SSCj6c64lA=",
+      "version": "1.96.0",
+      "integrity": "sha1-MYEAS/JdcWd65KrN12BaP9ft8I4=",
       "optional": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.17.0",
-      "integrity": "sha1-LuBzxCH06B4C0Q5zEkFmS2JTsjw=",
+      "version": "8.20.0",
+      "integrity": "sha1-tHo5jg5VHLAIxgGQuAQ5TmhSyGM=",
       "optional": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.17.0",
-        "@typescript-eslint/type-utils": "8.17.0",
-        "@typescript-eslint/utils": "8.17.0",
-        "@typescript-eslint/visitor-keys": "8.17.0",
+        "@typescript-eslint/scope-manager": "8.20.0",
+        "@typescript-eslint/type-utils": "8.20.0",
+        "@typescript-eslint/utils": "8.20.0",
+        "@typescript-eslint/visitor-keys": "8.20.0",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "ts-api-utils": "^1.3.0"
+        "ts-api-utils": "^2.0.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -988,23 +1002,19 @@
       },
       "peerDependencies": {
         "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.17.0",
-      "integrity": "sha1-LulyuxL6aaxiW4WBPcjZpaBT/1I=",
+      "version": "8.20.0",
+      "integrity": "sha1-XK8iMKNwlNwOZxz4Nrlt05tYfO0=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.17.0",
-        "@typescript-eslint/types": "8.17.0",
-        "@typescript-eslint/typescript-estree": "8.17.0",
-        "@typescript-eslint/visitor-keys": "8.17.0",
+        "@typescript-eslint/scope-manager": "8.20.0",
+        "@typescript-eslint/types": "8.20.0",
+        "@typescript-eslint/typescript-estree": "8.20.0",
+        "@typescript-eslint/visitor-keys": "8.20.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1015,21 +1025,17 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.17.0",
-      "integrity": "sha1-o/Sb89TSf/jWsuoJm6Rl7028qjo=",
+      "version": "8.20.0",
+      "integrity": "sha1-qvQZi1CfuHplJ8As+/r4kBF551w=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.17.0",
-        "@typescript-eslint/visitor-keys": "8.17.0"
+        "@typescript-eslint/types": "8.20.0",
+        "@typescript-eslint/visitor-keys": "8.20.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1040,14 +1046,14 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.17.0",
-      "integrity": "sha1-0yZWn0mM3Q7fWNW7YDC0rZFOY9M=",
+      "version": "8.20.0",
+      "integrity": "sha1-lYFx2GshOj8ytbFrkdsmeWik7xk=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.17.0",
-        "@typescript-eslint/utils": "8.17.0",
+        "@typescript-eslint/typescript-estree": "8.20.0",
+        "@typescript-eslint/utils": "8.20.0",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.3.0"
+        "ts-api-utils": "^2.0.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1057,17 +1063,13 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.17.0",
-      "integrity": "sha1-74THCe+DJOdmh4g0lwvqmn47cs8=",
+      "version": "8.20.0",
+      "integrity": "sha1-SH3lMUtUFd7gdelVaLh6daPnMM8=",
       "optional": true,
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1078,18 +1080,18 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.17.0",
-      "integrity": "sha1-QLWQO8kpsejdnHfbPLUs+xmaKjQ=",
+      "version": "8.20.0",
+      "integrity": "sha1-ZYzqB7flmB8ZvOXPFmLLcK1Z8ms=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.17.0",
-        "@typescript-eslint/visitor-keys": "8.17.0",
+        "@typescript-eslint/types": "8.20.0",
+        "@typescript-eslint/visitor-keys": "8.20.0",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
         "minimatch": "^9.0.4",
         "semver": "^7.6.0",
-        "ts-api-utils": "^1.3.0"
+        "ts-api-utils": "^2.0.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1098,21 +1100,19 @@
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <5.8.0"
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.17.0",
-      "integrity": "sha1-QcBRBaK2q3WS9RPS7rLCwCNtiQg=",
+      "version": "8.20.0",
+      "integrity": "sha1-UxJ+zTFLOwiDa0SYtxzbhvTvOqI=",
       "optional": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.17.0",
-        "@typescript-eslint/types": "8.17.0",
-        "@typescript-eslint/typescript-estree": "8.17.0"
+        "@typescript-eslint/scope-manager": "8.20.0",
+        "@typescript-eslint/types": "8.20.0",
+        "@typescript-eslint/typescript-estree": "8.20.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1122,20 +1122,16 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.17.0",
-      "integrity": "sha1-TbzQ4oub+VH0KTgFvzT5jfReGqg=",
+      "version": "8.20.0",
+      "integrity": "sha1-LfbiS8aQhLgfBqqqSNGYsQ04K+0=",
       "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "8.17.0",
+        "@typescript-eslint/types": "8.20.0",
         "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
@@ -1158,8 +1154,8 @@
       }
     },
     "node_modules/@ungap/structured-clone": {
-      "version": "1.2.0",
-      "integrity": "sha1-dWZBrbWHhRtcyz4JXa8nrlgchAY=",
+      "version": "1.2.1",
+      "integrity": "sha1-KPoYX2far3t6GowdRFEyxdl5+L0=",
       "optional": true
     },
     "node_modules/@vscode/debugprotocol": {
@@ -1399,12 +1395,9 @@
       }
     },
     "node_modules/agent-base": {
-      "version": "7.1.1",
-      "integrity": "sha1-vb3tffsJa3UaKgh+7rlmRyWy4xc=",
+      "version": "7.1.3",
+      "integrity": "sha1-KUNeuCG8QZRjOluJ5bxHA7r8JaE=",
       "devOptional": true,
-      "dependencies": {
-        "debug": "^4.3.4"
-      },
       "engines": {
         "node": ">= 14"
       }
@@ -1608,16 +1601,25 @@
       "integrity": "sha1-KxRqb9cugLT1XSVfNe1Zo6mkG9U=",
       "optional": true
     },
-    "node_modules/call-bind": {
-      "version": "1.0.7",
-      "integrity": "sha1-BgFlmcQMVkmMGHadJzC+JCtvo7k=",
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.1",
+      "integrity": "sha1-MuWJLmNhspsLVFum93YzeNrKKEA=",
       "dev": true,
       "dependencies": {
-        "es-define-property": "^1.0.0",
         "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "set-function-length": "^1.2.1"
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.3",
+      "integrity": "sha1-Qc/QMrWT45F2pxUzq084SqBP1oE=",
+      "dev": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "get-intrinsic": "^1.2.6"
       },
       "engines": {
         "node": ">= 0.4"
@@ -1927,8 +1929,8 @@
       }
     },
     "node_modules/debug": {
-      "version": "4.3.7",
-      "integrity": "sha1-h5RbQVGgEddtlaGY1xEchlw2ClI=",
+      "version": "4.4.0",
+      "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=",
       "devOptional": true,
       "dependencies": {
         "ms": "^2.1.3"
@@ -1982,22 +1984,6 @@
       "integrity": "sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=",
       "optional": true
     },
-    "node_modules/define-data-property": {
-      "version": "1.1.4",
-      "integrity": "sha1-iU3BQbt9MGCuQ2b2oBB+aPvkjF4=",
-      "dev": true,
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/define-lazy-prop": {
       "version": "2.0.0",
       "integrity": "sha1-P3rkIRKbyqrJvHSQXJigAJ7J7n8=",
@@ -2081,8 +2067,8 @@
       }
     },
     "node_modules/domutils": {
-      "version": "3.1.0",
-      "integrity": "sha1-xH9VEnjT3EsLGrjLtC11Gm8Ngk4=",
+      "version": "3.2.2",
+      "integrity": "sha1-7b/itmiwwdl8JLrw8QYrEyIhvHg=",
       "dev": true,
       "dependencies": {
         "dom-serializer": "^2.0.0",
@@ -2093,6 +2079,19 @@
         "url": "https://github.com/fb55/domutils?sponsor=1"
       }
     },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "integrity": "sha1-165mfh3INIL4tw/Q9u78UNow9Yo=",
+      "dev": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/eastasianwidth": {
       "version": "0.2.0",
       "integrity": "sha1-aWzi7Aqg5uqTo5f/zySqeEDIJ8s=",
@@ -2144,12 +2143,9 @@
       }
     },
     "node_modules/es-define-property": {
-      "version": "1.0.0",
-      "integrity": "sha1-x/rvvf+LJpbPX0aSHt+3fMS6OEU=",
+      "version": "1.0.1",
+      "integrity": "sha1-mD6y+aZyTpMD9hrd8BHHLgngsPo=",
       "dev": true,
-      "dependencies": {
-        "get-intrinsic": "^1.2.4"
-      },
       "engines": {
         "node": ">= 0.4"
       }
@@ -2162,6 +2158,17 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "integrity": "sha1-HE8sSDcydZfOadLKGQp/3RcjOME=",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/esbuild": {
       "version": "0.21.5",
       "integrity": "sha1-nKMBsSCSKVm3ZjYNisgw2g0CmX0=",
@@ -2475,15 +2482,15 @@
       "optional": true
     },
     "node_modules/fast-glob": {
-      "version": "3.3.2",
-      "integrity": "sha1-qQRQHlfP3S/83tRemaVP71XkYSk=",
+      "version": "3.3.3",
+      "integrity": "sha1-0G1YXOjbqQoWsFBcVDw8z7OuuBg=",
       "optional": true,
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
         "@nodelib/fs.walk": "^1.2.3",
         "glob-parent": "^5.1.2",
         "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
+        "micromatch": "^4.0.8"
       },
       "engines": {
         "node": ">=8.6.0"
@@ -2511,8 +2518,8 @@
       "optional": true
     },
     "node_modules/fastq": {
-      "version": "1.17.1",
-      "integrity": "sha1-KlI/B6TnsegaQrkbi/IlQQd1O0c=",
+      "version": "1.18.0",
+      "integrity": "sha1-1jHX4l+v/qgYh/5eqMkBDhs2/uA=",
       "optional": true,
       "dependencies": {
         "reusify": "^1.0.4"
@@ -2657,15 +2664,20 @@
       }
     },
     "node_modules/get-intrinsic": {
-      "version": "1.2.4",
-      "integrity": "sha1-44X1pLUifUScPqu60FSU7wq76t0=",
+      "version": "1.2.7",
+      "integrity": "sha1-3PyzPTJy4V9EXRUSS8CiFhibkEQ=",
       "dev": true,
       "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-define-property": "^1.0.1",
         "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
         "function-bind": "^1.1.2",
-        "has-proto": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0"
+        "get-proto": "^1.0.0",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2674,6 +2686,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "integrity": "sha1-FQs/J0OGnvPoUewMSdFbHRTQDuE=",
+      "dev": true,
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/github-from-package": {
       "version": "0.0.0",
       "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=",
@@ -2681,8 +2705,8 @@
       "optional": true
     },
     "node_modules/glob": {
-      "version": "11.0.0",
-      "integrity": "sha1-YDHfDXtl6qHMubKbXO0WzqZY534=",
+      "version": "11.0.1",
+      "integrity": "sha1-HDrvmlnWgOYRtT3NJLuGOc7wZNk=",
       "devOptional": true,
       "dependencies": {
         "foreground-child": "^3.1.0",
@@ -2742,12 +2766,9 @@
       }
     },
     "node_modules/gopd": {
-      "version": "1.1.0",
-      "integrity": "sha1-348IOcLUjK78MqAlpJKU05YGyRI=",
+      "version": "1.2.0",
+      "integrity": "sha1-ifVrghe9vIgCvSmd9tfxCB1+UaE=",
       "dev": true,
-      "dependencies": {
-        "get-intrinsic": "^1.2.4"
-      },
       "engines": {
         "node": ">= 0.4"
       },
@@ -2768,31 +2789,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/has-property-descriptors": {
-      "version": "1.0.2",
-      "integrity": "sha1-lj7X0HHce/XwhMW/vg0bYiJYaFQ=",
-      "dev": true,
-      "dependencies": {
-        "es-define-property": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-proto": {
-      "version": "1.1.0",
-      "integrity": "sha1-3rEElMu+iAm84WijuWH0KWn17UM=",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.7"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/has-symbols": {
       "version": "1.1.0",
       "integrity": "sha1-/JxqeDoISVHQuXH+EBjegTcHozg=",
@@ -2865,11 +2861,11 @@
       }
     },
     "node_modules/https-proxy-agent": {
-      "version": "7.0.5",
-      "integrity": "sha1-notQE4cymeEfq2/VSEBdotbGArI=",
+      "version": "7.0.6",
+      "integrity": "sha1-2o3+rH2hMLBcK6S1nJts1mYRprk=",
       "devOptional": true,
       "dependencies": {
-        "agent-base": "^7.0.2",
+        "agent-base": "^7.1.2",
         "debug": "4"
       },
       "engines": {
@@ -3426,6 +3422,14 @@
         "markdown-it": "bin/markdown-it.mjs"
       }
     },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "integrity": "sha1-oN10voHiqlwvJ+Zc4oNgXuTit/k=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/mdurl": {
       "version": "2.0.0",
       "integrity": "sha1-gGduwEMwJd0+F+6YPQ/o3loiN+A=",
@@ -3707,8 +3711,8 @@
       }
     },
     "node_modules/node-abi": {
-      "version": "3.71.0",
-      "integrity": "sha1-UthLvNhXXvtxRo+6ofmkmywkIDg=",
+      "version": "3.73.0",
+      "integrity": "sha1-RFnqd+cZae26hYg4fuywXiws/zs=",
       "dev": true,
       "optional": true,
       "dependencies": {
@@ -3860,8 +3864,8 @@
       }
     },
     "node_modules/ora/node_modules/chalk": {
-      "version": "5.3.0",
-      "integrity": "sha1-Z8IKfr73Dn85cKAfkPohDLaGA4U=",
+      "version": "5.4.1",
+      "integrity": "sha1-G0i/CWPsFY3OKqz2nAk64t0gktg=",
       "optional": true,
       "engines": {
         "node": "^12.17.0 || ^14.13 || >=16.0.0"
@@ -4167,11 +4171,11 @@
       }
     },
     "node_modules/qs": {
-      "version": "6.13.1",
-      "integrity": "sha1-POX8cr06gXG4XJm5PGXdILfRsW4=",
+      "version": "6.14.0",
+      "integrity": "sha1-xj+kBoDSxclBQSoOiZyJr2DAqTA=",
       "dev": true,
       "dependencies": {
-        "side-channel": "^1.0.6"
+        "side-channel": "^1.1.0"
       },
       "engines": {
         "node": ">=0.6"
@@ -4256,11 +4260,6 @@
         "util-deprecate": "~1.0.1"
       }
     },
-    "node_modules/readable-stream/node_modules/safe-buffer": {
-      "version": "5.1.2",
-      "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=",
-      "optional": true
-    },
     "node_modules/readdirp": {
       "version": "3.6.0",
       "integrity": "sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc=",
@@ -4401,23 +4400,9 @@
       }
     },
     "node_modules/safe-buffer": {
-      "version": "5.2.1",
-      "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=",
-      "devOptional": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ]
+      "version": "5.1.2",
+      "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=",
+      "devOptional": true
     },
     "node_modules/safer-buffer": {
       "version": "2.1.2",
@@ -4447,22 +4432,6 @@
         "randombytes": "^2.1.0"
       }
     },
-    "node_modules/set-function-length": {
-      "version": "1.2.2",
-      "integrity": "sha1-qscjFBmOrtl1z3eyw7a4gGleVEk=",
-      "dev": true,
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/setimmediate": {
       "version": "1.0.5",
       "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
@@ -4488,14 +4457,65 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.0.6",
-      "integrity": "sha1-q9Jft80kuvRUZkBrEJa3gxySFfI=",
+      "version": "1.1.0",
+      "integrity": "sha1-w/z/nE2pMnhIczNeyXZfqU/2a8k=",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "integrity": "sha1-EMtZhCYxFdO3oOM2WR4pCoMK+K0=",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "integrity": "sha1-1rtrN5Asb+9RdOX1M/q0xzKib0I=",
+      "dev": true,
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "integrity": "sha1-Ed2hnVNo5Azp7CvcH7DsvAeQ7Oo=",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.7",
+        "call-bound": "^1.0.2",
         "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "object-inspect": "^1.13.1"
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4655,11 +4675,6 @@
         "safe-buffer": "~5.1.0"
       }
     },
-    "node_modules/string_decoder/node_modules/safe-buffer": {
-      "version": "5.1.2",
-      "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=",
-      "optional": true
-    },
     "node_modules/string-width": {
       "version": "5.1.2",
       "integrity": "sha1-FPja7G2B5yIdKjV+Zoyrc728p5Q=",
@@ -4766,8 +4781,8 @@
       }
     },
     "node_modules/tar-fs": {
-      "version": "2.1.1",
-      "integrity": "sha1-SJoVq4Xx8L76uzcLfeT561y+h4Q=",
+      "version": "2.1.2",
+      "integrity": "sha1-Ql8VTzQEyxbLj/bmcdRasu2VlsU=",
       "dev": true,
       "optional": true,
       "dependencies": {
@@ -4876,14 +4891,14 @@
       "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
     },
     "node_modules/ts-api-utils": {
-      "version": "1.4.3",
-      "integrity": "sha1-v8IhX+ZSj+yrKw+6VwouikJjsGQ=",
+      "version": "2.0.0",
+      "integrity": "sha1-udfV9+yfc29NDwl1i4YHl5BEqQA=",
       "optional": true,
       "engines": {
-        "node": ">=16"
+        "node": ">=18.12"
       },
       "peerDependencies": {
-        "typescript": ">=4.2.0"
+        "typescript": ">=4.8.4"
       }
     },
     "node_modules/tslib": {
@@ -4951,8 +4966,8 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.7.2",
-      "integrity": "sha1-MWnPjEyKgozeU7qeyz0rHV3We+Y=",
+      "version": "5.7.3",
+      "integrity": "sha1-kZtEp9u4WDqbhW0WK+JKVL+ABz4=",
       "optional": true,
       "bin": {
         "tsc": "bin/tsc",
@@ -4973,8 +4988,8 @@
       "dev": true
     },
     "node_modules/undici": {
-      "version": "6.21.0",
-      "integrity": "sha1-Sz06+u+YTge0jnYgw07YooXtTNQ=",
+      "version": "6.21.1",
+      "integrity": "sha1-M2AloUFi5oN+RK17gZs1tsavDgU=",
       "dev": true,
       "engines": {
         "node": ">=18.17"
diff --git a/package.json b/package.json
index 208a191649..30c7a9a94a 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
   "publisher": "ms-vscode",
   "description": "Develop PowerShell modules, commands and scripts in Visual Studio Code!",
   "engines": {
-    "vscode": "^1.94.0"
+    "vscode": "^1.96.0"
   },
   "author": "Microsoft Corporation",
   "license": "SEE LICENSE IN LICENSE.txt",
@@ -73,24 +73,25 @@
     "esbuild": "^0.21.5"
   },
   "optionalDependencies": {
+    "@tsconfig/node20": "^20.1.4",
     "@types/mocha": "^10.0.10",
     "@types/mock-fs": "^4.13.4",
-    "@types/node": "^20.17.9",
+    "@types/node": "^20.17.14",
     "@types/node-fetch": "^2.6.12",
     "@types/rewire": "^2.5.30",
     "@types/semver": "^7.5.8",
     "@types/sinon": "^17.0.3",
     "@types/ungap__structured-clone": "^1.2.0",
     "@types/uuid": "^9.0.8",
-    "@types/vscode": "~1.94.0",
-    "@typescript-eslint/eslint-plugin": "^8.17.0",
-    "@typescript-eslint/parser": "^8.17.0",
-    "@ungap/structured-clone": "^1.2.0",
+    "@types/vscode": "~1.96.0",
+    "@typescript-eslint/eslint-plugin": "^8.20.0",
+    "@typescript-eslint/parser": "^8.20.0",
+    "@ungap/structured-clone": "^1.2.1",
     "@vscode/debugprotocol": "^1.68.0",
     "@vscode/test-electron": "^2.4.1",
     "eslint": "^8.57.0",
     "eslint-plugin-header": "^3.1.1",
-    "glob": "^11.0.0",
+    "glob": "^11.0.1",
     "mocha": "^10.8.2",
     "mocha-explorer-launcher-scripts": "^0.4.0",
     "mocha-multi-reporters": "^1.5.1",
@@ -98,7 +99,7 @@
     "rewire": "^7.0.0",
     "sinon": "^18.0.1",
     "source-map-support": "^0.5.21",
-    "typescript": "^5.7.2"
+    "typescript": "^5.7.3"
   },
   "extensionDependencies": [
     "vscode.powershell"
diff --git a/tsconfig.json b/tsconfig.json
index 3d2b64d81a..638345adf1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,22 +1,15 @@
+// NOTE: The TypeScript compiler is only used for building the tests (and
+// the sources which the tests need). The extension is built with `esbuild`.
 {
+  "extends": "@tsconfig/node20/tsconfig.json",
   "compilerOptions": {
-    // NOTE: The TypeScript compiler is only used for building the tests (and
-    // the sources which the tests need). The extension is built with `esbuild`.
-    "module": "commonjs",
-    "target": "ES2022",
-    "lib": [
-      "ES2022",
-      "DOM"
-    ],
     "sourceMap": true,
     "rootDir": ".",
-    "strict": true,
     "noFallthroughCasesInSwitch": true,
     "noImplicitOverride": true,
     "noImplicitReturns": true,
     "noUnusedLocals": true,
     "noUnusedParameters": true,
-    "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
     "useUnknownInCatchVariables": true
   },

From f6fd050ace4b9c82e4e572806c7a97f22571980f Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Thu, 16 Jan 2025 12:19:57 -0800
Subject: [PATCH 20/21] v2025.1.0-preview: VS Code engine update and snippet
 fix (#5127)

---
 CHANGELOG.md        |  9 +++++++++
 docs/development.md | 29 +++++++++++++++++------------
 package.json        |  2 +-
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 261b13fc84..ee4ea60c47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # PowerShell Extension Release History
 
+## v2025.1.0-preview
+### Thursday, January 16, 2025
+
+With PowerShell Editor Services [v4.2.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.2.0)!
+
+VS Code engine update and snippet fix
+
+See more details at the GitHub Release for [v2025.1.0-preview](https://github.com/PowerShell/vscode-powershell/releases/tag/v2025.1.0-preview).
+
 ## v2024.5.2-preview
 ### Wednesday, December 04, 2024
 
diff --git a/docs/development.md b/docs/development.md
index 548e37fa57..1d839612bc 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -87,29 +87,34 @@ of Microsoft. Assume `origin` is GitHub and `ado` is Azure DevOps.
 cd ./PowerShellEditorServices
 git checkout -B release
 ./tools/updateVersion.ps1 -Version "4.0.0" -Changes "Major release!"
+# Amend changelog as necessary
 git push --force-with-lease origin
-git push ado HEAD:main
-
+# Open, approve, and merge PR on GitHub
 cd ../vscode-powershell
 git checkout -B release
 ./tools/updateVersion.ps1 -Version "2024.4.0" -Changes "Major release!"
+# Amend changelog as necessary
 git push --force-with-lease origin
-git push ado HEAD:main
+# Open, approve, and merge PR on GitHub
+cd ../PowerShellEditorServices
+git checkout main
+git pull
+git push ado HEAD:release
+cd ../vscode-powershell
+git checkout main
+git pull
+git push ado HEAD:release
+# Download and test assets from draft GitHub Releases
+# Publish releases, ensuring tag is at release commit in `main`
+# Permit pipeline to publish to marketplace
 ```
 
-1. Amend changelogs as necessary.
-2. Push `release` branches to GitHub and to Azure DevOps `main` branch.
-3. Download and test assets!
-4. Publish draft releases and merge (don't squash!) branches.
-5. Permit pipeline to publish to marketplace.
-
 If rolling from pre-release to release, do not change the version of PowerShell
 Editor Services between a pre-release and the subsequent release! We only
 need to release the extension.
 
-The Azure DevOps pipelines have to build off `main` branch for _reasons_,
-but we still want to use PRs. Hence pushing `release` to `main` and then
-merging (not squashing nor rebasing) those PRs so the commit stays the same.
+The Azure DevOps pipelines have to build off a PR merged to `main` for _reasons_,
+hence that repo is a superset including all our commits plus signed PR merge commits.
 
 ### Versioning
 
diff --git a/package.json b/package.json
index 30c7a9a94a..fc698ded92 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "powershell",
   "displayName": "PowerShell",
-  "version": "2024.5.2",
+  "version": "2025.1.0",
   "preview": false,
   "publisher": "ms-vscode",
   "description": "Develop PowerShell modules, commands and scripts in Visual Studio Code!",

From 31e461e3cecc0fb5b893b8dba98e2642c45b1380 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Tue, 21 Jan 2025 11:34:29 -0800
Subject: [PATCH 21/21] v2025.0.0: New Year Update! (#5130)

PowerShell 7.4+ and Windows PowerShell 5.1 (on a best-effort basis)
are now solely supported as 7.3 LTS and 7.2 are past end-of-support.

A major bug due to a Global Assembly Cache conflict with Serilog
when using Windows PowerShell has been resolved by removing the
troublesome dependency. This also came with a wonderful logging
overhaul for both the server and client. Thanks Justin!

Dependencies and VS Code engine have been updated. Snippets fixed.
Extension settings are now categorized. Additional PowerShell
executable path verification fixed.
---
 CHANGELOG.md | 21 +++++++++++++++++++++
 package.json |  2 +-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee4ea60c47..e4a9829b1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
 # PowerShell Extension Release History
 
+## v2025.0.0
+### Tuesday, January 21, 2025
+
+With PowerShell Editor Services [v4.2.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v4.2.0)!
+
+New Year Update!
+
+PowerShell 7.4+ and Windows PowerShell 5.1 (on a best-effort basis)
+are now solely supported as 7.3 LTS and 7.2 are past end-of-support.
+
+A major bug due to a Global Assembly Cache conflict with Serilog
+when using Windows PowerShell has been resolved by removing the
+troublesome dependency. This also came with a wonderful logging
+overhaul for both the server and client. Thanks Justin!
+
+Dependencies and VS Code engine have been updated. Snippets fixed.
+Extension settings are now categorized. Additional PowerShell
+executable path verification fixed.
+
+See more details at the GitHub Release for [v2025.0.0](https://github.com/PowerShell/vscode-powershell/releases/tag/v2025.0.0).
+
 ## v2025.1.0-preview
 ### Thursday, January 16, 2025
 
diff --git a/package.json b/package.json
index fc698ded92..59bae72db2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "powershell",
   "displayName": "PowerShell",
-  "version": "2025.1.0",
+  "version": "2025.0.0",
   "preview": false,
   "publisher": "ms-vscode",
   "description": "Develop PowerShell modules, commands and scripts in Visual Studio Code!",
